Java反射
Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。而这也是Java被视为动态(或准动态,为啥要说是准动态,因为一般而言的动态语言定义是程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。)语言的一个关键性质。
对于一个正常的封装类的代码示例如下:
/**
* @author zane
* @date 2023-01-13-9:20
*/
public class Person {
private String name;
public int 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 Person(){
}
public Person(String name, int age){
this.name = name;
this.age = age;
}
private Person(String name){
this.name = name;
}
public void show(){
System.out.println("Hello, human being, I'm very sad.");
}
private String showNation(String nation){
System.out.println("I'm come from " + nation);
return nation;
}
@Override
public String toString(){
return "name: " + name + " ,age: " + age;
}
}
有私有属性,私有方法,私有构造器。通过正常的类去调属性方法代码如下:
//反射之前,对于Person的操作
@Test
public void test0(){
//1.创建Peroson类
Person p1 = new Person("Tom",12);
//2.通过对象,调用其内部的属性、方法
p1.age = 10;
System.out.println(p1.toString());
p1.show();
}
//在Person类外部,是不能通过Person类的对象调用其内部私有结构
//比如,name,showNation()以及私有构造器
通过反射去调用类中的属性和方法如下:
//反射之后,对于Person的操作
@Test
public void test1() throws Exception{
Class clazz = Person.class;
//1.通过反射,创建Person类的对象
Constructor cons = clazz.getConstructor(String.class, int.class);
Object obj = cons.newInstance("Tom", 12);
Person p1 = (Person) obj;
System.out.println(p1.toString());//name: Tom ,age: 12
//2.通过反射,调用对象指定的属性、方法
Field age = clazz.getDeclaredField("age");
age.set(p1,10);
System.out.println(p1.toString());//name: Tom ,age: 10
//3.调用方法
Method show = clazz.getDeclaredMethod("show");
show.invoke(p1);//Hello, human being.
//通过反射,可以调用Person类的私有结构,比如:私有构造器、方法、属性
Constructor cons1 = clazz.getDeclaredConstructor(String.class);
cons1.setAccessible(true);
Person p2 = (Person) cons1.newInstance("Zane");
System.out.println(p2);//name: Zane ,age: 0
//调用私有属性
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(p2,"HANHAN");
System.out.println(p2);//name: HANHAN ,age: 0
//调用私有方法
Method showNation = clazz.getDeclaredMethod("showNation", String.class);
showNation.setAccessible(true);
String nation = (String) showNation.invoke(p2,"China");//I'm come from China
System.out.println(nation);//China
}
/*
1.通过直接new的方式或反射的方式都可以调用公共的结构,开发中到底用那个
建议:直接new的方式,静态的时候
反射的特征:动态性
2.反射和面向对象的封装性是不是矛盾
不矛盾,封装性是建议你怎么调方法的问题,反射是解决能不能调的问题
*/
灵活使用反射能让我们代码更加灵活,这里比如JDBC原生代码注册驱动,hibernate 的实体类,Spring 的 AOP等等都有反射的实现。但是凡事都有两面性,反射也会消耗系统的性能,增加复杂性等。
Class加载的四种方式
关于java.Lang.class类的理解
1.类的加载过程:
程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class),接着我们使用java.exe 命令对某个字节码文
件进行解释运行。相当于将某个字节码文件加载到内存中,此过程就称为类的加载。加载到内存中的类,我们就称
为运行时类,此运行时类,就作为Class的一个实例。
2.换句话说,Class的实例就对应着一个运行时类。
3.加载到内存中的运行时类,会缓存一定的时间,在此时间之内,我们可以通过不同的方式来获取此运行时类。
代码示例:
@Test
public void test2() throws ClassNotFoundException{
//1.调用运行时类的属性 .class
Class clazz0 = Person.class;
System.out.println(clazz0);//class Person
//2.通过运行时类的对象,调用getClass()
Person p1 = new Person();
Class clazz1 = p1.getClass();
System.out.println(clazz1);//class Person
//3.调用Class的静态方法:forName(String classPath)开发中用的较多
Class clazz2 = Class.forName("Person");
System.out.println(clazz2);//class Person
System.out.println(clazz0 == clazz1);//true
System.out.println(clazz0 == clazz2);//true
System.out.println(clazz2 == clazz1);//true
//4.使用类的加载器:ClassLoader
ClassLoader classloader = ReflectionTest.class.getClassLoader();
Class clazz3 = classloader.loadClass("Person");
System.out.println(clazz3);//class Person
System.out.println(clazz0 == clazz3);//true
}