【JAVA CORE_API】Day21 Map接口、在线聊天室v3.0、Java的反射机制(P1)

news2024/11/14 18:45:13

Map接口

Map接口

  • Map是Java中用于存储键值对(key-value pairs)的接口,每个键(key)对应一个值(value)。它不允许重复的键,但允许不同的键映射相同的值。

  • 关键特点:

    • 键值对存储:每个key对应一个值,使用key来获取对应的值;

    • 不允许重复key:一个key只能存在一次,重复key会覆盖旧值;

    • 常用实现类:HashMapTreeMapLinkedHashMap等。

  • 常用方法:

    • put(K key, V value):插入键值对;

    • get(Object key):根据key获取对应的值;

    • remove(Object key):根据key删除对应的键值对;

    • containsKey(Object key):检查是否包含指定key;

    • keySet():返回所有key的集合。

  • Map是一个非常灵活的接口,广泛用于需要通过唯一key快速访问数据的场景。

 package Day21;
 
 import java.util.HashMap;
 import java.util.Map;
 
 /**
  * java.util.Map 查找表
  */
 public class MapDemo1 {
     public static void main(String[] args) {
         // Map创建时要分别指定key和value的类型
         Map<String, Integer> map = new HashMap<>();
         /*
             put(K k, V v):返回一个value值
             向Map中保存一组键值对
             由于Map要求key不允许重复,如果使用重复的key存入新的value,那么则是替换value操作,
             如果此Map中不包含该key的映射关系,则返回null。
             此时put方法会把被替换下来的value返回。否则返回值为null。
          */
         Integer value = map.put("语文", 100);
         System.out.println(value);
 
         map.put("数学", 100);
         map.put("外语", 100);
         map.put("物理", 100);
         map.put("化学", 100);
         System.out.println(map);
 
         value = map.put("语文", 66);   // 会将“语文”原本的99替换掉
         System.out.println(value);   // 99
         System.out.println(map);
 
 //        int valuePoint = map.put("语文", 100);
 //        System.out.println(valuePoint);  // 空指针异常
 
         System.out.println("");
 
         /**
          * get(Object key) 返回value类型
          * 根据给定的key提取对应的value
          * 如果给定的key在Map中不存在则返回值为null
          */
         value = map.get("语文");
         System.out.println(value);
         value = map.get("体育");
         System.out.println(value);
 
         System.out.println("");
 
         /**
          * size(): 返回int类型
          * 返回Map中键值对的个数
          */
         int size = map.size();
         System.out.println("size=" + size);
 
         System.out.println("");
 
         /**
          * remove() 返回value类型
          * 根据给定的key删除对应的键值对,并且会将该key对应的value返回
          */
         value = map.remove("数学");
         System.out.println(value);  // 数学对应的value值:98
         System.out.println(map);
 
         System.out.println("");
 
         /**
          * isEmpty() 返回boolean类型
          * 检查是否为空Map(Map中不含有任何键值对)
          */
         boolean isEmpty = map.isEmpty();
         System.out.println("是否为空Map" + isEmpty);
 
         System.out.println("");
 
         /**
          * containsKey(key) 返回boolean类型
          * containsValue(Value) 返回boolean类型
          * 判断Map中是否包含指定的key或value
          * 底层还是利用equals方法比较
          */
         boolean containsKey = map.containsKey("语文");
         System.out.println("是否包含key:" + containsKey);
         boolean containsValue = map.containsValue(100);
         System.out.println("是否包含value:" + containsValue);
 
         System.out.println("");
 
         /**
          * clear()
          * 清空Map
          */
         map.clear();
         System.out.println(map);
     }
 }
 

Map的遍历

常用实现类

  • 遍历<key,value>

    • 使用 entrySet() 方法,获取键值对的集合。

    • 示例:

       package Day21;
       
       import java.util.HashMap;
       import java.util.Map;
       import java.util.Set;
       
       public class MapDemo2 {
           public static void main(String[] args) {
               Map<String, Integer> map = new HashMap<>();
               map.put("语文", 100);
               map.put("数学", 90);
               map.put("英语", 80);
               map.put("化学", 100);
               map.put("物理", 100);
               System.out.println(map);
             
               /**
                * entrySet() 返回所有的键值对
                * 将当前Map中所有的键值对以若干的Entry实例表示,并将他们以一个Set集合的形式返回
                */
               Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
               for (Map.Entry<String, Integer> entry : entrySet) {
                   System.out.println("entry:" + entry);
               }
           }
       }
       
      
  • 遍历key

    • 使用 keySet() 方法,获取键的集合。

    • 示例:

       package Day21;
       
       import java.util.HashMap;
       import java.util.Map;
       import java.util.Set;
       
       public class MapDemo2 {
           public static void main(String[] args) {
               Map<String, Integer> map = new HashMap<>();
               map.put("语文", 100);
               map.put("数学", 90);
               map.put("英语", 80);
               map.put("化学", 100);
               map.put("物理", 100);
               System.out.println(map);
       
               /**
                * keySet() 返回所有的key
                * 将当前Map中所有的key以一个Set集合的形式返回
                */
               Set<String> keySet = map.keySet();
               for (String key : keySet) {
                   System.out.println("key:" + key);
               }
           }
       }
       
      
  • 遍历value

    • 使用 values() 方法,获取所有值的集合。

    • 示例:

       for (V value : map.values()) {
           // 处理值
       }
      

