文章目录

  • ==类加载==
  • 静态类加载和动态类加载
  • 概述
  • 类加载
  • JVM的类加载机制
  • JVM的类加载器
  • ==反射==
  • 概述
  • 获取Class类的对象 Class.forName(常用)
  • ==获取构造方法并使用 Constructor==
  • 概述
  • 案例1:反射+带参构造方法
  • 案例2:反射+私有构造方法
  • ==获取成员变量并使用 Field==
  • 概述
  • 案例1:反射获取成员变量并使用
  • 案例2:反射给成员变量赋值
  • ==反射获取成员方法并使用 Method==
  • 概述
  • 案例1:反射获取成员方法
  • 案例2:反射+构造方法、成员变量、成员方法
  • 案例3:ArrayList<Inetger>添加字符串
  • 案例4 : 通过配置文件运行类中的方法
  • ==模块化==
  • 概念
  • 模块基本使用
  • 模块服务的使用
  • 内聚和耦合


类加载

备注2022/8/11
Java程序编译后 会产生byte code 也就是.class文件。

静态类加载和动态类加载

备注2022/8/24
Class.forName 不仅可以表示类的类型,还代表动态加载类;
编译时加载是静态加载类、运行时加载是动态加载类;

静态加载:

程序编译时就加载所有类;
加载到内存中生成Class的实例对象;
通过new关键字生成对象;

动态加载:

程序运行时,通过Class.forName()、类名.class属性、对象.getClass() 进行动态加载;
加载类的过程中会初始化该类的static变量和代码块等,非静态类型对于forName而言仅加载类,并不执行任何代码;

概述

概念:

  • 将类的.class文件中的二进制数据读入java虚拟机的运行时数据区的方法区,在堆中创建一个java.lang.class对象,用来封装在方法区内的数据结构。
  • 类的生命周期:加载、链接、初始化、使用和卸载。其中加载、链接、初始化属于类加载的过程。

类加载

类加载的作用

  • 加载.class文件中的二进制数据到内存(方法区)→映射成jvm能识别的结构(方法区运行时的数据结构)→在堆内存中创建java.lang.Class对象。
  • 任何类被使用的时候,系统都会为之创建一个java.lang.Class对象

类的连接
类的连接 = 将类加载中创建好的class类合并至java虚拟机中,使之可以执行。

  • 验证阶段:用于检验被加载的类是否有正确的内部结构,并与其他类协调一致(类内部结构、与其他类协调问题)
  • 准备阶段:负责为类的静态变量分配内存,并设置默认初始化值(变量初始化)
  • 解析阶段:将常量池中的符号引用替换成直接引用。
    符号引用 = 一组符号来描述所引用的目标;直接引用 = 直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。

类的初始化
主要对类变量进行初始化,就是执行类的构造器方法init()的过程。
类的初始化步骤:

  • ①假如类还未被加载和连接,则程序先加载并连接该类
  • ②假如该类的直接父类还未被初始化,则先初始化被直接父类
  • ③假如类中有初始化语句,则系统依次执行这些初始化语句

注意:
在对该类直接父类初始化的时候,系统对直接父类的初始化步骤也遵循初始化步骤1-3
一个类被载入Java虚拟机的时候,同一个类就不会再次被载入了。

类的初始化时机: 强调首次

- 创建类的实例
 类名 对象名 = new 类名();
 
 - 调用类的类方法(静态方法)
 Integer.parseInt();
 
 - 访问类或者接口的类变量(静态变量),或者为该类变量赋值
 System.in;
类名.静态变量;
 
 - 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
 三种方式创建Class对象。
 class对象 = Class.forName(); 类Class的方法forName
 class对象 = 类名.class;       类的class属性
 class对象 = 对象.getClass();  对象的getClass方法
 
 - 初始化某个类的子类(初始化子类首先就会初始化父类)
 子类 对象名 = new 子类();
 
 - 直接使用java.exe命令来运行某个主类
 在命令行使用java.exe运行,

JVM的类加载机制

