【学习日记2023.4.15】之TCP编程_reflection反射

1. TCP编程

1.1 TCP客户端

方法

构造方法说明
public Socket(String ip,int port)和服务端建立连接 参数一: 服务端ip地址(字符串) 参数二: 服务端端口号
方法说明
public OutPutStream getOutPutStream()获取网络字节输出流
public InputStream getInputStream()获取网络字节输入流

步骤

  • 创建Socket对象: 需要指定服务端的ip和端口号
  • 获取字节输出流
  • 写数据
  • 释放资源
/*
    TCP客户端程序
    TCP在发送数据之前,确保连接已经建立
 */
public class Client {
    public static void main(String[] args) throws IOException {
        // 1.创建Socket对象  指定服务端ip和端口号 连接
        Socket socket = new Socket("127.0.0.1",8888);
        // 2.获取字节输出流
        OutputStream os = socket.getOutputStream();
        // 数据输出流: DataOutputStream .writeUTF(String)
        DataOutputStream dos = new DataOutputStream(os); // 直接写中文
        // 3.写数据
        dos.writeUTF("你好吗?");
        dos.flush(); // 真正将数据写出去
        // 4.释放资源: socket关闭了 获取的输出流也会随之关闭
        socket.close();
    }
}

1.2 TCP服务端

方法

构造方法说明
public ServerSocket(int port)服务端ServerSocket对象,参数为服务端的端口号
方法说明
public Socket accept()等待客户端的连接,连接成功获取Socket对象

步骤

  • 创建ServerSocket对象
  • 等待客户端连接并获取Socket对象
  • Socket对象获取输入流
  • 释放资源
/*
    TCP服务端代码
 */
public class Server {
    public static void main(String[] args) throws IOException {
        // 1.创建ServerSocket,需要指定服务端端口号
        ServerSocket ss = new ServerSocket(8888);
        // 2.accept等待客户端连接,连接成功返回Socket对象
        Socket socket = ss.accept();
        // 3.获取字节输入流
        InputStream is = socket.getInputStream();
        // 使用数据输入流配合使用读取数据
        DataInputStream dis = new DataInputStream(is);
        String msg = dis.readUTF();
        System.out.println(msg);
        // 4.释放资源
        socket.close(); // socket关闭,获取的字节输入流也会随之关闭
        ss.close();
    }
}

请添加图片描述

1.3 多发多收

步骤

  • 服务端: 使用死循环包裹读数据的代码
  • 客户端: 发送键盘录入的内容,使用死循环包裹写数据的代码,如果用户输入"886",结束循环
public class Client {
    public static void main(String[] args) throws IOException {
        // 1.创建Socket对象  指定服务端ip和端口号 连接
        Socket socket = new Socket("127.0.0.1",8888);
        // 2.获取字节输出流
        OutputStream os = socket.getOutputStream();
        // 数据输出流: DataOutputStream .writeUTF(String)
        // 数据流的特点: 读写数据时,带着数据类型
        DataOutputStream dos = new DataOutputStream(os);
        Scanner sc = new Scanner(System.in);

        while (true) {
            System.out.println("请录入: ");
            String s = sc.nextLine(); // 用户录入的数据
            if ("886".equals(s)){
                break;
            }
            // 3.写数据
            dos.writeUTF(s);
            dos.flush(); // 真正将数据写出去
        }
        // 4.释放资源: socket关闭了 获取的输出流也会随之关闭
        socket.close();
    }
}

public class Server {
    public static void main(String[] args) throws IOException {
        // 1.创建ServerSocket,需要指定服务端端口号
        ServerSocket ss = new ServerSocket(8888);
        // 2.accept等待客户端连接,连接成功返回Socket对象
        Socket socket = ss.accept();
        // 3.获取字节输入流
        InputStream is = socket.getInputStream();
        // 使用数据输入流配合使用读取数据
        DataInputStream dis = new DataInputStream(is);
        while (true) {
              String msg = dis.readUTF();
               // socket.getRemoteSocketAddress() 获取到客户端ip和端口号
              System.out.println(socket.getRemoteSocketAddress() + ": "+msg);
        }
        // 4.释放资源
        // socket.close(); // socket关闭,获取的字节输入流也会随之关闭
        // ss.close();
    }
}

