Java反射基础

1 反射简介

1.1 什么是反射

java反射机制是在运行状态中,可以获取任意一个类的结构 属性,创建对象,得到方法,执行方法。在这种运行状态动态获取信息以及动态调用对象方法的功能被称为java语言的反射机制。

1.2 反射的缺点

性能开销:由于反射涉及动态解析的类型,因此无法执行某些 Java 虚拟机优化。
破坏封装性: 反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。
内部曝光: 由于反射允许代码执行在非反射代码中非法的操作,例如访问私有字段和方法,这可能会导致代码功能失常并可能破坏可移植性。

2 反射机制

2.1 类加载过程

类加载的过程类加载过程:

1:java文件通过java编译器编译后,在磁盘中产生 .class文件。.class是二进制文件,通过jvm可以被识别。
2:jvm通过类加载器读取字节码文件,取出二进制数据,加载到内存中,解析 .class文件中的内容。.
3:加载结束后,JVM 开始进行连接阶段(包含验证、准备、初始化)。经过这一系列操作,类的变量会被初始化。

2.2 class对象

想使用反射,就必须获取先要使用该类对应的class对象。在java中,无论一个类生成多少个对象,这些对象都会对应一个 class对象,这个class对象是通过JVM生成的,通过class对象能够获取整个类的结构。

例如: Person person = new Person();
1:JVM加载的时候,当 new Person() 时,JVM会根据 Person的全限定类名去加载一个User.class文件。
2:JVM 会去本地磁盘查找 User.class 文件并加载 JVM 内存中。
3:JVM通过类加载器会创建User类的class对象。一个类只有一个class对象

2.3 方法的反射调用

方法的反射调用,也就是 Method.invoke 方法。

public static void main(String[] args) throws Exception{
		Class c=Class.forName("reflect_01.Person");
		//获取类的构造方法
		Constructor con=c.getConstructor();
		//创建了对象
		Object obj=con.newInstance();
		//获取单个方法:show
		Method m1=c.getMethod("show");
		//参数1:哪个对象要执行  参数2:可变参数
	    //public Object invoke(Object obj,Object...args)
		m1.invoke(obj); //show方法调用了
}
public class Person {
	public void show() {
		System.out.println("show方法调用了");
	}
}

3 使用反射

3.1 java.lang.reflect 包

java.lang.reflect 包的核心接口和类如下:

Member 接口:反映关于单个成员(字段或方法)或构造函数的标识信息。
Field 类:提供一个类的域的信息以及访问类的域的接口。
Method 类:提供一个类的方法的信息以及访问类的方法的接口。
Constructor 类:提供一个类的构造函数的信息以及访问类的构造函数的接口。
Array 类:该类提供动态地生成和访问 JAVA 数组的方法。
Modifier 类:提供了 static 方法和常量,对类和成员访问修饰符进行解码。
Proxy 类:提供动态地生成代理类和类实例的静态方法

3.2 获取 Class 对象

获取class对象的三种方式
1)Class.forName 静态方法(最常用的方式)
public static class forName(String classNanme)//需要全路径
使用类的完全限定名来反射对象的类。

package reflect_01;

public class ReflectDome1 {
	public static void main(String[] args) throws ClassNotFoundException {
		Class c1 = Class.forName("reflect_01.ReflectDome1");//全路径
	System.out.println(c1.getCanonicalName());//reflect_01.ReflectDome1

		Class c2=Class.forName("reflect_01.ReflectDome1");//全路径
	System.out.println(c2.getCanonicalName());//reflect_01.ReflectDome1

		System.out.println(c1 == c2);//true ,说明指向的是同一个class对象
	}

}

2)类名 + .class

package reflect_01;

public class ReflectDome1 {
	public static void main(String[] args) throws ClassNotFoundException {
		Class c3=ReflectDome1.class;	
		System.out.println(c3.getCanonicalName());//reflect_01.ReflectDome1

	}
}

3)object类的getClass方法

package reflect_01;

public class ReflectDome1 {
	public static void main(String[] args) throws ClassNotFoundException {
		//方式1
		ReflectDome1 p1 = new ReflectDome1();
		Class c1 = p.getClass();
		
		ReflectDome1 p2 = new ReflectDome1();
		Class c2 = p2.getClass();
		
		System.out.println(p1 == p2);//false
		System.out.println(c1 == c2);//true, 指向的是同一个class对象
		
		System.out.println(c.getCanonicalName());//reflect_01.ReflectDome1
		System.out.println(c2.getCanonicalName());//reflect_01.ReflectDome1
	}

}

3.3 创建实例

1)通过class对象 获取一个类的构造方法

package reflect_01;

public class ReflectDome3 {
	public static void main(String[] args) throws Exception {
		//获取字节码文件对象
		Class c=Class.forName("reflect_01.Person");
		
		//获取带参构造器
//		public Constructor<T> getConstructor(Class<?>...parameterTypes)
		Constructor con=c.getConstructor(String.class,int.class,String.class);//获取这样的构造方法,明确了类型
		
		//通过带参构造方法对象创建对象
//		public T newInstance(Object...initargs)
		Object obj=con.newInstance("lh",29,"北京");
		System.out.println(obj);//Person [name=鹿晗, age=29, addree=北京]
		
	}

}

public class Person {
	public String name;
	public int age;	
	public String addree;
	