forEach方法

  • forEach 方法

    • forEach 是 Java 8 引入的一个方法,用于遍历集合或流中的每个元素,通常与 Lambda 表达式结合使用。
  • 用法示例

     List<String> list = Arrays.asList("a", "b", "c");
     
     // 使用 forEach 方法遍历列表
     list.forEach(element -> {
         System.out.println(element);
     });
    
  • 关键特点

    • 简洁:用 Lambda 表达式直接在遍历时定义要执行的操作。

    • 灵活:可以传递方法引用,如 list.forEach(System.out::println);

    • 适用范围:适用于所有实现了 Iterable 接口的集合,如 ListSet,以及 Stream 流。

    • forEach 方法简化了遍历过程,特别适合在函数式编程风格中使用。

小练习

 package Day21;
 
 import java.util.HashMap;
 import java.util.Map;
 
 /**
  * 编写程序,统计字符串中每个字符出现的次数
  */
 public class Test {
 
     public static void main(String[] args) {
         /*
             输出的结果,要求看到每个字符出现的次数。
             例如
             h:2
             e:2
             l:4
             xx:xx
 
             思路:
             1:创建一个Map用来保存统计结果
               key:出现的每种字符   value:该字符出现的次数
 
             2:
               2.1:遍历line中的每一个字符
                   String有一个方法:charAt(int index) 根据下标提取对应的元素
                   下标范围:0-字符串的length
               2.2:将该字符作为key存入到map中,这是会有两种情况
                   Map的containsKey()方法
                   2.2.1:如果这个字符作为key在map中不存在,将字符作为key,value存入1(第一次出现)
                   2.2.3:如果该字符作为key在map中存在,说明该字符之前出现过,此时将value+1
               2.3:遍历完毕相当于统计完毕,将map输出看结果即可
 
               注意:char类型对应的包装类是Character
          */
         String line = "Patiention! Is key in life! Just be patient!";
         Map<Character, Integer> map = new HashMap<>();
         for (int i = 0; i < line.length(); i++) {
             char c = line.charAt(i);
             if (map.containsKey(c)) {
                 map.put(c, map.get(c) + 1);
             } else {
                 map.put(c, 1);
             }
         }
         System.out.println(map);
     }
 }
 

聊天室v3.0