问题

  • 当客户端结束后,服务端会出现异常EOFException?
    • 原因: 客户端结束了,服务端readUTF()方法,出现EOFException,表示没有数据可读
    • 解决: 使用try…catch去捕获EOFException
public class Server {
    public static void main(String[] args) throws IOException {
        // 1.创建ServerSocket,需要指定服务端端口号
        ServerSocket ss = new ServerSocket(8888);
        // 2.accept等待客户端连接,连接成功返回Socket对象
        Socket socket = ss.accept();
        // 3.获取字节输入流
        InputStream is = socket.getInputStream();
        // 使用数据输入流配合使用读取数据
        DataInputStream dis = new DataInputStream(is);
        while (true) {
            try {
                // 出现EOFException,表示没数据可读了
                String msg = dis.readUTF();
                // socket.getRemoteSocketAddress() 获取到客户端ip和端口号
                System.out.println(socket.getRemoteSocketAddress() + ": "+msg);
            } catch (EOFException e) {
                System.out.println(socket.getRemoteSocketAddress() + "下线了");
                socket.close(); // 客户端的socket已经断开连接,服务端获取到的socket也需要关闭
                break;
            }
        }
        // 4.释放资源
        // socket.close(); // socket关闭,获取的字节输入流也会随之关闭
        // ss.close();
    }
  • 无法实现多个客户端和服务端连接?
    • 原因: Socket socket = ss.accept(); 真正的接收客户端的连接

    • ​ main方法是一个线程,阻塞在readUTF()方法

    • 解决: 使用多线程

1.4 实现多个客户端和服务端同时通信

  • 使用多线程
public class Server {
    public static void main(String[] args) throws IOException {
        // 1.创建ServerSocket,需要指定服务端端口号
        ServerSocket ss = new ServerSocket(8888);

        while (true) {
            System.out.println("等待接收客户端的连接");
            Socket socket = ss.accept();
            // 连接成功后,再开启线程 执行读数据
            System.out.println(socket.getRemoteSocketAddress() + ": 上线了");
            MyRunnable m = new MyRunnable(socket);
            Thread t = new Thread(m);
            t.start();
        }
    }
}
public class MyRunnable implements Runnable{
    private Socket socket;

    public MyRunnable(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        // 接收客户端的连接
        try {
            // 获取字节流读数据
            InputStream is = socket.getInputStream();
            DataInputStream dis = new DataInputStream(is);
            while (true) {
                try {
                    String msg = dis.readUTF();
                    System.out.println(socket.getRemoteSocketAddress() + ":"+msg);
                } catch (EOFException e) {
                    System.out.println(socket.getRemoteSocketAddress() + "下线了");
                    // 释放资源 socket资源
                    socket.close();
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

请添加图片描述

1.5 练习

  • 客户端上传文件到服务端
    • 客户端: 从本地文件读数据,写到网络中, 循环读写
    • 服务端: 从网络中读,写到本地文件,循环读写
/*
    读取本地的文件 ,写到服务端
 */
public class Client {
    public static void main(String[] args) throws IOException {
        // 1.使用文件流读本地文件(缓冲流加速的效果)
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("day15-code\\clientDir\\a.avi"));

        Socket socket = new Socket("127.0.0.1",8888);

        // 2.写到网络中
        OutputStream os = socket.getOutputStream();
        BufferedOutputStream bos = new BufferedOutputStream(os);
        // 循环读写
        int b;
        while ((b = bis.read()) != -1){
            bos.write(b);
        }
        bos.flush();

        // bis本地文件流 单独的释放资源
        // socket关闭,网络流也会自动跟着关闭
        bis.close();
        socket.close();


    }
}

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(8888);
        Socket socket = ss.accept();

        // 从网络中读
        InputStream is = socket.getInputStream();
        BufferedInputStream bis = new BufferedInputStream(is);
        // 写到本地文件
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("day15-code\\serverDir\\a.avi"));
        // 循环读写
        int b;
        while ((b = bis.read()) != -1){
            bos.write(b);
        }
        bos.flush();

        // 释放资源 本地字节输出流bos  网络中socket
        bos.close();
        socket.close();
        ss.close();
    }

1.6 实现BS架构

  • 软件设计
    • C/S架构: Client(客户端) / Server服务端
    • B/S架构: Browser(浏览器) / Server服务端

原理

请添加图片描述

代码

/*
    使用TCP编程,实现B/S 浏览器
    在浏览器上输入 ip:端口就可以访问
 */
public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(8080);
        // 能够接收多个浏览器连接
        while (true){
            Socket socket = ss.accept(); // 等待客户端连接
            MyRunnable m = new MyRunnable(socket);
            // 开启线程
            Thread t = new Thread(m);
            t.start();
        }

    }
}

public class MyRunnable implements Runnable{
    private Socket socket;

