java基础 之 反射机制(重要)

一、定义

  • JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
  • 反射就是把java类中的各种成分映射成一个个的Java对象

二、反射的具体实现

1、获取字节码文件对象(Class)的三种方式

为什么要获取字节码文件对象?

  • 要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象

A:Object类的getClass()方法

    	FJC fjc =new FJC();//为FJC类创建实例化对象。
		Class fjcClass1 =fjc.getClass();
		System.out.println(fjcClass1);

B:任何数据类型(包括基本数据类型)都有一个“静态”的class属性

  		Class fjcClass2 =FJC.class;
		System.out.println(fjcClass2);

C:通过Class类的静态方法:forName(String className)(常用)

try{
		//注意此字符串必须是真实路径,就是带包名的类路径
		Class fjcClass3 = Class.forName("aaaa.FJC");
		System.out.println(fjcClass3);
	}
	catch(ClassNotFoundException e){
		System.out.println(e);

D:比较三种方法是否相同实例

		System.out.println(fjcClass1==fjcClass2);
		System.out.println(fjcClass2==fjcClass3);
		System.out.println(fjcClass1==fjcClass3);
  • 运行结果为:
class aaaa.FJC
class aaaa.FJC
class aaaa.FJC
true
true
true
  • 分析
  • 一个类在 JVM 中只会有一个 Class 实例

2、通过反射获取构造方法并使用

import java.lang.reflect.Constructor;

public class Student {
	//默认的构造方法
	Student(String Str){
		System.out.println("这是默认的构造方法"+Str);
	}
	//无参数的构造方法
	public Student(){
		System.out.println("这是无参数的构造方法");
	}
	//带一个参数的构造方法
	public Student(int age){
		System.out.println("这是带一个参数的构造方法"+age);
	}
	//带多个参数的构造方法
	public Student(int age,String name){
		System.out.println("这是带多个参数的构造方法"+age+name);
	}
	protected Student(boolean n){
		System.out.println("受保护的构造方法"+n);
	}
	private Student(char name){
		System.out.println("私有的构造方法"+name);
	}
	public static void main(String[] args) throws Exception{
		//1、加载Class对象
		Class studentClass = Class.forName("aaaa.Student");
		
		//2、获取所有公有构造方法
		System.out.println("获取所有公有构造方法:");
		Constructor[] conArray =studentClass.getConstructors();
		for(Constructor c : conArray){
			System.out.println(c);
		}
		
		//3、获取所有的构造方法
		System.out.println("获取所有的构造方法:");
		conArray=studentClass.getDeclaredConstructors();
		for(Constructor c:conArray){
			System.out.println(c);
		}
		
		//4、获取公有、无参数的构造方法
		System.out.println("获取公有、无参数的构造方法:");
		Constructor con = studentClass.getConstructor();
		System.out.println("con="+con);
		//调用构造方法
		Object obj =con.newInstance();
		
		//5、获取私有构造方法
		System.out.println("获取私有构造函数:");
		con=studentClass.getDeclaredConstructor(char.class);
		System.out.println(con);
		//调用构造方法
		con.setAccessible(true);
		obj=con.newInstance('f');
	}
}
  • 运行结果为:
获取所有公有构造方法:
public aaaa.Student(int,java.lang.String)
public aaaa.Student()
public aaaa.Student(int)
获取所有的构造方法:
private aaaa.Student(char)
protected aaaa.Student(boolean)
public aaaa.Student(int,java.lang.String)
aaaa.Student(java.lang.String)
public aaaa.Student()
public aaaa.Student(int)
获取公有、无参数的构造方法:
con=public aaaa.Student()
这是无参数的构造方法
获取私有构造函数:
private aaaa.Student(char)
私有的构造方法f
  • 分析
  • 获取公共构造器:getConstructors()
  • 获取所有构造器:getDeclaredConstructors()
  • 获取该类对象:newInstance()
  • 调用构造方法:Constructor–>newInstance(Object… initargs)。 其中,initargs就是这个对象的构造函数的参数。

3、获取成员变量并调用

import java.lang.reflect.Field;

public class Student {
	public Student(){
	}
	public String name;
	protected int age;
	char sex;
	private String phoneNum;
	
	@Override
	public String toString(){
		return"Student[name="+name+",age="+age+",sex="+sex+",phoneNum="+phoneNum+"]";
	}

	public static void main(String[] args) throws Exception{
		//1、获取Class对象
		Class stuClass = Class.forName("aaaa.Student");
		
		//2、获取字段
		//获取所有公有字段
		System.out.println("获取所有公有字段:");
		Field[] fieldArray =stuClass.getFields();
		for(Field f:fieldArray){
			System.out.println(f);
		}
		
		//3、获取所有字段
		System.out.println("获取所有字段:");
		fieldArray =stuClass.getDeclaredFields();
		for(Field f:fieldArray){
			System.out.println(f);
		}
		
		//4、获取公有字段并调用
		 Field f = stuClass.getField("name");
		 System.out.println(f);
		 //获取一个对象
		 Object obj=stuClass.getConstructor().newInstance();
		 Student stu = new Student();
		 //为student对象中的name属性赋值
		 f.set(obj, "fjc");
		 //验证
		 Student stu10 =(Student)obj;
		 System.out.println("验证姓名:"+stu10.name);
		 
		 //5、获取私有字段并调用
		 f=stuClass.getDeclaredField("phoneNum");
		 System.out.println(f);
		 //解除私有限制
		 f.setAccessible(true);
		 f.set(obj, "1111111");
		 System.out.println("验证电话:"+stu10.phoneNum);	 
	}
}

运行结果为:

获取所有公有字段:
public java.lang.String aaaa.Student.name
获取所有字段:
public java.lang.String aaaa.Student.name
protected int aaaa.Student.age
char aaaa.Student.sex
private java.lang.String aaaa.Student.phoneNum
public java.lang.String aaaa.Student.name
验证姓名:fjc
private java.lang.String aaaa.Student.phoneNum
验证电话:1111111
  • 分析
  • 获取类公共类型的所有属性:getFields()
  • 获取类的所有属性:getDeclaredFields()
  • 获取类公共类型的指定属性:getField(String name)
  • 获取类全部类型的指定属性:getDeclaredField(String name)

4、获取成员方法并调用

import java.lang.reflect.Method;

public class Student {
	public void show1(String s){
		System.out.println("调用了:公有的,有String参数的show1():s="+s);
	}
	protected void show2(){
		System.out.println("调用了:公有的,无参数的show2()");
	}
	void show3(){
		System.out.println("调用了:默认的,无参数的show3()");
	}
	private String show4(int age){
		System.out.println("调用了:私有的,并返回String,int参数的show3()");
		return "abcd";
	}
	public static void main(String[] args) throws Exception{
		//1、获取Class对象
		Class stuClass = Class.forName("aaaa.Student");
		
		//2、获取字段
		//获取所有公有方法
		System.out.println("获取所有公有方法:");
		Method[] methodArray =stuClass.getMethods();
		for(Method m:methodArray){
			System.out.println(m);
		}
		
		//3、获取所有方法
		System.out.println("获取所有方法:");
		methodArray =stuClass.getDeclaredMethods();
		for(Method m:methodArray){
			System.out.println(m);
		}
		
		//4、获取公有的show1()并调用
		 Method m = stuClass.getMethod("show1",String.class);
		 System.out.println(m);
		 //获取一个对象
		 Object obj=stuClass.getConstructor().newInstance();
		 m.invoke(obj,"fjc");
		 
		 //5、获取私有的show4()并调用
		 m=stuClass.getDeclaredMethod("show4",int.class);
		 System.out.println(m);
		 //解除私有限定,暴力访问。
		 m.setAccessible(true);
		 Object result =m.invoke(obj,20);
		 System.out.println("返回值:"+result);	 
	}
}
  • 运行结果为:
获取所有公有方法:
public static void aaaa.Student.main(java.lang.String[]) throws java.lang.Exception
public void aaaa.Student.show1(java.lang.String)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
获取所有方法:
public static void aaaa.Student.main(java.lang.String[]) throws java.lang.Exception
public void aaaa.Student.show1(java.lang.String)
void aaaa.Student.show3()
private java.lang.String aaaa.Student.show4(int)
protected void aaaa.Student.show2()
public void aaaa.Student.show1(java.lang.String)
调用了:公有的,有String参数的show1():s=fjc
private java.lang.String aaaa.Student.show4(int)
调用了:私有的,并返回String,int参数的show3()
返回值:abcd
  • 分析
  • 获取类公共类型的方法:getMethods()
  • 获取类的所有方法:getDeclaredMethods()
  • 获得类的特定公共类型方法: getMethod(String name, Class[] parameterTypes)
  • 获取内部类:getDeclaredClasses()
  • 获取外部类:getDeclaringClass()
  • 获取修饰符:getModifiers()
  • 获取所在包:getPackage()
  • 获取所实现的接口:getInterfaces()
  • m = stuClass.getDeclaredMethod(“show4”, int.class);//调用指定方法(所有包括私有的),需要传入两个参数,第一个是调用的方法名称,第二个是方法的形参类型,切记是类型。

三、反射的作用

  • 在运行时创建对象。
  • 获取任意一个类所具有的成员变量和方法。
  • 判断一个对象所属的类。
  • 调用一个对象的任意方法。

四、ClassLoader

1、定义

  • ClassLoader是根据一个指定的类的全限定名,找到对应的Class字节码文件,然后加载它转化成一个java.lang.Class类的一个实例。

2、分类

启动类加载器(Bootstrap ClassLoader)——加载java的核心库:

  • 这个类加载器负责将\lib目录下的类库加载到虚拟机内存中,用来加载java的核心库,此类加载器并不继承于java.lang.ClassLoader,不能被java程序直接调用,代码是使用C++编写的。是虚拟机自身的一部分。

扩展类加载器(Extendsion ClassLoader)——加载java的扩展库:

  • 这个类加载器负责加载\lib\ext目录下的类库,用来加载java的扩展库,开发者可以直接使用这个类加载器。

应用程序类加载器(Application ClassLoader)——加载java应用的类:

  • 这个类加载器负责加载用户类路径(CLASSPATH)下的类库,一般我们编写的java类都是由这个类加载器加载,这个类加载器是CLassLoader中的getSystemClassLoader()方法的返回值,所以也称为系统类加载器。一般情况下这就是系统默认的类加载器。

五、Class.forName()和ClassLoader的区别

1、作用

  • 这两种方式都可以将类加载到JVM中。
  • class加载到JVM中有三个步骤:
  • 装载:(loading)找到class对应的字节码文件。
  • 连接:(linking)将对应的字节码文件读入到JVM中。
  • 初始化:(initializing)对class做相应的初始化动作。

2、用法

  • Java中两种加载class到JVM中的方式

Class.forName(“className”);

  1. 其实这种方法调运的是:Class.forName(className, true,ClassLoader.getCallerClassLoader())方法。

  2. 参数一:className,需要加载的类的名称。
    参数二:true,是否对class进行初始化(需要initialize)。
    参数三:classLoader,对应的类加载器。

ClassLoader.laodClass(“className”);

  • 其实这种方法调运的是:ClassLoader.loadClass(name, false)方法。

  • 参数一:name,需要加载的类的名称
    参数二:false,这个类加载以后是否需要去连接(不需要linking)。

3、区别

  • Class.forname():是一个静态方法,最常用的是Class.forname(String className);根据传入的类的全限定名返回一个Class对象。该方法在将Class文件加载到内存的同时,会执行类的初始化
  • ClassLoader.loadClass():这是一个实例方法,需要一个ClassLoader对象来调用该方法,该方法将Class文件加载到内存时,并不会执行类的初始化,直到这个类第一次使用时才进行初始化。该方法因为需要得到一个ClassLoader对象,所以可以根据需要指定使用哪个类加载器。

【END】初次学习若有不正之处请见谅,欢迎留言批评指出。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值