	public Person(String name,int age,String addree) {
		this.name=name;
		this.age=age;
		this.addree=addree;
	}
	
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + ", addree=" + addree + "]";
	}
}

2)Constructor 创建对象

常用方法: 
	newInstance(Object... para) 
		调用这个构造方法, 把对应的对象创建出来 
		参数: 是一个Object类型可变参数, 传递的参数顺序 必须匹配构造方法中形式参数列表的顺 序! 
	setAccessible(boolean flag) 
		如果flag为true 则表示忽略访问权限检查 !(可以访问任何权限的方法)

3.4 Field

getFiled - 根据名称获取公有的(public)类成员。
getDeclaredField - 根据名称获取已声明的类成员。但不能得到其父类的类成员。
getFields - 获取所有公有的(public)类成员。
getDeclaredFields - 获取所有已声明的类成员。
package reflect_01;

//person类
public class Person {
	private String name;
	int age;	
	public String addree;
	
	public Person() {		
	}
	
	Person(String name,int age){
		this.name=name;
		this.age=age;
	}
	
	private Person(String name){
		this.name=name;
	}

	public Person(String name,int age,String addree) {
		this.name=name;
		this.age=age;
		this.addree=addree;
	}
	
	public void show() {
		System.out.println("show111");
	}
//	无参构造
	public void method(String s) {
		System.out.println("method"+s);
	}
//	带参带返回值
	public String getString(String s,int i) {
		return s+"---"+i;
	}
	
	private void functon() {
		System.out.println("functon");
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + ", addree=" + addree + "]";
	}
	
package reflect_01;

public class ReflectDome5 {
	public static void main(String[] args) throws Exception {
		Class c=Class.forName("reflect_01.Person");
		
		Constructor con=c.getDeclaredConstructor();
		Object obj=con.newInstance();
		
		//获取所有成员变量
		Field[] fields=c.getDeclaredFields();
		for(Field field:fields) {
			System.out.println(field);
		}
		//输出结果
		//private java.lang.String reflect_01.Person.name
		//int reflect_01.Person.age
		//public java.lang.String reflect_01.Person.addree
		
		//获取address并对其赋值
		Field addressField=c.getField("addree");
		
//		将指定对象变量上field,设值
		addressField.set(obj, "北京");// 该obj对象的addressfield字段设置值为北京
		System.out.println(obj);
		
		//对name赋值 ,私有
		Field nameField=c.getDeclaredField("name");
		//暴力访问,私有就可以访问,忽略权限检查
		nameField.setAccessible(true);
		//赋值
		nameField.set(obj, "lh");
		System.out.println(obj);
		
		//对age赋值
		Field ageField=c.getDeclaredField("age");
		ageField.setAccessible(true);
		ageField.set(obj, 18);
		System.out.println(obj);
	}
	//输出结果
	//Person [name=null, age=0, addree=北京]
	//Person [name=lh, age=0, addree=北京]
	//Person [name=lh, age=18, addree=北京]
}
	
}

3.5 Method

  1. getMethod(String methodName , class… clss)
    根据参数列表的类型和方法名, 得到一个方法(public修饰的)
  2. getMethods();
    得到一个类的所有方法 (public修饰的)
  3. getDeclaredMethod(String methodName , class… clss)
    根据参数列表的类型和方法名, 得到一个方法(除继承以外所有的:包含私有, 共有, 保护, 默认)
  4. getDeclaredMethods();
    得到一个类的所有方法 (除继承以外所有的:包含私有, 共有, 保护, 默认)
public class ReflectDome6 {
	public static void main(String[] args) throws Exception {
		Class c=Class.forName("reflect_01.Person");
		//获取类的构造方法
		Constructor con=c.getConstructor();
		//创建了对象
		Object obj=con.newInstance();
		
		//获取单个方法:show
		Method m1=c.getMethod("show");
		//参数1:哪个对象要执行  参数2:可变参数
//		public Object invoke(Object obj,Object...args)
		m1.invoke(obj);
		
		//获取类的方法
		Method m3=c.getMethod("method",String.class);
		m3.invoke(obj, "hello");
		
		Method m4=c.getMethod("getString",String.class,int.class);
		String s=(String)m4.invoke(obj, "hello",100);
		System.out.println(s);//字符串
		
		Method m5=c.getDeclaredMethod("functon");//获取所有的私有方法
		m5.setAccessible(true);//暴力破解
		m5.invoke(obj);
		
	}

}

3.6 Constructor

getConstructor - 返回类的特定 public 构造方法。参数为方法参数对应 Class 的对象。
getDeclaredConstructor - 返回类的特定构造方法。参数为方法参数对应 Class 的对象。
getConstructors - 返回类的所有 public 构造方法。
getDeclaredConstructors - 返回类的所有构造方法。
在这里插入代码片

3.6 破解访问权限限制

可以使用 Constructor.setAccessible(true) 来绕开 Java 语言的访问权限限制。

public static void main(String[] args) throws Exception {
		//获取字节码文件对象
		Class c=Class.forName("reflect_01.Person");
		
		//获取私有构造方法
//		public Constructor[] getDelaredConstructor : 返回所有构造方法
		Constructor con=c.getDeclaredConstructor(String.class); //所有权限的构造方法

		con.setAccessible(true);// 值为true,表示反射的对象在使用时应该取消Java语言访问检查

		
		Object obj=con.newInstance("lh");
		System.out.println(obj);
		
	}

最后,如果有问题,希望指正,一起进步。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值