    public MyRunnable(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        // 向浏览器写 "城市套路深,我要回农村" 文字
        // 获取输出流
        // try-with-resources
        try(OutputStream os = socket.getOutputStream();)  {
            PrintStream ps = new PrintStream(os);
            // 写数据的格式必须要按照HTTP要求的格式书写,否则浏览器不识别
            ps.println("HTTP/1.1 200 OK");
            ps.println("Content-Type:text/html;charset=UTF-8");
            ps.println(); // 必须要有空行
            // 写内容(展示在浏览器中)
            ps.println("<div style='color:red;font-size: 120px;text-align: center'>城市套路深,我要回农村</div>");
            ps.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

HTTP协议规定的数据格式

请添加图片描述

1.7 线程池优化

  • 每个连接都要创建新的线程对象,浪费资源,可以使用线程池(复用线程)
/*
    使用TCP编程,实现B/S 浏览器
    在浏览器上输入 ip:端口就可以访问
 */
public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(8080);
        // 创建线程池
        // 最大线程数:
            // CPU密集型: 和服务器CPU核心数量一致,保证CPU最大的使用率
            // IO密集型: 比CPU核心数量多
        // 队列的大小: 最大线程数量2倍

        // 获取CPU最大支持线程数量
        int num = Runtime.getRuntime().availableProcessors();
        System.out.println(num);

        ExecutorService pool = new ThreadPoolExecutor(
                num / 2,
                num,
            10,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(num * 2),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy()
        );

        // 能够接收多个浏览器连接
        while (true){
            Socket socket = ss.accept(); // 等待客户端连接
            MyRunnable m = new MyRunnable(socket);
            // 开启线程,每个连接,创建线程对象,浪费性能,使用线程池优化(线程复用)
           pool.execute(m);
        }

    }
}

public class MyRunnable implements Runnable{
    private Socket socket;