JVM的类加载机制有三种:全盘负责、父类委托、缓存机制。

  • 全盘负责:当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也由该类加载器负责载入,除非显示使用另外一个类加载器载入;
  • 父类委托:当一个类加载器负责加载某个Class时,先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类;
  • 缓存机制:保证所有被加载的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区中搜索该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存储在缓存区。

JVM的类加载器

ClassLoader: 负责加载类的对象
java运行时具有以下内置类加载器

类名

说明

Bootstrap class loader

启动类加载器

它是虚拟机的内置类加载器,通常表示null,并且没有父null

也就是这个类是类加载器的祖宗

Platform class loader

java9

从扩展目录加载类 即 lib/modules

Ext ClassLoader

java9之前

从扩展目录加载类 即lib/modules

ApplicationClassLoader

应用程序类加载器。系统类加载器通常用于定义应用程序类路径、模块路径和JDK特定工具上的类

类加载器的继承关系:Application的父加载器为Platform(java9)Ext(java8),Platform的父加载器为Bootstrap。

ClassLoader中的两个方法:

方法名

说明

static ClassLoader getSystemClassLoader()

返回用于委派的系统类加载器

ClassLoader.getSystemClassLoader()

ClassLoader getParent()

返回父类加载器进行委派

案例:

package itiheima315.test1;
public class ClassLoaderDemo {
    public static void main(String[] args) {
        // 系统类加载器 查看当前类的类加载器
        ClassLoader c = ClassLoader.getSystemClassLoader();
        System.out.println(c);//AppClassLoader(应用程序类加载器)

        // 父类加载器
        ClassLoader c2 = c.getParent();
        System.out.println(c2);//ExtClassLoader(扩展类加载器) 视频中是 PlatformClassLoader 
        // java8 的ExtClassLoader = java9 的 PlatformClassLoader 
        
        ClassLoader c3 = c2.getParent();
        System.out.println(c3);// null
    }
}

2022/7/28
当PlatformClassLoader.geParent() 执行 返回值是null,因为PlatformClassLoader的父类加载器是BootstrapClassLoader
而该类加载器会表示为null。

反射

概述

概念:

  • Java反射机制:运行时去获取一个类的变量和方法信息,通过获取到的信息创建对象、调用方法的一种机制;
  • 动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展;
  • 面向对象语言设计中,一切都是对象,包括创建的类也是对象,它是java.lang.Class类的实例对象;

理解xhj:

  • 自定义类要想使用–》类加载器加载对应的.class文件–》每一个.class文件都会包含构造方法、成员变量、成员方法等信息;
  • Class类就是所有.class文件对应的类型、躯体。
  • 不再通过自定义类去使用构造方法、成员变量、成员方法,而是用Class类去使用构造方法、成员变量、成员方法,这就是反射。
  • 反射就是把java类中的各种成分映射成相应的java类。

作用
可以完成一些正常情况下,无法实现的功能。——在ArrayList< Integer>集合中添加String字符串。
反射可以越过泛型检查,获取到原始的方法所需要的参数类型。

获取Class类的对象 Class.forName(常用)

原因: 通过反射去使用一个类。首先获取该类的 字节码文件对象=类型为Class类型的对象。

获取Class类型对象的方法:

方法

说明

类.class

使用类的class属性来获取该类对应的Class对象

Student.class将会返回Student类对应的Class对象

不会引起类初始化

对象.getClass()

调用对象的getClass()方法,返回该对象所属类对应的Class对象

该方法是Object类中的方法,所有的Java对象都可以调用该方法

会引起类初始化

Class.forName(String name)

使用Class类中的静态方法forName(String className)

该方法需要出入 字符串参数 = 某个类的全路径=完整包名的路径

会引起类初始化

案例:

package itiheima315.test2;
public class ReflectDemo {
    public static void main(String[] args) {
    1 使用《类的class属性》来获取该类对应的Class对象
        Class<Student> c1 = Student.class;
        System.out.println(c1);
        // 输出内容是:class itiheima315.test2.Student
        
        // 一个类在内存中只有一个字节码文件
        Class<Student> c2 = Student.class;
        System.out.println(c1 == c2);
        // 输出:true
        System.out.println("------");
        
    2 调用 对象的getClass()方法 
        Student s = new Student();
        Class<? extends Student> c3 = s.getClass();
        System.out.println(c1 == c3);
        
    3 使用《Class类中的静态方法forName(String className)》
        Class<?> c4 = null;
        try {
            c4 = Class.forName("itiheima315.test2.Student");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        System.out.println(c1 == c4);
    }
}

2022/7/28
一个类在内存中只有一个字节码文件。

获取构造方法并使用 Constructor

概述

Class类

