Java基础Day25

  1. 反射
    1.1 类的加载
    程序在使用一个类型(类或者接口)时,如果这个类型没有在内存中,系统就会通过加载,连接,初始化三个步骤将类型加载进内存中

  2. 加载 : 将类型的.class文件加载进内存中,.class文件称为字节码文件,系统同时在堆内存中创建一个对应的字节码文件对象, 所有类型在内存中都是具有字节码文件对象

  3. 连接 : 检测.class文件中的内容的语法格式是否正确,将类中的静态成员加载进内存

  4. 初始化 : 执行代码中的内容,new …

类的加载机制(什么时候类需要进内存):

  1. 类中的main方法执行
  2. 调用了类中的静态成员, 类名.调用 ; 创建对象调用
  3. 创建对象(new)
  4. 当前类的父类,随着当前类型的加载需要父类先进内存

1.2 反射机制
反射 : 任意一个类型在内存中运行时,都可以获取到这个类型中的所有的变量和方法,对衣对于这个类型的任意一个对象,都可以使用类型中变量和方法. 这种动态的获取变量和方法的过程,称为反射

翻译 : 任意一个类型,需要在程序中运行,那么就一定需要进入到内存的类型的字节码文件(.class文件),系统会生成这个类型对应的字节码文件对象,通过这个字节码文件对象,将类型中的所有的变量和方法都可以获取到,称为动态的获取方式

1.3 Class类型
一个类型进入到内存后,系统生成的字节码文件对象的类型就是一个Class类型

Class类型 : 来自于java.lang包,表示Class 类的实例表示正在运行的 Java 应用程序中的类和接口

1.4 获取Class类对象的三种方式

  1. 创建一个Person对象,通过对象中的getClass()获取到Person对应的字节码文件对象
    getClass() : 方法来自于父类Object中的方法,为了获取到所有类型的字节码文件对象
  2. 所有类型在定义时,系统默认给出的一个静态的成员变量,叫做class,每个类型都有,包括基本数据类型都具有class变量
  3. Class类型中,有一个静态的方法,forName(String className):
    forName(String className) : 获取到给定的参数所表示的字节码文件对象
    className参数 : 是一个带有完整包名的类名

说明 : 类型在内存中的字节码文件对象只有1个

代码
package com.zgjy.reflect;
public class PersonClass {
public static void main(String[] args) throws ClassNotFoundException {
// 1. 创建一个Person对象,通过对象中的getClass()获取到Person对应的字节码文件对象
// getClass() : 方法来自于父类Object中的方法,为了获取到所有类型的字节码文件对象
Person p = new Person();
Class c = p.getClass();
//class com.zgjy.reflect.Person
System.out.println©;

// 2. 所有类型在定义时,都有一个静态的成员变量,叫做class,每个类型都有,系统默认给出的
// 基本数据类型都是具有class变量
Class c1 = Person.class;
System.out.println(c1);

	// 3. Class类型中,有一个静态的方法,forName(String className):
	// forName(String className) : 获取到给定的参数所表示的字节码文件对象
	// className参数 : 是一个带有完整包名的类名
	Class c2 = Class.forName("com.zgjy.reflect.Person");
	System.out.println(c2);
	
	// 4. Person类型在内存中的字节码文件对象只有1个
	System.out.println(c==c1); // true
	System.out.println(c1==c2);// true
}

}

1.5可变参数
可变参数 : 方法的参数是可变的参数

修饰符 返回值类型 方法名(可变参数){
// 方法功能
}

举例:
public static int getSumFinal(int…is){

}

说明:
终极要求 : 求任意个数的int类型整数求和
分析终极要求 : 参数类型固定,参数个数不一定,将参数设计成可变参数
参数设计成可变参数 : 数据类型…变量名,三个.就表示个数可变参数
解释 : getSumFinal方法,实际调用时,传递的int类型的参数个数可以是任意个数
1) 不传参数,可以
2) 传一个参数,可以
3) 多个参数,可以
4) 可变参数变量类型,当做数组类型使用
5) 一个方法只能有一个可变参数,并且这个可变参数必须在所有参数的最后