    public MyRunnable(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        // 向浏览器写 "城市套路深,我要回农村" 文字
        // 获取输出流
        // try-with-resources
        try(OutputStream os = socket.getOutputStream();)  {
            PrintStream ps = new PrintStream(os);
            // 写数据的格式必须要按照HTTP要求的格式书写,否则浏览器不识别
            ps.println("HTTP/1.1 200 OK");
            ps.println("Content-Type:text/html;charset=UTF-8");
            ps.println(); // 必须要有空行
            // 写内容(展示在浏览器中)
            ps.println("<div style='color:red;font-size: 120px;text-align: center'>城市套路深,我要回农村</div>");
            ps.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2. 反射

2.1 反射概述,获取Class对象

  • 反射指的是什么?
    • 加载类,并允许以编程的方式解剖类中的各种成分(成员变量、方法、构造器等)。
  • 反射学什么?
    • 学习获取类的信息、操作它们
  • 反射第一步是什么?
    • 获取类的字节码:Class对象
  • 如何获取Class对象
    • Class c1 = 类名.class
    • Class中的静态方法
      • Class c2 = Class.forName(“全类名”)
    • Object类中提供的方法: public Class getClass();
      • Class c3 = 对象.getClass();
public class Student {
}
public class Demo {
    public static void main(String[] args) throws ClassNotFoundException {
        // 1.类名.class
        Class<Student> c1 = Student.class;
        // 2.Class类提供的静态方法  forName(全类名(包名+类名))
        // idea 选择类,右键 Copy Reference  复制一个类的全类名
        Class<?> c2 = Class.forName("com.itheima.d2_reflect.d1_start.Student");
        // 3.Object类提供的方法 对象名.getClass()
        Student stu = new Student();
        Class<? extends Student> c3 = stu.getClass();

        // 同一个类的字节码文件对象,只有一个
        System.out.println(c1 == c2);
        System.out.println(c2 == c3);
    }
}

2.2 构造器(构造方法)

获取构造器

Class提供的方法说明
Constructor<?>[] getConstructors()获取全部构造器(只能获取public修饰的)
Constructor<?>[] getDeclaredConstructors()获取全部构造器(只要存在就能拿到)
Constructor getConstructor(Class<?>… parameterTypes)获取某个构造器(只能获取public修饰的)
Constructor getDeclaredConstructor(Class<?>… parameterTypes)获取某个构造器(只要存在就能拿到)
public class Student {
 private String name;
 private int age;

 private Student() {
     System.out.println("无参构造器执行了~~~~");
 }

 public Student(String name, int age) {
     System.out.println("有参构造器执行了~~~~");
     this.name = name;
     this.age = age;
 }

 @Override
 public String toString() {
     return "Student{" +
             "name='" + name + '\'' +
             ", age=" + age +
             '}';
 }

 public String getName() {
     return name;
 }

 public void setName(String name) {
     this.name = name;
 }

 public int getAge() {
     return age;
 }

 public void setAge(int age) {
     this.age = age;
 }
}
public class Demo1 {
 public static void main(String[] args) throws NoSuchMethodException {
     // 0.获取Class对象
     Class<Student> c = Student.class;

     // 1.获取全部构造方法(只能获取public) 将所有构造方法 放到一个数组中返回
     Constructor<?>[] arr1 = c.getConstructors();
     for (Constructor<?> constructor : arr1) { // constructor表示每一个构造方法
         System.out.println(constructor);
     }
     System.out.println("===================");
     // 2.获取全部构造方法(任意修饰符)
     Constructor<?>[] arr2 = c.getDeclaredConstructors();
     for (Constructor<?> constructor : arr2) {
         System.out.println(constructor);
     }
     System.out.println("===================");
     // 3.获取单个构造方法(只能获取public修饰),参数类型的字节码文件对象,要和构造方法的参数一一对应
     Constructor<Student> constructor1 = c.getConstructor(String.class, int.class);
     System.out.println(constructor1);
     System.out.println("===================");
     // 4.获取单个构造方法(任意修饰符)
     Constructor<Student> constructor2 = c.getDeclaredConstructor();
     System.out.println(constructor2);

 }
}

获取构造器的作用

  • 获取类构造器的作用:依然是初始化对象返回
Constructor中的方法说明
T newInstance(Object… initargs)调用此构造器对象表示的构造器,并传入参数,完成对象的初始化并返回
public void setAccessible(boolean flag)设置为true,表示禁止检查访问控制(暴力反射)
/*
    获取构造器的作用: 创建该类的对象,类的初始化
 */
public class Demo2 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 1.获取Class对象
        Class<Student> c = Student.class;
        // 2.获取带参数的构造方法
        Constructor<Student> constructor1 = c.getDeclaredConstructor(String.class, int.class);
        Student stu1 = constructor1.newInstance("张三", 23);// 执行构造方法,给参数赋值
        System.out.println(stu1);

        // 3.获取空参的构造方法
        Constructor<Student> constructor2 = c.getDeclaredConstructor();
        constructor2.setAccessible(true); // 不检查访问权限,暴力反射
        Student stu2 = constructor2.newInstance();
        System.out.println(stu2);
    }
}

2.3 成员变量

获取成员变量

Class提供的方法说明
public Field[] getFields()获取类的全部成员变量(只能获取public修饰的)
public Field[] getDeclaredFields()获取类的全部成员变量(只要存在就能拿到)
public Field getField(String name)获取类的某个成员变量(只能获取public修饰的)
public Field getDeclaredField(String name)获取类的某个成员变量(只要存在就能拿到)
public class Student {
 public int a;
 public static String school = "传智大学";
 private String name;
 private int age;