  • 在java.lang包下,使用需要导包;Class Class< T >
  • public final class Class< T > extends Object implements …是最终类,说明不能被继承
  • Class类的实例表示正在运行的java应用程序的类和接口
  • 通过Class对象获取Constructor对象的数据

方法名

说明

Constructor< ? >[] getConstructors()

返回所有公共构造方法对象的数组

Constructor<?>[] getDeclaredConstructors()

返回所有构造方法对象的数组

Constructor< T> getConstructor(Class<?>…parameterType)

返回单个公共构造方法对象

要获取的构造方法的参数的个数和数据类型对应的字节码文件对象

使用的是:String类型,则用String.class;int类型,则用的是int.class

Constructor< T> getDeclaredConstructor(Class<?>…parameterType)

返回单个构造方法对象

要获取的构造方法的参数的个数和数据类型对应的字节码文件对象

Constructor类

  • 在java.lang.reflect包下,使用需要导包
  • public final class Constructor< T> extends Executable 是最终类
  • 提供了类的单个构造函数的信息和访问权限
  • 通过Constructor类中的newInstance方法创建对象:

方法名

说明

T newInstance(Object…initargs)

使用由此Constructor对象表示的构造函数创建对象

理解xhj:

  • 在反射中,实际上是将类中的构造方法、成员变量、成员方法,看成对象;
  • 通过反射创建类的对象:

思路

1 通过Class.forName方法得到 类的字节码文件对象
	Class<?> c = Class.forName("类的路径")
	抛出异常 ClassNotFoundException
	
2 通过字节码文件对象的getConstructor方法得到单个构造函数
	Constructor<?> con = c.getConstructor();
	抛出异常 NoSuchMethodException
	
3 通过Constructor对象的newInstance方法创建对象
	Object obj = con.newInstance();
	抛出异常 IllegalAccessException, InvocationTargetException, InstantiationException

案例:

package itiheima315.test2;
public class ReflectDemo2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        # 获取Class对象,也就是类的字节码对象
        Class<?> c = Class.forName("itiheima315.test2.Student");
        // 得到Student类的字节码文件对象

        # 要想获取构造方法,要在Class类中找方法获取构造方法。
        // 即getConstructors、getDeclaredConstructors、getConstructor、getDeclaredConstructor

        Constructor< ? >[] getConstructors() :返回一个包含Constructor对象的数组
//        Constructor<?>[] arrayC = c.getConstructors();
//        for(Constructor con:arrayC){
//            System.out.println(con);
//        }
        // 结果:
        // public itiheima315.test2.Student(java.lang.String,int,java.lang.String)
        //public itiheima315.test2.Student() 

        Constructor<?>[] getDeclaredConstructors() :返回反映该Class对象的类声明的所有构造函数的Constructor对象的数组
//        Constructor<?>[] conS = c.getDeclaredConstructors();
//        for (Constructor con : conS) {
//            System.out.println(con);
//        }
        // 结果
        /*
        public itiheima315.test2.Student(java.lang.String,int,java.lang.String) 公共构造方法
        itiheima315.test2.Student(java.lang.String,int)  默认构造方法
        private itiheima315.test2.Student(java.lang.String) 私有构造方法
        public itiheima315.test2.Student()*/ 公共无参构造方法

        Constructor< T> getConstructor(Class<?>.....parameterType):返回一个Constructor对象,该对象反映该Class对象表示的类的指定公共构造函数 
        Constructor<?> c0 = c.getConstructor();//拿public的无参构造方法
//        System.out.println(c0);

        // 使用Constructor对象来实现通过类创建对象。
        Object obj = c0.newInstance();
        System.out.println(obj);
// 输出Student{name='null', age=0, address='null'}