代码
package com.zgjy.reflect;
public class ChangeParam {
// 客户要求 : 定义方法,功能,做两个int类型整数的求和
// 追加要求 : 三个int类型整数求和
// 继续追加 : 50个int类型整数和
// 终极要求 : 可有求任意个数的int类型整数求和
// 分析终极要求 :从参数类型固定,个数不一定,将参数设计成可变参数

public static int getSum(int x ,int y) {	
	return x+y;
}

public static int getSum(int x ,int y,int z) {
	return x + y + z;
}

// 终极要求 : 求任意个数的int类型整数求和
// 分析终极要求 : 参数类型固定,参数个数不一定,将参数设计成可变参数
// 参数设计成可变参数 : 数据类型...变量名,三个.就表示个数可变参数
// 解释 : getSumFinal方法,实际调用时,传递的int类型的参数个数可以是任意个数
// 1) 不传参数,可以
// 2) 传一个参数,可以
// 3) 多个参数,可以
// 4) 可变参数类型,当做数组类型使用
// 5) 一个方法只能有一个可变参数,并且这个可变参数必须在所有参数的最后
public static int getSumFinal(int...is) {// is就是一个int类型的数组
	int sum = 0;
	// 数组遍历,将数组中的每一个元素获取到,循环求和
	for(int x : is) {
		sum = sum +x;	
	}
	return sum;
}

/*

  • 错误代码.可变参数只能有一个

  • public static int getSumFinal1(double…t,int…is) {// is就是一个int类型的数组
    int sum = 0;
    // 数组遍历,将数组中的每一个元素获取到,循环求和
    for(int x : is) {
    sum = sum +x;
    }
    return sum;
    }*/

    public static void main(String[] args) {
    // 1. 不传递参数
    int sum = getSumFinal();// 0
    System.out.println(sum);
    // 2. 传递1个参数
    int sum1 = getSumFinal(45);// 45
    System.out.println(sum1);
    // 3. 多个参数
    int sum2 = getSumFinal(9,18,20,-10);// 37
    System.out.println(sum2);
    }
    }

1.6通过反射获取构造方法并运行
Constructor类: 表示一个构造器,构造函数,构造方法,来自于java.lang.reflect包

获取类型中的构造方法:

  1. getConstructors() : 获取到当前类中的所有的公共修饰(public)的构造方法,返回值类型构造器数组
  2. getDeclaredConstructors(): 获取到类型中的所有构造方法,不验证构造方法的修饰符,返回值类型构造器数组
  3. getConstructor(Class<?>… parameterTypes) : 获取到类中指定的公共修饰的构造方法,返回值类型Constructor构造器类型
  4. getDeclaredConstructor(Class<?>… parameterTypes) : 获取到类中指定的构造方法,返回值类型Constructor构造器类型

运行构造方法:
Constructor类中寻找运行方法

  1. newInstance(Object…param): 参数表示要运行的构造方法需要提供的实际参数,方法的返回值类型,是一个Object类型,运行构造方法,就相当于创建一个对象

运行构造方法的快捷方式:
要求 : 类型中包含一个空参数的公共修饰的构造方法
可以直接使用Class这个类型对象中的newInstance()方法,直接运行空参数构造,创建出一个对象

代码
package com.zgjy.reflect;
import java.lang.reflect.Constructor;
public class PersonReflectCon {
public static void main(String[] args) throws Exception {
//getCon2();
//getCon3();
//getCon4();
getFastCon();
}

//1.获取到当前类中的所有的公共修饰的构造方法,返回值类型构造器数组 
public static void getCon1() {	
	// 1. 获取到Person类型的字节码文件对象
	Class c = Person.class;
	// 2. 通过Class类中的方法 1.getConstructors() : 获取到当前类中的所有的公共修饰的构造方法,返回值类型构造器数组
	Constructor[] con = c.getConstructors();
	//3. 查看所有公共修饰构造
		for(Constructor cc : con) {
			System.out.println(cc);
		}	
}

//2. 获取到当前类中的所有的构造方法,返回值类型构造器数组 
public static void getCon2() {
	
	// 1. 获取到Person类型的字节码文件对象
	Class c = Person.class;
	// 2. 通过Class类中的方法 1.getConstructors() : 获取到当前类中的所有的公共修饰的构造方法,返回值类型构造器数组
	Constructor[] con = c.getDeclaredConstructors();
	//3. 查看所有公共修饰构造
	for(Constructor cc : con) {
		System.out.println(cc);
	}	
}

// 3. 获取指定的公共修饰的构造并运行
public static void getCon3() throws Exception {
	// 1. 获取到Person类型的字节码文件对象
	Class c = Person.class;
	// 2.getConstructor(Class...param) : 获取到类中指定的公共修饰的构造方法
	Constructor con = c.getConstructor(String.class,int.class,String.class);
	System.out.println(con);
	// 3. 运行公共修饰的构造方法,使用Constructor类中的newInstance(构造方法实际参数)
	Object obj = con.newInstance("张三",20,"男");
	// 4. 将obj转换成Person类型,需要多态的向下转型
	Person p = (Person)obj;
	// 5. 将Person中的信息获取到
	System.out.println(p.getName()+"--"+p.getAge()+"--"+p.getSex());
}

// 4. 获取任意的构造并运行
public static void getCon4() throws Exception {
	// 1. 获取到Person类型的字节码文件对象
	Class c = Person.class;
	// 2.getDeclaredConstructor(Class...param):获取到类中指定的构造方法
	// 获取到Person中的私有构造
	Constructor con = c.getDeclaredConstructor(String.class);
	System.out.println(con);
	// 3. 运行私有构造
	// 直接运行私有构造方法,报错,私有的权限控制不能执行在外类运行
    Object obj = con.newInstance("QQ");
    // 4. 将obj转换成Person类型,需要多态的向下转型
 	Person p = (Person)obj;
 	// 5. 将Person中的信息获取到
 	System.out.println(p.getName()+"--"+p.getAge()+"--"+p.getSex());
}

// 5. 快速进行构造方法的运行
public static void getFastCon() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
	// 1. 创建一个Person类型的Class字节码文件对象
	Class c = Class.forName("com.zgjy.reflect.Person");
	// 2. Person类中有一个公共的空参数的构造,于是快速运行构造方法
	// 使用Class类中的方法newInstance();
	Object obj = c.newInstance();
	// 3. 将obj转换成Person类型,需要多态的向下转型
	Person p = (Person)obj;
	// 4. 将Person中的信息获取到
	System.out.println(p.getName()+"--"+p.getAge()+"--"+p.getSex());
}

}

