写在前面
后面两节关于泛型和注解的实在是不入脑,等后续提到相关内容了再补吧。
反射机制(Reflection)
反射概述
-
动态语言:在运行时可以改变某些自身结构
-
静态:结构不可变
反射机制允许程序在执行期间借助Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
优点:可以实现动态创建对象和编译,灵活性高
缺点:对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么,它会满足我们的一起,这类操作总是慢于直接执行相同的操作。
public class reflection {
//有可能会找不到类,需要抛出异常
public static void main(String[] args) throws ClassNotFoundException{
//获取类的class对象 如果有包名,需要在前面顺序加上
//如Class<?>c1 = Class.forName("com.baidu.www.user");
Class<?>c1 = Class.forName("user");
Class<?>c2 = Class.forName("user");
Class<?>c3 = Class.forName("user");
//一个类在内存中只有一个class对象
//一个类被加载后,类的整个结构都会被封装在Class对象中
//打印的哈希值相同,说明对象相同
System.out.println(c1.hashCode());
System.out.println(c2.hashCode());
System.out.println(c3.hashCode());
}
}
class user{
private int age;
private String name;
public user(int age, String name) {
this.age = age;
this.name = name;
}
public user() {
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
运行结果如下:
460141958
460141958
460141958
进程已结束,退出代码为 0
-
反射可以获取类的class对象
-
一个类在内存中只有一个class对象
-
一个类被加载后,类的整个结构都会被封装在Class对象中
在Object类在,定义了以下方法:
public final Class getClass();
由于是在Object类中,这个方法将被所有子类继承。
以上方法的返回值是一个Class类。这个类是java反射的源头。
实际上,反射就是通过一个类的实例化对象来求出类的名称。
Class类
public class reflection {
//有可能会找不到类,需要抛出异常
public static void main(String[] args) throws ClassNotFoundException{
user u1=new consumer();
System.out.println(u1.age);
//1.通过对象获得
Class c1 = u1.getClass();
System.out.println(c1.hashCode());
//2.forname
Class c2=Class.forName("consumer");
System.out.println(c2.hashCode());
//3.类名.class
Class c3 = consumer.class;
System.out.println(c3.hashCode());
//4.基本内置类型的包装类都有一个type属性
Class<Integer> c4=Integer.TYPE;
System.out.println(c4);
//获得父类类型
Class c5=c1.getSuperclass();
System.out.println(c5);
}
}
class user{
public int age=15;
public String name;
public user(int age, String name) {
this.age = age;
this.name = name;
}
public user() {
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class administrator extends user{
public administrator(){
this.age= 55;
}
}
class consumer extends user{
public consumer(){
this.age= 20;
}
}
所有类型的Class对象
import javax.xml.bind.Element;
import java.lang.annotation.ElementType;
public class reflectTest {
public static void main(String[] args) {
Class<Object> c1 = Object.class;
Class c2= Comparable.class;
Class c3= String[].class;
Class c4= String.class;
Class c5 = int [][].class;
Class c6 = Override.class;
Class c7= ElementType.class;
Class c8= Integer.class;
Class c9= void.class;
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
System.out.println(c9);
}
}
class java.lang.Object
interface java.lang.Comparable
class [Ljava.lang.String;
class java.lang.String
class [[I
interface java.lang.Override
class java.lang.annotation.ElementType
class java.lang.Integer
void
只要元素类型和维度一样,就是同一个Class对象。
Java的内存分析
测试静态代码块的执行顺序:
public class reflect02 {
public static void main(String[] args) {
A a= new A();
System.out.println(A.m);
}
}
class A{
static {
System.out.println("静态代码初始化");
m=300;
}
static int m = 100;
public A(){
System.out.println("无参构造初始化");
}
}
输出的顺序为:
静态代码初始化
无参构造初始化
100
可以看出,static部分的内容早在类的对象实例化之前就已经运行了。

整个类的初始化的内存变化如上图。
类的初始化顺序
主动引用---一定会发生类的初始化
package reflect;
//测试类什么时候会初始化
public class reflect03 {
public static void main(String[] args) {
System.out.println("main类加载完成");
}
}
class Father{
static int b = 2;
static {
System.out.println("父类加载完成");
}
}
class Son extends Father{
static {
System.out.println("子类加载完成");
m= 300;
}
static int m=100;
static final int M=1;
}
结果:
main类加载完成
package reflect;
//测试类什么时候会初始化
public class reflect03 {
public static void main(String[] args) {
System.out.println("main类加载完成");
//主动引用
Son s = new Son();
}
}
class Father{
static int b = 2;
static {
System.out.println("父类加载完成");
}
}
class Son extends Father{
static {
System.out.println("子类加载完成");
m= 300;
}
static int m=100;
static final int M=1;
}
main类加载完成
父类加载完成
子类加载完成
此时可以看出,当主动引用子类时,即使Main函数中未进行父类的初始化,在子类初始化的过程中也会先对父类进行初始化。
package reflect;
//测试类什么时候会初始化
public class reflect03 {
public static void main(String[] args) throws ClassNotFoundException{
System.out.println("main类加载完成");
//反射产生主动引用的情况
Class<?> c1 = Class.forName("reflect.Son");
}
}
class Father{
static int b = 2;
static {
System.out.println("父类加载完成");
}
}
class Son extends Father{
static {
System.out.println("子类加载完成");
m= 300;
}
static int m=100;
static final int M=1;
}
main类加载完成
父类加载完成
子类加载完成
可以看出,当采用反射创建类的Class对象时,父类和子类也发生了初始化
被动引用---不会发生类的初始化
-
访问静态域时,只有真正声明这个域的类才会被初始化。
当子类访问父类的static变量时,子类不会被初始化。
因为静态变量只属于定义它的类,即使子类继承了,并且可以访问,但访问的是同一个内存。
-
通过数组定义类引用,不会触发此类的初始化
-
引用常量不会触发此类的初始化
引用常量是指final过的引用对象,被final修事故后的引用变量无法再指向别的对象,但它指向的对象内部内容仍然是可变的。
(p.s.:真正的常量是用static final修饰过的变量)
package reflect;
//测试类什么时候会初始化
public class reflect03 {
public static void main(String[] args) throws ClassNotFoundException{
System.out.println("main类加载完成");
System.out.println(Son.b);
}
}
class Father{
static int b = 2;
static {
System.out.println("父类加载完成");
}
}
class Son extends Father{
static {
System.out.println("子类加载完成");
m= 300;
}
static int m=100;
static final int M=1;
}
main类加载完成
父类加载完成
2
package reflect;
//测试类什么时候会初始化
public class reflect03 {
public static void main(String[] args) throws ClassNotFoundException{
System.out.println("main类加载完成");
Son[] a= new Son[5];
}
}
class Father{
static int b = 2;
static {
System.out.println("父类加载完成");
}
}
class Son extends Father{
static {
System.out.println("子类加载完成");
m= 300;
}
static int m=100;
static final int M=1;
}
main类加载完成
可以看出,当用数组形式新建引用对象的时候,父类和子类都未进行初始化。
package reflect;
//测试类什么时候会初始化
public class reflect03 {
public static void main(String[] args) throws ClassNotFoundException{
System.out.println("main类加载完成");
System.out.println(Son.M);
}
}
class Father{
static int b = 2;
static {
System.out.println("父类加载完成");
}
}
class Son extends Father{
static {
System.out.println("子类加载完成");
m= 300;
}
static int m=100;
static final int M=1;
}
main类加载完成
1
由于常量在链接过程中就已经被初始化,存入调用类的常量池中,因此无需初始化即可调用。
类加载器
类加载:把class文件字节码加载到内存,将数据转化为方法区的运行时数据结构,在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问接口。
类缓存:标准的Javase类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载一段时间。JVM垃圾回收机制可以回收这些Class对象。
加载器的分类:
- 引导类(负责核心库,无法直接获取)
- 扩展类 jre/lib/ext
- 系统类(最常用)java -classpath
package reflect;
public class reflect04 {
public static void main(String[] args) throws ClassNotFoundException {
//获取系统类的加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
//获得系统类加载器的父类加载器:扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);
//获得扩展类加载器的父类加载器:根加载器(C/C++)
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);
//测试当前类是哪个加载器加载的
ClassLoader classLoader1 = Class.forName("reflect.reflect04").getClassLoader();
System.out.println(classLoader1);
//测试JDK内置的类是谁加载的
ClassLoader classLoader = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader);
//查看加载器可以应用于哪些路径
System.out.println(System.getProperty("java.class.path"));
}
}
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@1b6d3586
null
sun.misc.Launcher$AppClassLoader@18b4aac2
null
D:\work\java\jre\lib\charsets.jar;D:\work\java\jre\lib\deploy.jar;D:\work\java\jre\lib\ext\access-bridge-64.jar;D:\work\java\jre\lib\ext\cldrdata.jar;D:\work\java\jre\lib\ext\dnsns.jar;D:\work\java\jre\lib\ext\jaccess.jar;D:\work\java\jre\lib\ext\jfxrt.jar;D:\work\java\jre\lib\ext\localedata.jar;D:\work\java\jre\lib\ext\nashorn.jar;D:\work\java\jre\lib\ext\sunec.jar;D:\work\java\jre\lib\ext\sunjce_provider.jar;D:\work\java\jre\lib\ext\sunmscapi.jar;D:\work\java\jre\lib\ext\sunpkcs11.jar;D:\work\java\jre\lib\ext\zipfs.jar;D:\work\java\jre\lib\javaws.jar;D:\work\java\jre\lib\jce.jar;D:\work\java\jre\lib\jfr.jar;D:\work\java\jre\lib\jfxswt.jar;D:\work\java\jre\lib\jsse.jar;D:\work\java\jre\lib\management-agent.jar;D:\work\java\jre\lib\plugin.jar;D:\work\java\jre\lib\resources.jar;D:\work\java\jre\lib\rt.jar;D:\javawork\untitled\out\production\untitled;D:\apps\IntelliJ IDEA Community Edition 2024.2.1\lib\idea_rt.jar
由第三和第五行可以看出,扩展类加载器的父类,即根加载器,是无法直接获取的
通过反射获取类的信息
此部分对照代码即可
package reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class reflect05 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class<?> c1 = Class.forName("reflect.user");
//获取类名
System.out.println(c1.getName());
System.out.println(c1.getSimpleName());
//获取类的属性
Field[] fields =c1.getFields();//只能找到公共属性(public)
for (Field field : fields) {
System.out.println(field);
}
System.out.println("======================");
fields=c1.getDeclaredFields();//找到全部属性
for (Field field1 : fields) {
System.out.println(field1);
}
System.out.println("======================");
//获取指定属性的值
Field age = c1.getField("age");
System.out.println(age);
System.out.println("======================");
//获取类的方法
//会获取到本类及父类的全部public方法
Method[] methods=c1.getMethods();
for (Method method : methods) {
System.out.println("method:"+method);
}
//所有方法
methods = c1.getDeclaredMethods();
for (Method method : methods) {
System.out.println("declare method:"+method);
}
System.out.println("======================");
//获得指定方法
//重载
Method getName = c1.getMethod("getName", null);
Method setName = c1.getMethod("setName", String.class);
System.out.println(getName);
System.out.println(setName);
System.out.println("======================");
//获得构造器
Constructor<?>[] constructors = c1.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
constructors=c1.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println("declared:"+constructor);
}
System.out.println("======================");
//获得指定构造器 指定参数就可以
Constructor<?> constructor;
constructor = c1.getConstructor(int.class,String.class);
System.out.println(constructor);
}
}
reflect.user
user
public int reflect.user.age
public java.lang.String reflect.user.name
======================
public int reflect.user.age
public java.lang.String reflect.user.name
======================
public int reflect.user.age
======================
method:public java.lang.String reflect.user.getName()
method:public void reflect.user.setName(java.lang.String)
method:public int reflect.user.getAge()
method:public void reflect.user.setAge(int)
method:public final void java.lang.Object.wait() throws java.lang.InterruptedException
method:public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
method:public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
method:public boolean java.lang.Object.equals(java.lang.Object)
method:public java.lang.String java.lang.Object.toString()
method:public native int java.lang.Object.hashCode()
method:public final native java.lang.Class java.lang.Object.getClass()
method:public final native void java.lang.Object.notify()
method:public final native void java.lang.Object.notifyAll()
declare method:public java.lang.String reflect.user.getName()
declare method:public void reflect.user.setName(java.lang.String)
declare method:public int reflect.user.getAge()
declare method:public void reflect.user.setAge(int)
======================
public java.lang.String reflect.user.getName()
public void reflect.user.setName(java.lang.String)
======================
public reflect.user(int,java.lang.String)
public reflect.user()
declared:public reflect.user(int,java.lang.String)
declared:public reflect.user()
======================
public reflect.user(int,java.lang.String)
通过反射调用类的指定结构
package reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class reflect06 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
// 获得Class对象
Class c1 = Class.forName("reflect.user");
// 构造一个对象
user user = (user) c1.newInstance(); // 本质上调用了类的无参构造器
System.out.println(user);
// 通过构造器创建对象
Constructor constructor = c1.getDeclaredConstructor(int.class,String.class);
user user1 = (user) constructor.newInstance(1,"光荣时代");
System.out.println(user1);
// 通过反射调用普通方法
user user2 = (user) c1.newInstance();
// 通过反射获取一个方法
Method setName = c1.getDeclaredMethod("setName", String.class);
// invoke:激活
// (对象,"方法值")
setName.invoke(user2, "some");
System.out.println(user2.getName());
// 通过反射操作属性
user user3 = (user) c1.newInstance();
Field name = c1.getDeclaredField("name");
// 不能直接操作私有属性,我们需要关闭程序的安全检测,属性或方法的setAccessible(true)
// 设置安全检测
name.setAccessible(true);
name.set(user3, "some2");
System.out.println(user3.getName());
}
}
reflect.user@1b6d3586
reflect.user@4554617c
some
some2
创建对象
-
通过反射(Class.forName())创建Class对象,然后调用newInstance函数以及强制类型转换来新建一个类的对象。
-
调用getDeclaredConstructor(参数)来获取构造器,再调用 constructor.newInstance(参数)来新建对象。
调用方法
Class对象调用:getDeclaredMethod(参数),再用methodName.invoke(参数);激活并使用。
修改属性
Class对象调用getDeclaredField(参数)
对于私有属性,需要赋予权限:参数.setAccessible(true);
再运用参数.set(实例化对象,内容)
性能比较
package reflect;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class reflect07 {
public static void test01() {
user user = new user();
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
user.getName();
}
long endTime = System.currentTimeMillis();
System.out.println("普通方式执行10亿次:" + (endTime - startTime) + "ms");
}
// 反射方式调用
public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
user user = new user();
Class c1 = user.getClass();
Method getName;
getName = c1.getDeclaredMethod("getName", null);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user,null);
}
long endTime = System.currentTimeMillis();
System.out.println("反射方式执行10亿次:" + (endTime - startTime) + "ms");
}
// 反射方式调用,关闭检测
public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
user user = new user();
Class c1 = user.getClass();
Method getName = c1.getDeclaredMethod("getName", null);
getName.setAccessible(true);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user,null);
}
long endTime = System.currentTimeMillis();
System.out.println("反射方式执行10亿次,关闭检测:" + (endTime - startTime) + "ms");
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
test01();
test02();
test03();
}
}
普通方式执行10亿次:4ms
反射方式执行10亿次:1606ms
反射方式执行10亿次,关闭检测:968ms
浙公网安备 33010602011771号