        // Constructor< T> getDeclaredConstructor(Class<?>...parameterType)|返回一个Constructor对象,该对象反映由此Class对象表示的类或接口的指定构造函数|
    }
}

Student类

package itiheima315.test2;

public class Student {
    private String name;//私有成员变量
    int age;//默认
    public String address;//公共

    // 构造方法
    // 公共
    public Student(){}
    // 私有
    private Student(String name){
        this.name = name;
    }
    // 默认
    Student(String name,int age){
        this.name = name;
        this.age = age;
    }
    // 公共
    public Student(String name,int age,String address){
        this.name = name;
        this.age = age;
        this.address = address;
    }

    // 成员方法
    private void function(){
        System.out.println("function");
    }
    public void method1(){
        System.out.println("method");
    }
    public void method2(String s){
        System.out.println("method:" + s);
    }
    public String method3(String s,int i){
        return s + ", " + i;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                '}';
    }
}
案例1:反射+带参构造方法

需求:
通过反射实现以下操作:
Student s = new Student(“林俊杰”,30,“西安”); System.out.println(s);

2022/7/28
注意带参构造方法getConstructor(类型.class) 获取类的class对象。

思路

2022/5/19
1 获得class对象 
	类.class属性
2 获得constructor对象
	class对象.getConstructor(String.class,int.class,String.class);
3 使用newInstance方法创建对象
	constructor对象.newInstance("参数1","参数2","参数3");

基本数据类型可以通过.class得到对应的Class类型

package itiheima315.test2;

public class ReflectTest1 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    /*    Student s = new Student("林俊杰",30,"西安");
        System.out.println(s);*/

        Class<?> c = Class.forName("itiheima315.test2.Student");
        // 获取Class对象
        Constructor<?> con = c.getDeclaredConstructor(String.class,int.class,String.class);
        Object obj = con.newInstance("林俊杰", 30, "西安");
        System.out.println(obj);
    }
}
案例2:反射+私有构造方法

需求:
通过反射实现以下操作:
Student s = new Student(“林俊杰”); System.out.println(s);

public void setAccessible(boolean flag):值为true,取消访问检查 被称为暴力反射

  • 概述:
    该方法所属类是:pubilc final class Constructor< T > extends Executable 提供类的单个构造函数的信息和访问权限;
  • 作用:
    当通过getDeclaredConstructor方法获得私有构造方法 并 创建对象的时候,会报错,IllegalAccessException
  • 解决:
    对通过getDeclaredConstructor方法获得的私有构造方法使用setAccessible方法可以取消访问检查,即可实现利用私有构造方法创建对象。

2022/7/28 思路

1 获取类的Class对象
	Class cla = Class.forName(String name);
2 获取类的构造方法
	Constructor con = cla.getDeclaredConstructor(String.class);
3 取消访问检查
	con.setAccessible(true);
4 创建对象newInstance方法
	Object obj = con.newInstance();
package itiheima315.test2;

public class ReflectTest2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        /*Student s = new Student("林俊杰");
        System.out.println(s);*/
        Class<?> c = Class.forName("itiheima315.test2.Student");

        Constructor<?> con = c.getDeclaredConstructor(String.class);

        // 暴力反射
        // public void setAccessible(boolean flag):值为true,取消访问检查
        con.setAccessible(true);

        Object obj = con.newInstance("林俊杰");
        System.out.println(obj)
       
        //获取到的私有构造方法不能创建对象 会报错 IllegalAccessException
        //加了setAccessible方法 可以实现私有方法创建对象

        // 输出:Student{name='林俊杰', age=0, address='null'}
    }
}

获取成员变量并使用 Field

概述

获取成员变量的方法

方法名

说明

Field[] getFields()

返回类或接口的所有可访问的公共字段组成的数组

Field[] getDeclaredFields()

返回类或接口声明的所有字段组成的数组

Field getField(String name)

返回类或接口的指定公共字段,其中name是变量名

Field getDeclaredField(String name)

返回类或接口的指定字段,其中name是变量名

Field类

  • Field提供有关类或接口的单个字段的信息和动态访问
  • 所给成员变量赋值的方法:

方法名

说明

void set(Object obj,Object value)