1.7暴力反射
暴力反射 : 就是将类型中的私有成员通过暴力的方式进行运行
AccessibleObject类型,是Field(变量类),Method(方法类),Constructor(构造器类)的父类,来自于java.lang.reflect包, 主要作用就是: 在进行反射时,可以取消变量或者是方法的权限的检查,公共的,受保护的,默认的,私有的权限,都可以运行

AccessibleObject类型中的方法:
setAccessible(boolean flag): 参数flag设置成true, 功能就是取消变量或者是方法的修饰权限控制---->暴力反射

注意 : 尽量不要使用暴力反射破坏private的安全性

代码
// 获取私有构造,通过暴力反射执行私有构造
public static void getCon4() throws Exception {
// 1. 获取到Person类型的字节码文件对象
Class c = Person.class;
// 2.getDeclaredConstructor(Class…param):获取到类中指定的构造方法
// 获取到Person中的私有构造
Constructor con = c.getDeclaredConstructor(String.class);
System.out.println(con);
// 3. 运行私有构造
// 直接运行私有构造方法,报错,私有的权限控制不能执行在外类运行
// 使用暴力反射进行私有构造方法的运行
// 1) 先将私有的构造器中的权限检查取消,暴力反射
con.setAccessible(true);
Object obj = con.newInstance(“QQ”);
// 4. 将obj转换成Person类型,需要多态的向下转型
Person p = (Person)obj;
// 5. 将Person中的信息获取到
System.out.println(p.getName()+"–"+p.getAge()+"–"+p.getSex());
}

1.8通过反射获取成员变量并赋值
Field类 : 表示类或者是接口中的变量类型,来自于java.lang.reflect包

获取类型中的成员变量

  1. getFields(): 获取到类型中的使用公共修饰的变量,返回值Field类型数组
  2. getDeclaredFields(): 获取到类型中的所有变量,返回值Field类型数组
  3. getField(String name): 获取到类型中的指定使用公共修饰的变量,返回值类型Field
    参数name表示成员变量的名字
  4. getDeclaredField(String name):获取到类型中的指定变量,返回值类型Field

给类型中的成员变量赋值
Field类中方法:

  1. get(Object obj) : 参数需要一个对象,Class类型的一个实例化对象,返回值类型Object类型,返回当前这个变量的字段值
  2. set(Object obj,Object value): 表示给当前的字段进行赋值.
  1. obj : 表示的是对象
  2. value : 表示的新的字段值