 public Student() {
 }

 public Student(String name, int age) {
     this.name = name;
     this.age = age;
 }

 @Override
 public String toString() {
     return "Cat{" +
             "name='" + name + '\'' +
             ", age=" + age +
             '}';
 }

 public String getName() {
     return name;
 }

 public void setName(String name) {
     this.name = name;
 }

 public int getAge() {
     return age;
 }

 public void setAge(int age) {
     this.age = age;
 }

 public int getA() {
     return a;
 }

 public void setA(int a) {
     this.a = a;
 }
}
/*
 获取成员变量Field
*/
public class Demo1 {
 public static void main(String[] args) throws NoSuchFieldException {
     // 1.获取Class对象
     Class<Student> c = Student.class;
     // 获取所有的成员变量(任意权限,只要存在就能获取)
     Field[] fields = c.getDeclaredFields();
     for (Field field : fields) {
         System.out.println(field.getName()); // getName获取成员变量的名字
     }
     System.out.println("=====================");
     // 通过成员变量的名字 获取指定的成员变量
     Field school = c.getDeclaredField("school");
     System.out.println(school.getName());
     Field age = c.getDeclaredField("age");
     System.out.println(age.getName());
 }
}

获取成员变量的作用

  • 依然是赋值、取值。
Field中的方法说明
void set(Object obj, Object value):赋值
Object get(Object obj)取值
public void setAccessible(boolean flag)设置为true,表示禁止检查访问控制(暴力反射)
public class Demo2 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        // 1.获取Class对象
        Class<Student> c = Student.class;
        // 2.获取成员变量
        Field school = c.getDeclaredField("school");

        Student stu = new Student();
        // 参数一: 操作哪一个对象的成员变量
        // 参数二: 给成员变量赋什么值
        // 给school赋值,获取值
        school.set(stu,"社大专修学院");
        System.out.println(school.get(stu));
        // 获取age成员变量
        Field age = c.getDeclaredField("age");
        // 给age赋值和获取值
        age.setAccessible(true); // 如果成员变量是private 取消权限检查 暴力反射
        age.set(stu,32);
        System.out.println(age.get(stu));
    }
}

2.4 成员方法

获取成员方法

Class提供的方法说明
Method[] getMethods()获取类的全部成员方法(只能获取public修饰的)
Method[] getDeclaredMethods()获取类的全部成员方法(只要存在就能拿到)
Method getMethod(String name, Class<?>… parameterTypes)获取类的某个成员方法(只能获取public修饰的)
Method getDeclaredMethod(String name, Class<?>… parameterTypes)获取类的某个成员方法(只要存在就能拿到)
public class Student {
 public int a;
 public static String school = "XX大学";
 private String name;
 private int age;


 public Student() {
 }

 public Student(String name, int age) {
     this.name = name;
     this.age = age;
 }

 @Override
 public String toString() {
     return "Cat{" +
             "name='" + name + '\'' +
             ", age=" + age +
             '}';
 }