将指定的对象参数,设置为由Field对象表示的字段

Field类变量名.set(对象,“具体值”) # 给对象的成员变量赋值为具体值

反射获取成员变量并赋值

1 获取Class对象
	Class<?> c = Class.forName("类的路径");
	
2 获取Class对象的无参构造方法
	Constructor<?> con = c.getConstructor();
	
3 获取Class对象的成员变量
	Field fName = c.getField("成员变量名");
	
4 通过无参构造方法创建对象
	Object obj = con.newInstance();
	
5 给对象的成员变量赋值
	fName.set(obj,"设置的值");
案例1:反射获取成员变量并使用
package itiheima315.test2;
public class ReflectDemo3 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException
            , IllegalAccessException, InvocationTargetException, InstantiationException {
        // 获取类的Class对象
        Class<?> c = Class.forName("itiheima315.test2.Student");

//        Field[] fields = c.getFields();//获取公共的成员变量
        Field[] fields = c.getDeclaredFields();//获取所有成员变量组成的数组
        for(Field f:fields){
            System.out.println(f);
        }
        System.out.println("-------");

		// 获取成员变量
        Field addressField = c.getField("address");
        
        //获取无参构造方法创建对象
        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();
		
		// 给成员变量赋值
        addressField.set(obj,"西安");
        System.out.println(obj);
    }
}
案例2:反射给成员变量赋值

需求:
通过反射实现:
Student s = new Student();
s.name = “汪苏泷”;
s.age = 30;
s.address = “阳泉”;
System.out.println(s);

2022/7/28 思路

1 获取类的Class类对象
Class cla = Class.forName("类的全路径名");

2 获取Constructor对象
Constructor con = cla.getConstructor();

3 创建实例对象
Object obj = con.newInstrance();

4 获取Filed对象
Filed fi1 = cla.getFiled("name");
Filed fi1 = cla.getFiled("age");
Filed fi1 = cla.getFiled("address");

5 设置成员变量的值
fi1.set(obj,"汪苏泷");
fi2.set(obj,"30");
fi3.set(obj,"阳泉");

代码

package itiheima315.test2;
public class ReflectTest3 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        // 通过反射实现
  /*      Student s = new Student();
        s.name = "汪苏泷";
        s.age = 30;
        s.address = "阳泉";
        System.out.println(s);*/

  		// 获取Class对象
        Class<?> c = Class.forName("itiheima315.test2.Student");
        // 获取无参构造方法
        Constructor<?> con = c.getConstructor();
        // 创建对象
        Object obj = con.newInstance();
        // 获取成员变量
//        Field fname = c.getField("name");
        Field fname = c.getDeclaredField("name");
        // 暴力反射
        fname.setAccessible(true);
        Field fage = c.getDeclaredField("age");
        fage.setAccessible(true);
        Field faddress = c.getField("address");
        // 给对象的成员变量赋值
        fname.set(obj, "汪苏泷");
        fage.set(obj, 30);
        faddress.set(obj,"北京");

        System.out.println(obj);

    }
}

注意:
当想使用私有的成员变量的时候,需要进行如下操作:

Field f = class类对象.getDeclaredField("成员变量名");
 f.setAccessible(ture);
 // 取消访问检查

当想使用私有成员方法的时候,需要进行如下操作:

Method method= class类对象.getDeclaredMethod("成员方法名",参数的.class对象);
method.setAccessible(ture);
 // 取消访问检查

反射获取成员方法并使用 Method

概述

获取成员方法的方法:

方法名

说明

Method[] getMethods()

返回类或接口的所有公共方法

包括类或接口声明的对象以及从超类和超级接口继承的类

Method[] getDeclaredMethods()

返回类或接口声明的所有方法

包括public、protected、default和private,不包括继承方法

Method[] getMethod(String name,Class<?> … parametreTyoes)

返回类或接口的指定公共成员方法

Method[] getDeclaredMethod(String name,Class<?> … parametreTyoes)

返回类或接口的指定成员方法

Method类

  • 在类或接口上提供有关单一方法的信息和访问权限
  • 实现对象调用方法

方法名

说明