代码
package com.zgjy.reflect;
import java.lang.reflect.Field;
public class PersonFlectField {
public static void main(String[] args) throws Exception {
//getField();
//getField1();
//getField2();
getField3();

}
// 1. 获取Person中的所有公共修饰的成员变量
public static void getField() throws ClassNotFoundException {
	// 1. 创建Person的Class类型的对象
	Class c = Class.forName("com.zgjy.reflect.Person");
	// 2. 获取Person中的公共修饰成员
	Field[] f = c.getFields();
	// 3. 查看成员变量
	for(Field fie : f) {
		System.out.println(fie);
	}
}

// 2. 获取Person中的所有成员变量
public static void getField1() throws ClassNotFoundException {
	// 1. 创建Person的Class类型的对象
	Class c = Class.forName("com.zgjy.reflect.Person");
	// 2. 获取Person中的公共修饰成员
	Field[] f = c.getDeclaredFields();
	// 3. 查看成员变量
	for(Field fie : f) {
		System.out.println(fie);
	}
}

// 3. 获取到指定的公共修饰的成员变量
public static void getField2() throws Exception {
	// 1. 创建Person的Class类型的对象
	Class c = Class.forName("com.zgjy.reflect.Person");
// 2.getField(String name): 获取到类型中的指定使用公共修饰的变量,返回值类型Field
	//  参数name表示成员变量的名字
	Field age = c.getField("age");
	System.out.println(age);
	// 3. 查看变量age的字段值
	// Field类中get(Object obj)
	// 1) 使用Class中的newInstance() 方法获取到一个对象
	Object obj = c.newInstance();
	// 查看的是哪个对象中的哪个成员变量
	// 查看obj这个对象中的age这个变量的值
	Integer a = (Integer)age.get(obj);
	System.out.println(a);//0
	
	// 4. 给age进行赋值
	// set(Object obj,Object value)
	// 给obj对象中的age变量赋值,赋的值为value
	age.set(obj, 28);
	
	// 5. 使用反射查看age的值
	System.out.println(age.get(obj));
	
	// 6.使用get方法进行变量值的查看
	Person p = (Person)obj;
	System.out.println(p.getAge());
}

 // 4. 获取到指定的私有修饰的成员变量
 public static void getField3() throws Exception {
		// 1. 创建Person的Class类型的对象
		Class c = Class.forName("com.zgjy.reflect.Person");
		// 2.getField(String name): 获取到类型中的指定的变量,返回值类型Field
		//  参数name表示成员变量的名字
		Field sex = c.getDeclaredField("sex");
		System.out.println(sex);
		
		// 3. 查看私有的成员变量sex的值
		// 1) 使用Class中的newInstance() 方法获取到一个对象
		// 2) 使用暴力反射,查看并且修改私有成员变量的值
		sex.setAccessible(true);
		Object obj = c.newInstance();
		System.out.println(sex.get(obj));
		
		// 4. 修改私有成员变量的值
		sex.set(obj, "nv");
		
		// 5. 查看修改后的变量值
		System.out.println(sex.get(obj));	
	}

}

1.9通过反射获取方法并运行
Method类 : 表示类中的一个方法,来自于java.lang.reflect包

获取类型中的方法:

  1. getMethods() : 获取到类型中的所有公共修饰的方法(包括从父类继承而来的公共修饰方法)
  2. getDeclaredMethods(): 获取到类型中的所有的方法,不包括父类继承的方法
  3. getMethod(String methodname,Class…param) : 获取到类型中的指定公共修饰的方法
    methodname : 表示方法名
    param : 表示方法的参数列表
    需要方法的参数列表的原因是 : 相同的方法名,参数列表不同,方法重载
  4. getDeclaredMethod(String methodname,Class…param) : 获取到类型中指定的方法

运行方法:
Method类中的方法

  1. invoke(Object obj,Object…param): 运行一个对象中的某一个方法,返回值类型是Object,表示返回方法的结果
  1. obj : 表示一个对象
  2. param : 表示要运行的方法的实际参数

代码
package com.zgjy.reflect;
import java.lang.reflect.Method;
public class PersonReflectMethod {
public static void main(String[] args) throws Exception {
//getMethod1();
//getMethod2();
//getMethod3();
getMethod4();
}
// 1. 获取到所有的公共修饰方法,包括从父类继承来的方法
public static void getMethod1() {
// 1. 创建一个Person对应的Class类型对象
Class c = Person.class;
// 2. 使用getMethods()
Method[] m = c.getMethods();
// 3. 查看所有的公共方法
for(Method me : m) {
System.out.println(me);
}
}

// 2. 获取到所有的方法,不包括从父类继承来的方法
public static void getMethod2() {
	// 1. 创建一个Person对应的Class类型对象
	Class c = Person.class;
	// 2. 使用getMethods()
	Method[] m = c.getDeclaredMethods();
	// 3. 查看所有的公共方法
	for(Method me : m) {
		System.out.println(me);
	}
}

// 3. 获取到指定的公共修饰的方法
	public static void getMethod3() throws Exception {
		// 1. 创建一个Person对应的Class类型对象
		Class c = Person.class;
		// 2. 使用getMethod()
		Method eat = c.getMethod("eat");
		System.out.println(eat);
	// 3. 运行eat方法,需要传递实际参数,而eat没有参数,于是第二个可变参数为空
		eat.invoke(c.newInstance());
		
	}
	