 public String getName() {
     return name;
 }

 public void setName(String name) {
     this.name = name;
 }

 public int getAge() {
     return age;
 }

 public void setAge(int age) {
     this.age = age;
 }

 public int getA() {
     return a;
 }

 public void setA(int a) {
     this.a = a;
 }

 public void run() {
     System.out.println("跑的快");
 }

 public void eat() {
     System.out.println("吃的多");
 }

 private void eat(String name) {
     System.out.println("最爱吃" + name);
 }
}
public class Demo1 {
 public static void main(String[] args) throws NoSuchMethodException {
     //1.获取Class对象
     Class<Student> c = Student.class;
     // 2.获取所有的public方法(包括从父类中继承的)
     Method[] methods1 = c.getMethods();
     for (Method method : methods1) {
         System.out.println(method.getName()); // 获取成员方法的名字
     }
     System.out.println("=====================");
     Method[] arr2 = c.getDeclaredMethods();
     for (Method method : arr2) {
         System.out.println(method.getName());
     }

     System.out.println("=====================");
     // 3.获取单个成员方法(方法名,参数类型)
     Method run = c.getDeclaredMethod("run");
     System.out.println(run);
     Method eat = c.getDeclaredMethod("eat", String.class);
     System.out.println(eat);

 }
}

获取成员方法的作用

  • 成员方法的作用:依然是执行
Field中的方法说明
public Object invoke(Object obj, Object… args)触发某个对象的该方法执行。
public void setAccessible(boolean flag)设置为true,表示禁止检查访问控制(暴力反射)
/*
    获取方法,并且执行方法
 */
public class Demo2 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        // 1.获取Class对象
        Class<Student> c = Student.class;
        // 2.获取指定的成员方法
        // 3.执行方法
        Method run = c.getDeclaredMethod("run");
        Student stu = new Student();
        // 参数一: 具体的学生对象
        // 参数二: 给方法的参数赋值
        // 返回值和run方法的返回值一致
        run.invoke(stu);

        // 获取私有的eat方法
        Method eat = c.getDeclaredMethod("eat", String.class);
        eat.setAccessible(true); // 禁止权限访问检查,暴力反射
        eat.invoke(stu,"重庆小面");
    }
}

2.5 反射的作用和应用场景

反射的作用

  • 基本作用:可以得到一个类的全部成分然后操作。
  • 可以破坏封装性。(禁止检查访问控制)
  • 最重要的用途是:适合做Java的框架,基本上,主流的框架都会基于反射设计出一些通用的功能。

反射的应用场景

  • 需求: 对于任意一个对象,该框架都可以把对象的字段名和对应的值,保存到文件中去。
  • 步骤:
    • ①定义一个方法,可以接收任意对象。
    • ②每收到一个对象后,使用反射获取该对象的Class对象,然后获取全部的成员变量。
    • ③遍历成员变量,然后提取成员变量在该对象中的具体值。
    • ④把成员变量名、和其值,写出到文件中去即可。
public class Demo {
    public static void main(String[] args) throws IllegalAccessException, FileNotFoundException {
        Student stu = new Student("柳岩",40,'女',167.5,"女星");
        show(stu);
        Teacher t = new Teacher("肥仔",6000);
        show(t);
    }

    // 1.定义一个方法,方法的参数接收任意的对象
    public static void show(Object obj) throws IllegalAccessException, FileNotFoundException {
        PrintStream ps = new PrintStream(new FileOutputStream("day15-code\\obj.txt",true));
        // 2.通过对象  获取Class    对象名.getClass
        Class<?> c = obj.getClass();
        // getSimpleName() 获取类名  getName() 获取全类名
        ps.println("==================="+c.getSimpleName()+"===================");
        // 3.获取所有的成员变量   Field[]
        Field[] fields = c.getDeclaredFields();
        // 4.遍历数组 将成员变量名和成员的值 写入到一个文件中
        for (Field field : fields) { // 获取每一个成员变量
            // 获取成员变量名
            String name = field.getName();
            // 获取成员变量的值
            field.setAccessible(true);
            Object value = field.get(obj);
            ps.println(name + "=" + value);
        }
        ps.close();
    }
}