Object invoke(Object obj,Object …args)

在具有指定参数的指定对象上调用此方法表示的基础方法

Object返回值类型;obj调用方法的对象;args方法需要的参数

反射获取成员方法

1 获取Class对象
	Class<?> c = Class.forName("类的路径");
	
2 获取无参构造方法
	Constructor con = c.getConstructor();
	
3 无参构造方法创建对象
	Object obj = con.newInstance();
	
4 获取成员方法
	Method m = c.getMethod("成员方法名");
	
5 对象使用成员方法对象
	m.invoke(obj,"成员方法所需参数");
案例1:反射获取成员方法
package itiheima315.test2;
public class ReflectDemo4 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException,
            IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<?> c = Class.forName("itiheima315.test2.Student");

//        Method[] methods = c.getMethods();//本类的 以及 继承的公共方法
//        Method[] methods = c.getDeclaredMethods();// 本类所有的方法
//        for(Method method:methods){
//            System.out.println(method);
//        }
//    }
        Method m1 = c.getMethod("method1");

        // 获取无参构造方法
        Constructor<?> con = c.getConstructor();
        // 创建对象
        Object obj = con.newInstance();
        // 反射调用方法
        m1.invoke(obj);
    }
}
案例2:反射+构造方法、成员变量、成员方法

需求 2022/5/19

// Student s = new Student();
Class stuclass = Student.class

// 创建类的对象
Constructor stucon = stuclass.getConstructor();
Object obj = stucon.newInstance();

// s.method1();
Method method1 = stuclass.getMethod("method1");
method1.invoke(obj);

// s.method2("汪苏泷");
Method method2 = stuclass.getMethod("method2",String.class);
method2.invoke(obj,"汪苏泷");

// String ss = s.method3("许嵩",32);
Method method3 = stuclass.getMethod("method3",String.class,int.class);
Object obj1 = method3.invoke(obj,"许嵩",32);

// System.out.println(ss);
String ss = (String)obj1; // 强制类型转换
System.out.println(ss);

//s.function(); 私有方法
Method function = stuclass.getDeclaredMethod("function");
function.setAccessible(true);
function.invoke(obj);

代码:

package itiheima315.test2;
public class ReflectTest4 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//         //使用反射实现如下操作:
//        Student s = new Student();
//        s.method1();
//        s.method2("汪苏泷");
//        String ss = s.method3("许嵩",32);
//        System.out.println(ss);
//        s.function();

        Class<?> c = Class.forName("itiheima315.test2.Student");

        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();

// s.method1();
        Method method1 = c.getDeclaredMethod("method1");
        method1.invoke(obj );
// s.method2("汪苏泷");
        Method method2 = c.getDeclaredMethod("method2", String.class);
        method2.invoke(obj,"汪苏泷");
//String ss = s.method3("许嵩",32);
        Method method3 = c.getDeclaredMethod("method3", String.class, int.class);
        Object ss = method3.invoke(obj, "许嵩", 30);
//  System.out.println(ss);
        String st = (String)ss;
        System.out.println(ss);
// s.function();
        Method function1 = c.getDeclaredMethod("function");
        // 由于function是私有成员,所以需要暴力反射
        function1.setAccessible(true);
        function1.invoke(obj);
    }
}

Student类

package itiheima315.test2;

public class Student {
    private String name;//私有成员变量
    int age;//默认
    public String address;//公共

    // 构造方法
    // 公共
    public Student(){}
    // 私有
    private Student(String name){
        this.name = name;
    }
    // 默认
    Student(String name,int age){
        this.name = name;
        this.age = age;
    }
    // 公共
    public Student(String name,int age,String address){
        this.name = name;
        this.age = age;
        this.address = address;
    }

    // 成员方法
    private void function(){
        System.out.println("function");
    }
    public void method1(){
        System.out.println("method");
    }
    public void method2(String s){
        System.out.println("method:" + s);
    }
    public String method3(String s,int i){
        return s + ", " + i;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                '}';
    }
}
案例3:ArrayList添加字符串

需求
有一个ArrayList< Integer>集合,在这个集合中添加一个字符串数据,如何实现?
反射可以完成一些正常情况下无法完成的事情
反射可以越过泛型检查的,获取到原始的方法所需要的参数类型