Client.java

 package Day21;
 import java.io.*;
 import java.net.Socket;
 import java.nio.charset.StandardCharsets;
 import java.util.Scanner;
 
 /**
  * 客户端
  */
 public class Client {
     /**
      * java.net.Socket:套接字
      * 它封装了TCP协议的通信细节,使用它可以和服务器建立连接并进行交互
      * 可以理解为是电话
      */
     private Socket socket;
     public Client() {
         // 实例化Socket对象,就是与远端计算机建立连接的过程
         // 需要传递两个对数:
         // 1.远端计算机的IP地址,用于找到网络上对方的计算机
         // 2.远端计算机的端口号,用于找到对方计算机中的应用程序
         try {
             System.out.println("正在连接服务端......");
             /**
              实例化Socket对象,就是与远端计算机建立连接的过程
              需要传入两个对数:
              1.服务端的IP地址,用于找到网络上对方的计算机
              2.服务端口,用于找到服务端计算机上的应用程序
 
              查看IP操作:
               Windows: win+r,输入cmd,回车,输入ipconfig,IPv4就是IP地址
               Mac:触控板上五指向中间抓,选择"终端"程序打开,输入/sbin/ifconfig查看IP地址
 
               创建Socket对象向服务端发送请求时,会进行三次握手
              */
             socket = new Socket("localhost", 8888);
             System.out.println("连接成功!");
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
 
     // 该方法用于启动客户端程序的执行
     public void start() {
         /*
              Socket提供了一个方法:
              OutputStream getOutputStream()
              通过socket获取输出流,将写出的信息发送给服务端
              */
         try {
             OutputStream outputStream = socket.getOutputStream();
             OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
             BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
             PrintWriter printWriter = new PrintWriter(bufferedWriter,true);  // 自动行刷新
 
             // 输入昵称
             Scanner scanner01 = new Scanner(System.in);
             while (true) {
                 System.out.println("请输入昵称:");
                 // 要求:昵称不能为空或者只有空格都不可以,如果发生则提示用户重新输入
                 String nickname = scanner01.nextLine();
                 if (nickname.trim().isEmpty()){
                     System.out.println("昵称不能为空,请重新输入!");
                 }else {
                     printWriter.println(nickname);
                     System.out.println("昵称设置成功!欢迎您:" + nickname);
                     break;
                 }
             }
 
             // 启动用于读取服务端消息的线程
             ServerHandler serverHandler = new ServerHandler();
             Thread thread = new Thread(serverHandler);
             // 如果我们输入exit想结束聊天,这个线程依然活着,所以我们想让他和主线程一起死,于是我们设计这个线程为守护线程
             thread.setDaemon(true);
             thread.start();
 
             Scanner scanner = new Scanner(System.in);
             while (true) {
                 String line = scanner.nextLine();
                 if ("exit".equalsIgnoreCase(line)) {
                     break;
                 }
                 printWriter.println(line);  // 发送消息给服务端
             }
         } catch (IOException e) {
             e.printStackTrace();
         } finally {
             try {
                 socket.close();  // 进行四次挥手
             } catch (IOException e) {
                 e.printStackTrace();
             }
         }
     }
 
     public static void main(String[] args) {
         Client client = new Client();
         client.start();
     }
 
     // 处理回复消息,创建回复接口:该线程任务负责读取来自服务端发送过来的消息并输出到控制台上
     private class ServerHandler implements Runnable {
         @Override
         public void run() {
             try {
                 // 通过Socket提供的方法InputStream getInputStream(),获取输入流,将服务端发送的消息读取出来
                 InputStream inputStream = socket.getInputStream();
                 InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
                 BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
 
                 // 循环读取来自服务端发送过来的消息并输出到客户端控制台上
                 String message;
                 while ((message = bufferedReader.readLine()) != null) {
                     System.out.println(message);
                 }
 
             } catch (IOException e) {
                 e.printStackTrace();
             }
         }
     }
 }
 

server.java

 package Day21;
 
 import java.io.*;
 import java.net.ServerSocket;
 import java.net.Socket;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
 /**
  * 服务端
  */
 public class Server {
     /**
      * java.net.ServerSocket:
      * 运行在服务端,主要作用有两个:
      * 1.像系统申请服务端口,客户端通过该端口与服务端建立连接
      * 2.监听服务端口,一旦有客户链接了,就会立即创建一个Socket对象与客户端进行交互
      *      如果将Socket比喻为“电话”,那ServerSocket就相当于客户中心的“总机”。
      * 解决方法:
      * 1.更换端口号;
      * 2.杀死占用该端口的进行(通常由于服务端启动了两次导致)
      */
     // key:用户的昵称    value:该客户端的输出流
     private Map<String, PrintWriter> allOut = new HashMap<>();
 
     private ServerSocket serverSocket;
 //    private List<PrintWriter> allOut = new ArrayList<>();
     public Server(){
         try {
             System.out.println("正在启动服务端......");
             /**
              * 创建ServerSocket对象时,会申请一个端口
              * 如果该端口被其它程序占用,会抛出异常:
              *   java.net.BindException: Address already in use:
              * 解决办法:
              * 1.更换端口号
              * 2.杀死占用该端口的进行(通常由于服务端启动两次导致)
              */
             serverSocket = new ServerSocket(8888);
             System.out.println("服务端启动完成!");
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
 
     // 启动服务端方法:
     /**
      * accept():
      * 用于接收客户端连接,并返回一个Socket对象与所连接的客户端进行交互
      * 该方法是一个阻塞方法,调用后程序会“卡住”,直到一个客户端连接为止
      */
     public void start(){
         try {
             while (true){
                 System.out.println("服务端正在等待客户端连接......");
                 Socket socket = serverSocket.accept();
                 System.out.println("一个客户端连接了!");
 
                 // 启动单独的线程来处理与客户端的通信
                 Runnable clientHandler = new ClientHandler(socket);
                 Thread thread = new Thread(clientHandler);
                 thread.start();
             }
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
     public static void main(String[] args) {
         Server server = new Server();
         server.start();
     }
 
     /**
      * 该线程任务用于负责与指定的客户端通信
      */
     private class ClientHandler implements Runnable{
         private Socket socket;
         private String host;  // 记录客户端的IP地址
         private String nickname;
         public ClientHandler(Socket socket) {
             this.socket = socket;
             // 通过socket获取客户端ip地址
             host = socket.getInetAddress().getHostAddress();
         }
         @Override
         public void run() {
             PrintWriter printWriter = null;
             try {
                 //接收客户端发送过来的消息
                 /*
                  Socket提供了一个方法:
                  InputStream getInputStream()
                  通过socket获取输入流,用于读取客户端发送过来的数据
                  */
                 InputStream inputStream = socket.getInputStream();
                 InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
                 BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
                 // 读取客户端发送过来的第一行字符串一定是客户端的昵称
                 nickname = bufferedReader.readLine();
 
                 // 通过Socket获取输出流,用于将消息发送给该客户端
                 OutputStream outputStream = socket.getOutputStream();
                 OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
                 BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
                 printWriter = new PrintWriter(bufferedWriter,true);
 
                 // 将对应该客户端的输出流存入到共享集合allOut中
                 synchronized (allOut) {
                     allOut.put(nickname, printWriter);
                 }
 
                 // 广播该客户端上线了
                 broadcast(nickname + "["+host+"]上线了!当前在线人数:" + allOut.size());
                 /*
                   当我们通过br.readLine()读取客户端发送过来的数据时,
                   客户端如果调用socket.close()时(进行四次挥手),正常与我们断开连接
                   那么readLine()会返回null
                   若客户端异常中断(强制杀死进程),此时readLine()会抛出异常
                   注意:这种情况咱们服务端是没有办法的
                  */
 
                 String message;
                 while ((message = bufferedReader.readLine()) != null) {    // 读取客户端发送过来的消息
                     if(message.startsWith("@")) {
                         // 如果消息是以@开头,说明是私聊消息
                         sendMessageToSomeone(message);
                     }else{
                         broadcast(nickname + "[" +host + "]说:" + message);
                     }
                 }
             } catch (IOException e) {
 
             } finally {
                 // 处理该客户端断开连接后的操作
                 // 将该客户端的输出流从共享集合allOut中删除
                 synchronized (allOut) {
 //                    allOut.remove(printWriter);
                     allOut.remove(nickname);
                 }
                 // 广播消息,告知所有客户端该用户下线了
                 broadcast(nickname + "[" + host + "]下线了!当前在线人数:" + allOut.size());
 
                 try {    // 进行四次挥手
                     serverSocket.close();
                 } catch (IOException e) {
                     e.printStackTrace();
                 }
             }
         }
 
         /**
          * 将消息发送给所有客户端
          * @param message
          */
         private void broadcast(String message){
             System.out.println(message);
             synchronized (allOut) {
 //                for (PrintWriter writer : allOut) {
 //                    writer.println(message);
 //                }
                 for (PrintWriter writer : allOut.values()){
                     writer.println(message);
                 }
             }
         }
 
         /**
          * 私聊功能,将消息发送给某个人
          * @param message
          */
         public void sendMessageToSomeone(String message){
             /*
                 过程:
                 私聊消息的格式-->@对方昵称:聊天消息
                 1:截取出对方的昵称
                   substring()->截"@"到":"之间的内容
                 2:从allOut中使用对方的昵称作为key提取到对应的输出流
                   map.get(对方昵称)
                 3:将聊天消息通过该输出流发送给对方
                   聊天消息substring()->":"后面的内容
                   然后通过输出流发送给对象
              */
             // 首先验证私聊消息是否为正确的格式:@xxx:xxx
             if (message.matches("@.+:.+")) {
                 //1
                 String toNickName = message.substring(1, message.indexOf(":"));
                 if (allOut.containsKey(toNickName)) {
                     //2
                     PrintWriter printWriter = allOut.get(toNickName);
                     //3
                     String toMessage = message.substring(message.indexOf(":") + 1);
                     printWriter.println(nickname + "悄悄对你说:" + toMessage);
                 }else {
                     PrintWriter printWriter = allOut.get(nickname);
                     printWriter.println("私聊对象不存在!");
                 }
             } else {
                 // 如果格式不对,给当前用户提示私聊格式不正确
                 PrintWriter printWriter = allOut.get(nickname);
                 printWriter.println("私聊消息格式不正确,私聊的格式应当为:@对方昵称:私聊消息");
             }
         }
     }
 }
 
  • 接下来将Client.java客户端内全部代码发送给GPT,请求生成一个可视化界面,GPT语言:这是一个多人在线聊天室的客户端代码,请根据Client.java中的Client类的代码内容,生成图形化的界面,对输入昵称可以单独创建界面,聊天界面要求要在窗口的下方生成文本框,用于获取用户输入的聊天消息发送给服务端,包括上方存在一个文本框用来获取服务器发送来的聊天记录信息,下方具有发送按钮,发送可以使用enter键触发,字体也可以进行修改,界面内部使用中文布局,可以参考其他在线聊天室软件的界面布局,还有就是将字的大小改为24,聊天界面按钮别搞那么长,把聊天框放大一些,把界面做的更复杂一些

     package Day21;
     
     import javax.swing.*;
     import java.awt.*;
     import java.awt.event.ActionEvent;
     import java.awt.event.ActionListener;
     import java.awt.event.KeyAdapter;
     import java.awt.event.KeyEvent;
     import java.io.*;
     import java.net.Socket;
     import java.nio.charset.StandardCharsets;
     
     public class ClientGUI {
         private Socket socket;
         private String nickname;
         private JTextArea chatArea;
         private JTextField inputField;
     
         public ClientGUI() {
             connectToServer();
             createNicknameFrame();
         }
     
         // 连接服务器
         private void connectToServer() {
             try {
                 socket = new Socket("localhost", 8888);
                 System.out.println("连接成功!");
             } catch (IOException e) {
                 e.printStackTrace();
             }
         }
     
         // 昵称输入界面
         private void createNicknameFrame() {
             JFrame nicknameFrame = new JFrame("输入昵称");
             nicknameFrame.setSize(500, 250);
             nicknameFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
             nicknameFrame.setLayout(new BorderLayout());
     
             JPanel panel = new JPanel(new GridBagLayout());
             JLabel label = new JLabel("请输入昵称:");
             label.setFont(new Font("宋体", Font.PLAIN, 24));
             JTextField nicknameField = new JTextField(15);
             nicknameField.setFont(new Font("宋体", Font.PLAIN, 24));
             JButton confirmButton = new JButton("确认");
             confirmButton.setFont(new Font("宋体", Font.PLAIN, 24));
     
             confirmButton.addActionListener(e -> {
                 String inputNickname = nicknameField.getText().trim();
                 if (inputNickname.isEmpty()) {
                     JOptionPane.showMessageDialog(nicknameFrame, "昵称不能为空,请重新输入!");
                 } else {
                     nickname = inputNickname;
                     sendNicknameToServer();
                     nicknameFrame.dispose();
                     createChatFrame();
                 }
             });
     
             GridBagConstraints gbc = new GridBagConstraints();
             gbc.insets = new Insets(10, 10, 10, 10);
             gbc.gridx = 0;
             gbc.gridy = 0;
             panel.add(label, gbc);
     
             gbc.gridx = 1;
             panel.add(nicknameField, gbc);
     
             gbc.gridx = 0;
             gbc.gridy = 1;
             gbc.gridwidth = 2;
             panel.add(confirmButton, gbc);
     
             nicknameFrame.add(panel, BorderLayout.CENTER);
             nicknameFrame.setVisible(true);
         }
     
         // 发送昵称给服务器
         private void sendNicknameToServer() {
             try {
                 PrintWriter writer = new PrintWriter(new BufferedWriter(
                         new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8)), true);
                 writer.println(nickname);
                 System.out.println("昵称设置成功!欢迎您:" + nickname);
             } catch (IOException e) {
                 e.printStackTrace();
             }
         }
     
         // 主聊天界面
         private void createChatFrame() {
             JFrame chatFrame = new JFrame("聊天室 - " + nickname);
             chatFrame.setSize(800, 600);
             chatFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
             chatFrame.setLayout(new BorderLayout());
     
             chatArea = new JTextArea();
             chatArea.setEditable(false);
             chatArea.setFont(new Font("宋体", Font.PLAIN, 24));
             JScrollPane chatScrollPane = new JScrollPane(chatArea);
     
             inputField = new JTextField();
             inputField.setFont(new Font("宋体", Font.PLAIN, 24));
             inputField.setPreferredSize(new Dimension(600, 50));
     
             JButton sendButton = new JButton("发送");
             sendButton.setFont(new Font("宋体", Font.PLAIN, 24));
             sendButton.setPreferredSize(new Dimension(150, 50));
     
             // 按下发送按钮或回车键发送消息
             sendButton.addActionListener(e -> sendMessage());
             inputField.addKeyListener(new KeyAdapter() {
                 @Override
                 public void keyPressed(KeyEvent e) {
                     if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                         sendMessage();
                     }
                 }
             });
     
             JPanel inputPanel = new JPanel(new BorderLayout());
             inputPanel.add(inputField, BorderLayout.CENTER);
             inputPanel.add(sendButton, BorderLayout.EAST);
     
             chatFrame.add(chatScrollPane, BorderLayout.CENTER);
             chatFrame.add(inputPanel, BorderLayout.SOUTH);
     
             chatFrame.setVisible(true);
     
             // 启动接收服务器消息的线程
             new Thread(new ServerHandler()).start();
         }
     
         // 发送消息到服务器
         private void sendMessage() {
             String message = inputField.getText().trim();
             if (!message.isEmpty()) {
                 try {
                     PrintWriter writer = new PrintWriter(new BufferedWriter(
                             new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8)), true);
                     writer.println(message);
                     inputField.setText("");
                 } catch (IOException e) {
                     e.printStackTrace();
                 }
             }
         }
     
         // 处理服务器消息
         private class ServerHandler implements Runnable {
             @Override
             public void run() {
                 try {
                     BufferedReader reader = new BufferedReader(
                             new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
                     String message;
                     while ((message = reader.readLine()) != null) {
                         chatArea.append(message + "\n");
                     }
                 } catch (IOException e) {
                     e.printStackTrace();
                 }
             }
         }
     
         public static void main(String[] args) {
             new ClientGUI();
         }
     }
     
    

在这里插入图片描述

Java的反射机制

反射机制

什么是反射机制

  • Java的反射机制是一种让程序在运行时动态获取类、接口、方法、属性等信息的能力。通过反射,程序可以在不知道类的情况下创建对象、调用方法、或访问属性。这种机制允许程序在运行时检查和操作代码结构,而不需要在编译时知道具体的类型。

  • 简而言之,反射使得代码能够“自我检查”和“自我修改”。这对于编写更灵活、通用的代码非常有用,比如框架、工具库、或一些需要处理未知类型对象的程序。

  • Java的反射机制就像是你手中的一面“魔法镜子”(反射API)。当你面对一扇门(类)时,不用打开门就能通过这面魔法镜子看到门后面的所有东西(类的结构),比如房间里的家具(属性)、开关(方法)等等。你甚至可以通过镜子直接操作这些东西,比如打开开关(调用方法)或移动家具(修改属性),而不需要知道这个房间具体是什么样的。

反射API

什么是Class类?

  • Class类是Java反射机制的核心。它是一个用来表示类或接口的字节码文件在JVM中的对象。通过Class类,程序可以在运行时获取关于类的各种信息,比如类的名称、修饰符、构造方法、方法、字段等。

  • 每个Java类在加载时,JVM都会为其生成一个对应的Class对象,这个对象包含了该类的所有元数据信息。开发者可以通过这个Class对象使用反射机制来操作这个类。

获取类对象

  • Class.forName("类的全限定名")

    • 用法: 通过类的全限定名(即包名+类名)来获取Class对象。

    • 适用场景: 当你知道类的名称,并且希望在运行时动态加载类时,这种方法最为常用。

    • 示例:

       Class class1 = Class.forName("java.util.String");
      
  • 类名.class

    • 用法: 直接通过类的名字获取Class对象。

    • 适用场景: 当你在编译时就已经知道类的具体类型时,这种方法更简洁。

    • 示例:

       String name = class1.getName();
      
  • 对象.getClass()

    • 用法: 通过一个已实例化的对象来获取它的Class对象。

    • 适用场景: 当你有一个对象的实例,但不确定它的具体类型时,这种方法可以动态获取该对象的Class对象。

    • 示例:

       String string = new String();
       Class class1 = string.getClass();
      

获取类信息

  • 获取类的完全限定名

    • 方法: getName()

    • 示例:

       System.out.println(class1.getName());
      
  • 获取当前类名

    • 方法getSimpleName()

    • 示例

       System.out.println(class1.getSimpleName());
      
  • 获取构造方法

    • 方法: getConstructors(), getDeclaredConstructors()

    • 示例:

       Constructor[] constructors = class1.getConstructors();
       for (Constructor constructor : constructors) {
           System.out.println("Constructor: " + constructor);
       }
       
       // 获取所有声明的构造方法(包括私有构造方法)
       Constructor[] declaredConstructors = class1.getDeclaredConstructors();
       for (Constructor constructor : declaredConstructors) {
           System.out.println("Declared Constructor: " + constructor);
       }
      
  • 获取方法

    • 方法: getMethods(), getDeclaredMethods()

    • 示例:

       Method[] methods = class1.getMethods();
       for (Method method : methods) {
           System.out.println("Method: " + method);
       }
       
       // 获取所有声明的方法(包括私有方法)
       Method[] declaredMethods = class1.getDeclaredMethods();
       for (Method method : declaredMethods) {
           System.out.println("Declared Method: " + method);
       }
      
  • 获取字段

    • 方法: getFields(), getDeclaredFields()

    • 示例:

       Field[] fields = class1.getFields();
       for (Field field : fields) {
           System.out.println("Field: " + field);
       }
       
       // 获取所有声明的字段(包括私有字段)
       Field[] declaredFields = class1.getDeclaredFields();
       for (Field field : declaredFields) {
           System.out.println("Declared Field: " + field);
       }
      
  • 获取父类

    • 方法: getSuperclass()

    • 示例:

       Class superClass = class1.getSuperclass();
       System.out.println("Superclass: " + superClass);
      
  • 获取接口

    • 方法: getInterfaces()

    • 示例:

       Class[] interfaces = class1.getInterfaces();
       for (Class iface : interfaces) {
           System.out.println("Interface: " + iface);
       }
      
  • 获取类加载器

    • 方法: getClassLoader()

    • 示例:

       ClassLoader classLoader = class1.getClassLoader();
       System.out.println("ClassLoader: " + classLoader);
      

Package类

  • java.lang.Package,用于获取包内的数据,每一个实例都用于表示一个包的信息,通过类对象的getPackage()方法获取一个类的包信息。

     Class class1 = ArrayList.class;
     Package pack = class1.getPackage();
     // 通过包对象获取它表示的包的名字
     String packName = pack.getName();
     System.out.peintln(packName);  // 输出java.util
    

对象实例化

  • 我们提前创建一个Person类,顺便将其配置好:

     package day21;
     
     import java.util.Objects;
     
     public class Person {
         private String name;
         private int age;
     
         public Person() {
         }
     
         public Person(String name) {
             this.name = name;
         }
     
         public Person(String name, int age) {
             this.name = name;
             this.age = age;
         }
     
         public String getName() {
             return name;
         }
     
         public void setName(String name) {
             this.name = name;
         }
     
         @Override
         public boolean equals(Object object) {
             if (this == object) return true;
             if (object == null || getClass() != object.getClass()) return false;
             Person person = (Person) object;
             return age == person.age && Objects.equals(name, person.name);
         }
     
         @Override
         public int hashCode() {
             return Objects.hash(name, age);
         }
     
         public int getAge() {
             return age;
         }
     
         public void setAge(int age) {
             this.age = age;
         }
     
         @Override
         public String toString() {
             return "Person{" +
                     "name='" + name + '\'' +
                     ", age=" + age +
                     '}';
         }
     }
     
    
  • 使用 new 关键字

    • 这是最常见和直接的对象实例化方法,通过new关键字可以创建类的实例:
     // 程序写死,那么只能实例化ArrayList
     ArrayList arrayList = new ArrayList(); 
    
    • 这种方式在编译时需要知道类的具体类型,并且只能用于类的公共无参构造函数。
  • 使用 Class 类的 newInstance() 方法

    • Class类提供了newInstance()方法来创建对象实例。这种方法允许你通过反射在运行时动态创建类的实例:

    • 方法: Class.newInstance()

    • 用法: 调用Class对象的newInstance()方法,创建一个该类的实例。

    • 示例:

               // 1.获取要实例化的类的对象
               // 反射机制实例化对象
               Scanner scanner = new Scanner(System.in);  // 反射机制可以运行起来根据需要去实例化的对象
               System.out.println("请输入一个类的名字:");
               String className = scanner.next();
               Class cls = Class.forName(className);
               /*
                   2.类对象
                */
               Object obj = cls.newInstance();
               System.out.println(obj);
      
    • 注意: newInstance()方法已被标记为过时(deprecated),主要因为它不能处理构造函数的异常。建议使用Constructor类的方法来替代。

  • 使用 Constructor

    • Constructor类提供了更灵活的方式来创建对象实例。可以通过Constructor对象调用构造函数来创建实例:

    • 获取 Constructor 对象: 使用Class类的getConstructor()getDeclaredConstructor()方法获取构造函数。

    • 创建实例: 使用Constructor对象的newInstance()方法创建对象。

    • 示例:

     package day21;
     
     import java.lang.reflect.Constructor;
     import java.lang.reflect.InvocationTargetException;
     
     public class ReflectDemo3 {
         public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
             Person person = new Person("张禹垚", 21);
             System.out.println(person);
     
             // 获取类对象
             Class cls = Class.forName("day21.Person");
             // Person(String)
             Constructor constructor = cls.getConstructor(String.class);
             // new Person("刘备");
             Object obj = constructor.newInstance("刘备");
             System.out.println(obj);
     
             Class cls1 = Class.forName("day21.Person");
             Constructor constructor1 = cls1.getConstructor(String.class, int.class);
             Object obj1 = constructor1.newInstance("关羽", 33);
             System.out.println(obj1);
         }
     }
     
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2069672.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux 可视化管理工具:宝塔面板(BT)

&#x1f600;前言 在 Linux 系统的运维管理中&#xff0c;命令行界面&#xff08;CLI&#xff09;是主要的操作方式。然而&#xff0c;对于许多系统管理员或开发者来说&#xff0c;使用 CLI 进行管理和维护任务并不总是最直观或最方便的方式。为了简化操作并提高效率&#xff…

【注解】反序列化时匹配多个 JSON 属性名 @JsonAlias 详解

JsonAlias 注解是 Jackson 提供的一个功能强大的注解&#xff0c;允许一个字段在反序列化时匹配多个 JSON 属性名。它适用于在处理多种输入数据格式时&#xff0c;或当 JSON 数据的键名可能变化时。 一、JsonAlias 的作用 多种别名&#xff1a;JsonAlias 允许你为一个字段定义…

利用深度学习技术来实现街景图像的语义分割(街景图像语义分割)

本项目致力于利用深度学习技术来实现街景图像的语义分割。通过精确地识别和分类图像中的每个像素&#xff0c;该技术能够自动划分出街道、人行道、车辆、行人等各种不同的物体类别。这在智能交通系统、自动驾驶、城市规划等领域有着广泛的应用前景。 技术实现 深度学习模型&am…

网站上线流程完全手册:域名、服务器与CDN

网站上线的核心要点 需要买域名 域名备案(国内) 买服务器 把服务器IP和域名(网址)绑定 把本地网站代码文件上传到服务器上 我来先来了解下以上的概念 域名介绍 域名是网站的地址&#xff0c;类似于你的家在街上的位置。它让人们通过简单的名字&#xff08;如 www.baidu.…

大模型时代的AI应用开发,可以不用,但必须会

成熟的“格子衫”和年轻的“脸庞”&#xff0c;与开发者有关的大会总是少不了这两种元素&#xff0c;Create 2024百度AI开发者大会也不例外。 过去几十年&#xff0c;层出不穷的编程语言、框架等新技术&#xff0c;把一代又一代年轻的脸庞&#xff0c;塑造为成熟的格子衫&…

网络安全威胁2024年中报告

近日&#xff0c;奇安信威胁情报中心发布《网络安全威胁2024年中报告》&#xff0c;内容涵盖高级持续性威胁&#xff08;APT&#xff09;、勒索软件、互联网黑产、漏洞利用等几方面。 APT攻击活动&#xff1a;2024 年上半年&#xff0c;APT 攻击主要集中在信息技术、政府、科研…

设计模式笔记07(java版)

文章目录 迭代器模式定义结构优缺点使用场景迭代器模式在JDK源码中的使用 访问者模式定义结构案例实现优缺点使用场景扩展1. 分派&#xff1a;2. 动态分派&#xff1a;3. 静态分派&#xff1a;4. 双分派&#xff1a; 备忘录模式概述定义结构“黑箱”备忘录模式优缺点使用场景 解…

递归神经网络 (RNN) 简介

文章目录 一、介绍二、什么是递归神经网络 &#xff08;RNN&#xff09;&#xff1f;三、展开递归神经网络四、训练递归神经网络五、RNN 的类型六、现实生活中的 RNN 用例七、RNN 的两个主要限制八、RNN的变体8.1 双向递归神经网络 &#xff08;BRNN&#xff09;8.2 长短期记忆…

色彩的含义和使用色彩象征的艺术

无论你走到哪里&#xff0c;你都能看到颜色&#xff1a;天空中的白云&#xff0c;绿色的植被逐渐上升&#xff0c;灰色的石头结构和红砖建筑&#xff0c;在你脚下磨砺着棕色的土壤。你不只是看到这些颜色......你感觉到它们。 一&#xff0c;颜色的含义从何而来&#xff1f; 熟…

华为OD算法题整理目录

目录 专栏导读华为OD机试算法题太多了&#xff0c;知识点繁杂&#xff0c;如何刷题更有效率呢&#xff1f; 一、逻辑分析二、数据结构1、线性表① 数组② 双指针 2、map与list3、队列4、链表5、栈6、滑动窗口7、二叉树8、并查集9、矩阵 三、算法1、基础算法① 贪心思维② 二分查…

面试搜狐大模型算法工程师,体验真棒!!!

搜狐大模型算法工程师面试题 应聘岗位&#xff1a;搜狐大模型算法工程师 面试轮数&#xff1a; 整体面试感觉&#xff1a;偏简单 面试过程回顾 1. 自我介绍 在自我介绍环节&#xff0c;我清晰地阐述了个人基本信息、教育背景、工作经历和技能特长&#xff0c;展示了自信和沟通…

代码随想录算法训练营第 53 天 |卡码网110.字符串接龙 卡码网105.有向图的完全可达性 卡码网106.岛屿的周长

代码随想录算法训练营 Day53 代码随想录算法训练营第 53 天 |卡码网110.字符串接龙 卡码网105.有向图的完全可达性 卡码网106.岛屿的周长 目录 代码随想录算法训练营前言卡码网110.字符串接龙卡码网105.有向图的完全可达性卡码网106.岛屿的周长 一、卡码网110.字符串接龙1.题…

Java:StringBuilder、StringBuffer、StringJoiner

文章目录 StringBuilder代码为什么操作字符串建议使用StringBuilder,而不是String&#xff1f;但String还是不可替代的 StringBuffer案例&#xff1a;接收字符串&#xff0c;返回String类型的[11,22]这种形式代码 StringJoiner上面那个案例&#xff0c;用StringJoiner的方法解决…

Linux服务器不能使用su切换到其他用户

1. 现象 使用非root用户登录系统后&#xff0c;想使用su - 切换到root时&#xff0c;输入正确的密码后出现&#xff1a; 一开始以为自己密码记错了&#xff0c;或者是系统被入侵后篡改了root用户密码&#xff0c;服务器上的数据非常重要。后面不通过SSH连接&#xff0c;直接去…

2024年AI最热门行业人才:计算机视觉与自然语言及语音处理设计开发工程师!

2024年&#xff0c;计算机视觉设计与开发工程师以及自然语言及语音处理设计与开发工程师将成为技术行业中的“超级明星”。无论是智能家居、自动驾驶汽车&#xff0c;还是医疗诊断、金融服务&#xff0c;AI已经深刻融入我们的日常生活&#xff0c;而这些领域的智能化转型都离不…

《机器学习》决策树 集成学习 随机森林 参数及实例解析

目录 一、集成学习 1、含义 2、集成学习代表 1&#xff09;bagging方法 2&#xff09;boosting方法 3&#xff09;stacking方法 二、随机森林 1、什么是随机森林 2、特点 1&#xff09;数据采样随机 2&#xff09;特征选取随机 3&#xff09;森林 4&#xff09;基分类器为…

LLAMA模型解析:快速掌握大模型LLM推理入门技巧

本文借助llama这个模型快速入门LLM的推理过程&#xff0c;技术细节很多都是通用的&#xff0c;也适合其他的LLM。本篇也可以作为CV快速入门NLP简易指南。 本文借助llama这个模型快速入门LLM的推理过程&#xff0c;很多技术细节都是通用的&#xff0c;也适合其他的LLM。这篇文章…

普元EOS-微前端调用base基座的ajax

1 前言 微前端调用EOS应用的服务或API&#xff0c;需要引入base基座&#xff0c;然后使用base基座中的ajax方法。 本文将介绍微前端的高开和低开使用base基座的Ajax的方法。 强烈建议不要自己引入axios或其他ajax库&#xff0c;因为EOS的服务接口都要求身份验证和授权&#…

分页查询面试记录和面试详情

文章目录 1.分页查询面试记录1.req和vo1.InterviewHistoryReq.java2.InterviewHistoryVO.java 2.InterviewController.java3.service1.InterviewHistoryService.java2.InterviewHistoryServiceImpl.java 4.测试 2.查询面试详情1.InterviewQuestionHistoryVO.java2.InterviewCon…

适合开发人员的网页爬虫工具DrissionPage

DrissionPage是一个基于Python的网页自动化工具,它集成了Selenium和Requests的功能,既能够控制浏览器进行用户界面的模拟操作,也能够直接收发数据包进行数据的获取和处理。 开源地址:DrissionPage: 基于python的网页自动化工具。既能控制浏览器,也能收发数据包。可兼顾浏…