public class Student {
    private String name;
    private int age;
    private char sex;
    private double height;
    private String hobby;

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex=" + sex +
                ", height=" + height +
                ", hobby='" + hobby + '\'' +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

    public String getHobby() {
        return hobby;
    }

    public void setHobby(String hobby) {
        this.hobby = hobby;
    }

    public Student(String name, int age, char sex, double height, String hobby) {
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.height = height;
        this.hobby = hobby;
    }

    public Student() {
    }
}

public class Teacher {
    private String name;
    private double salary;

    @Override
    public String toString() {
        return "Teacher{" +
                "name='" + name + '\'' +
                ", salary=" + salary +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public Teacher() {
    }

    public Teacher(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }
}

2.6 反射操作带有泛型的类

  • 使用反射向List集合中添加任意类型的数据
  • 反射操作带有泛型的类时,跳过泛型检查
  • 泛型的原理: 泛型擦除,只在编译阶段有效,一旦编译成class文件 泛型就不存在了
  • 反射获取字节码文件的对象
public class Demo {
    /*
        反射操作带有泛型的类时,跳过泛型检查
        泛型的原理: 泛型擦除,只在编译阶段有效,一旦编译成class文件 泛型就不存在了
        反射获取字节码文件的对象
     */
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        List<String> list = new ArrayList<>();
        // 1.获取Class
        Class<? extends List> c = list.getClass();
        // 2.获取add方法
        Method add = c.getDeclaredMethod("add", Object.class);
        // 3.执行add方法
        add.setAccessible(true);
        add.invoke(list,"abc");
        add.invoke(list,123);

        // 使用反射操作带有泛型的类时,一定注意数据类型,有可能出现类型转换异常
        for (String s : list) {
            System.out.println(s);
        }
    }
}

2.7 反射练习

  • obj.properties
#name=com.itheima.d2_reflect.d7_test3.Student
#name=com.itheima.d2_reflect.d7_test3.Teacher
name=com.itheima.d2_reflect.d7_test3.Manager
  • 需求:
    • properties文件中 记录的是什么类名,创建什么类的对象 记录的是什么方法,我就调用什么方法
    • 有什么办法让学生类和老师类必须要有show方法 答案: 接口 接口: 制定规则
public class Teacher implements Inter{
    public void show(){
        System.out.println("老师教书");
    }
}
public class Manager implements Inter{
    @Override
    public void show() {
        System.out.println("管理班级");
    }
}
public class Student  implements Inter{
    public void show(){
        System.out.println("学生学习");
    }
}
public interface Inter {
    void show();
}

public class Demo {
    /*
        properties文件中
            记录的是什么类名,创建什么类的对象
            记录的是什么方法,我就调用什么方法
        有什么办法让学生类和老师类必须要有show方法
            答案: 接口
            接口: 制定规则
     */
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 1.读取properties中的内容
        Properties prop = new Properties();
        prop.load(new FileInputStream("day15-code\\obj.properties"));
        String name = prop.getProperty("name"); // 获取的是全类名
//        String method = prop.getProperty("method");// 获取方法名

        // 2.通过全类名,获取Class
        Class<?> c = Class.forName(name);

        // 3.获取这个类的构造方法,创建对象
        Constructor<?> constructor = c.getConstructor();
        constructor.setAccessible(true);
        Object obj = constructor.newInstance();

        // 4.通过方法名获取方法
        Method m = c.getDeclaredMethod("show");

        // 5.运行这个方法
        m.setAccessible(true);
        m.invoke(obj);
    }
}

3. 小结

3.1 网络编程之TCP编程

请添加图片描述

3.2 反射

请添加图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值