思路

1 ArrayList<Integer> 集合创建
List<Integer> list = new ArrayList<Integer>();

2 获取Class类对象
Class<?> cla = Class.forName("类全限定类名");

3 创建对象
Object obj = cla.newInstance();

4 获取add方法
Method<?> meAdd = cla.getMethod("add",object,class);

5 集合添加String类型变量
meAdd.invoke(obj,"添加元素");

代码

package itiheima315.test2;
public class ReflectTes5 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException,
            IllegalAccessException {
        ArrayList<Integer> array = new ArrayList<Integer>();

//        array.add(10);
//        array.add(20);
        // 获取Class对象,使用对象.getClass方法实现。
        Class<? extends ArrayList> c = array.getClass();
        Method madd = c.getMethod("add",Object.class);

        madd.invoke(array, "pretty");
        madd.invoke(array,"sunshine");

        System.out.println(array);

    }
}
案例4 : 通过配置文件运行类中的方法

思路2022/7/29

1 创建Properties对象
Properties pro = new Properties();

2 创建配置文件的字符输入流对象
FileReader fr = new FileReader("文件路径");

3 将字符输入流对象载入Properties对象
pro.load(fr);

4 关闭字符输入流对象
fr.close();

5 获取想要使用的类名字、方法名字
String className = pro.getProperties();
String name = pro.getPropreties();

6 通过反射获取类的Class对象
Class cla = className.class;

7 反射获取构造方法、创建对象、调用方法
Constructor con = cla.getConstructor();
Object obj = con.newInstance();
Method mName = cla.getMethod("name");
mName.invoke(obj);

代码:

package itiheima315.test3;
public class ReflectDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException,
            IllegalAccessException, InvocationTargetException, InstantiationException {
//        Student s = new Student();
//        s.study();
//
//        Teacher t = new Teacher();
//        t.teach();
        // 为了方便使用Student和Teacher两个类,可以使用配置文件完成 而不用每次修改main方法

        // 加载数据
        Properties prop = new Properties(); // 创建持久属性集 properties
        FileReader fr = new FileReader(".\\class.txt"); // 读取字符流
//        char[] chs = new char[1024];
//        int len;
//        while((len = fr.read(chs)) != -1){
//            System.out.println(new String(chs,0,len));
//        }
        // 输出结果是:
        // className=itiheima315.test3.Student
        //MethodName=study
        // 说明 内容已被读入
        
        prop.load(fr);
        fr.close(); // 字符流关闭

        // 数据文件加载成功
        String className = prop.getProperty("className"); // 使用指定键索引属性
        String methodName = prop.getProperty("MethodName");
        // 使用不使用 类以及其成员方法 在class.txt文件中修改

        // 通过反射来使用
        Class<?> c = Class.forName(className);//得到的是itiheima315.test3.Student
        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();


        Method m = c.getMethod(methodName);
        // System.out.println(m);
        m.invoke(obj);
        
    }
}

案例涉及Properties方法

方法名

说明

String getProperty(String key)

使用此属性列表中指定的键搜索属性

void load(InputStream instream)

从输入字节流读取属性列表 (键值对)

模块化

概念

  • 随着java语言的发展,逐渐成为一个“臃肿”的语言,无论是大系统还是小软件,JVM都需要加载整个JRE环境;
  • java9实现了模块化,成功给java实现了瘦身,允许java程序可以根据需求选择要加载的模块;
  • 整体的项目project,下面依次是:模块、包、类或者接口;
  • 模块与模块之间是独立的,也可以作为访问权限的界定边界,可以通过模块的描述文件设置包是否被暴露处理、隐藏处理,对于隐藏的包即使它所包含的java类型使用了public修饰,别的模块仍旧不能访问这些类型。

模块基本使用