	// 4. 获取到私有修饰的方法
	public static void getMethod4() throws Exception {
		// 1. 创建一个Person对应的Class类型对象
		Class c = Person.class;
		// 2. 使用getDeclaredMethod
		/*3.getDeclaredMethodMethod(String methodname,Class...param) : 获取到类型中的指定公共修饰的方法
		  methodname : 表示方法名
		  param : 表示方法的参数列表
		需要方法的参数列表的原因是  : 相同的方法名,参数列表不同,方法重载*/
		Method m = c.getDeclaredMethod("getSum", int.class,int.class);
		System.out.println(m);
		
		// 4. 因为方法时私有的,于是需要暴力反射,将方法的权限检查取消
		m.setAccessible(true);
		
		// 5. 运行私有方法
		Object methodResult = m.invoke(c.newInstance(), 5,17);
		System.out.println("-----"+methodResult);			
	}

}

1.10反射实现泛型擦除案例
需求 : 创建一个ArrayList,存储字符串类型, 使用反射,向集合中添加Integer类型的数据,将list的结果输出到控制台上

分析:

  1. 创建一个ArrayList list集合,list集合中调用的add方法,只能添加String类型
  2. 可以获取到ArrayList对应的字节码文件对象
    Class c = Class.forName(“java.util.ArrayList”);
  3. 通过Class获取到add方法
    Method m= c.getDeclaredMethod(“add”,Object.class);
    m.invoke(obj,12);

代码
package com.zgjy.reflect;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class ArrayListReflect {
public static void main(String[] args) throws Exception{
// 1. 创建一个ArrayList集合
ArrayList list = new ArrayList<>();
//2. 向集合中添加字符串
list.add(“abc”);
list.add(“hello”);
//list.add(12);

	// 3. 获取到Arraylist的字节码文件对象,Class类型
	Class c = Class.forName("java.util.ArrayList");
	// 4. 使用反射,获取到ArrayList中的方法add
	Method add = c.getDeclaredMethod("add", Object.class);
	// 5. 运行add方法
	add.invoke(list, 12);
	add.invoke(list, -99);
	add.invoke(list, new Person());
	
	//6.查看list中的内容
	System.out.println(list);
}

}

1.11反射读取配置文件案例
定义配置文件run.properties内容如下:
className=com.zgjy.reflect.Person
methodName=getSum

通过读取配置文件内容,使用反射运行对应Person类中的对应方法getSum

分析:

  1. 有配置文件—>读取配置文件内容—>提取出有消息,反射需要的信息1) 完整包名的类名 2) getSum方法
  2. 读取配置文件,使用Hashtable子类,Properties---->load—>将配置文件内容读取到Properties中
  3. 通过Properties中方法getProperty(key)---->value—>获取到类名和方法名
  4. 通过类名和方法名使用反射进行Person中的getSum方法运行

代码
package com.zgjy.reflect;
import java.io.FileInputStream;
import java.lang.reflect.Method;
import java.util.Properties;
public class ReflectProperties {
public static void main(String[] args) throws Exception{
// 1. 使用IO流进行文件的内容读取
FileInputStream fis = new FileInputStream(“run.properties”);
// 2.将使用IO流读取到的配置文件内容放置到Properties键值对的集合中
Properties p = new Properties();
// 3. 使用Properties类中的load方法,将IO流中读取到的内容放置到集合中
p.load(fis);
// 4.将配置文件中的类名和方法名获取到
// 获取到带有完整包名的类名
String claName = p.getProperty(“className”);
// 获取到方法名
String mName = p.getProperty(“methodName”);
// 5.使用反射执行类中的指定方法
// 1) 获取到类型的Class类型对象
Class c = Class.forName(claName);
// 2) 通过Class类型获取到类中的方法
Method m = c.getDeclaredMethod(mName, int.class,int.class);
// 3) 因为方法时私有修饰的,于是需要暴力反射,取消的权限验证
m.setAccessible(true);
// 4)执行方法
Object result= m.invoke(c.newInstance(), 6,10);
System.out.println(result);
}
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值