基本使用步骤:

  • 创建模块 …包、类、定义方法
    定义两个模块,myOne和MyTwo
  • 在模块的src目录下新建一个名为module-info.java的描述性文件,该文件专门定义模块名、访问权限、模块依赖等信息,描述性文件中使用模块导出模块依赖进行配置并使用。
    所要使用的和被使用的两个模块都创建module-info.java文件
  • 模块中所有未导出的包都是模块私有的,他们不能在模块之外被访问
    模块导出格式:exports 包名;
  • 一个模块要访问其他模块,必须明确指定依赖那些模块,未明确指定依赖的模块不能访问
    模块依赖格式:requires 模块名;
    注意:写模块名报错,需要按下Alt+enter,选择模块依赖。

代码:
MyOne模块

package itiheima1;
public class Student {
    public void study(){
        System.out.println("你的自律,给你自信");
    }
}

package itiheima2;
public class Teacher {
    public void teach(){
        System.out.println("全心付出只为金榜题名");
    }
}

// module-info.java文件
module MyOne {
    exports itiheima1;
}

MyTwo模块:

package itiheima315;
import itiheima1.Student;
public class Test1 {
    public static void main(String[] args) {
        Student s = new Student();
        s.study();
    }
}

// module-info.java文件
module MyTwo {
    requires MyOne;
}

模块服务的使用

概述:

  • java6开始,提供了服务机制 = 允许服务提供者和服务使用者之间完成解耦 = 服务使用者只面向接口编程,不清楚服务提供者的实现类
  • java9的模块化编程系统进一步简化了java的服务机制
  • java9允许 = 》服务接口定义在模块中 =》使用uses语句声明该服务接口=》针对服务接口提供不同的服务实现类=》服务实现模块使用provides语句为服务接口指定实现类。 同样是服务使用者面向接口编程

模块服务的使用步骤

  • 在模块a下创建包a,创建接口a定义抽象方法a
  • 在包a下创建包b,创建接口a的两个实现类 类a和类b
  • 模块a中的描述文件module-info.java文件中写如下配置:
    模块导出:exports 包a
    服务提供:provides 接口a with 类a/类b
  • 在模块b的描述文件module-info.java中添加如下配置:
    声明服务接口:uses 接口a;
  • 在模块b的类中使用接口a提供的服务

图示:

java热加载jar包 java 模块化 热加载_java

ServiceLoader类

  • 在java.util包下 使用需要导包
  • public final class ServiceLoader< S > extends Object implements Iterable< S>说明是最终类,且可以使用增强for循环遍历
  • 一种加载服务实现的工具
  • 获取服务加载程序 =》 通过 ServiceLoader的静态方法load

方法名

说明

static < S> ServiceLoader< S > load(Class< S> service )

使用当前线程的context class loader为给定的服务类型创建一个新的服务加载器

ServiceLoader.load(服务名.class)参数是服务的class对象

代码:
模块b
测试类:

package itiheima315;
import itiheima3.MyService;
import java.util.ServiceLoader;
public class Test2 {
    public static void main(String[] args) {
        // 加载服务
        ServiceLoader<MyService> myService = ServiceLoader.load(MyService.class);
        // 遍历服务
        for(MyService my:myService){
            my.service();
        }
    }
}

module-info.java文件

// 模块MyTwo配置
module MyTwo {
   // 导入模块依赖
   requires MyOne;
   // 申明服务接口
   uses MyService;
}

模块a
接口:

package itiheima3;
public interface MyService {
    void service();
}

实现类:

package itiheima3.impl;

import itiheima3.MyService;

public class Czxy implements MyService {
    @Override
    public void service() {
        System.out.println("you are my pretty sunshine");
    }
}

public class itiheima implements MyService {
    @Override
    public void service() {
        System.out.println("唯有不断学习,才能缓解焦虑不安");
    }
}

module-info.java配置文件:

// 导入接口、实现类
import itiheima3.MyService;
import itiheima3.impl.itiheima;
import itiheima3.impl.Czxy;
// 模块MyOne配置
module MyOne {
    // 导出包
    exports itiheima1;
    exports itiheima3;

    // 服务实现:通过provides为服务接口指定实现类
//    provides MyService with itiheima;
    provides MyService with Czxy;
}

内聚和耦合

内聚 = 模块内部各成分之间相关程度的度量;
耦合 = 模块之间依赖程度的度量;
模块设计 = 高内聚、低耦合。