java反射机制可以完成:
1.在运行时判断任意一个对象所属的类
2.在运行时构造任意一个类的对象
3.在运行时得到任意一个类所具有的成员变量和方法
4.在运行时调用任意一个对象的成员变量和方法
5.生成动态代理
package ll;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
public class example
{
public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//根据配置文件re.properties指定信息,创建Cat对象并调用方法hi
//1.使用Properties类可以读写配置文件
Properties properties = new Properties();
properties.load(new FileInputStream("src\\re.properties"));
String classfullpath = properties.get("classfullpath").toString();
String method = properties.get("method").toString();
System.out.println(classfullpath);
System.out.println(method);
//使用反射机制解决
//(1)加载类,返回一个Class类型的对象
Class cls = Class.forName(classfullpath);
//(2)通过cls得到加载的类的对象实例
Object o = cls.newInstance();
System.out.println(o.getClass());
//(3)通过cls得到你加载的类的method方法
//即:在反射机制中,可以把方法视为对象(万物皆对象)
Method method1 = cls.getMethod(method);
//(4)通过method1调用方法:即通过方法对象来1调用方法
method1.invoke(o);
}
}
package ll;
public class Cat {
private String name="tom";
public void hi(){
System.out.println("hi"+name);
}
}
//配置文件src:\\re.properties
classfullpath=ll.Cat
method=hi
//这样的需求在学习框架时特别多,即通过外部文件配置,在不修改源码情况下来控制程序,
也符合设计模式的ocp原则(开闭原则:不修改源码,扩容功能)
一、反射机制
1.反射允许程序在执行时借助于Reflection API获取任何类的内部信息(成员方法、构造器、成员变量等),并能操作对象的属性和方法,反射机制在操作模式和框架底层都有广泛应用。
2.加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整的结构信息。通过这个对象得到类的结构,这个对象就像一面镜子,透过这个镜子看到类的结构,形象的称为反射。
package ll;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
public class example
{
public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
//根据配置文件re.properties指定信息,创建Cat对象并调用方法hi
//1.使用Properties类可以读写配置文件
Properties properties = new Properties();
properties.load(new FileInputStream("src\\re.properties"));
String classfullpath = properties.get("classfullpath").toString();
String method = properties.get("method").toString();
System.out.println(classfullpath);
System.out.println(method);
//使用反射机制解决
//(1)加载类,返回一个Class类型的对象
Class cls = Class.forName(classfullpath);
//(2)通过cls得到加载的类的对象实例
Object o = cls.newInstance();
System.out.println(o.getClass());
//(3)通过cls得到你加载的类的method方法
//即:在反射机制中,可以把方法视为对象(万物皆对象)
Method method1 = cls.getMethod(method);
//(4)通过method1调用方法:即通过方法对象来1调用方法
method1.invoke(o);
//得到name字段
//getField不能得到私有属性
Field nameField = cls.getField("age");
System.out.println(nameField.get(o));
//Constructor
Constructor constructor = cls.getConstructor();//()中可以指定构造器参数类型,返回无参构造器
System.out.println(constructor);
Constructor constructor2 =cls.getConstructor(String.class);//传入的String.class就是String类的Class对象
System.out.println(constructor2);
}
}
3.反射的优点和缺点:
1)优点:可以动态的创建和使用对象(也是底层框架的核心),没有反射机制,框架底层就失去技术支撑。
2)缺点:使用反射基本是解释执行,对执行速度有影响。
反射调用优化,关闭访问检查
1)Method和Field、Constructor对象都有setAccessible()方法
2)setAccessible()的作用是启动和禁用安全访问检查开关
3)参数值为true表示反射的对象在使用时取消访问检查,提高反射的效率。参数值为false则表示反射的对象进行访问检查。
二、Class类
1.基本介绍
1)Class也是类,即继承Object类。
2)Class对象不是new出来的,而是系统创建的。
3)对于某个类的对象,在内存中只有一个,因为类只加载一次。
4)Class对象是存放在堆的。
5)每个类的实例都会记得自己是由哪个Class实例所生成。
6)类的字节码二进制数据是存放在方法区的,有的地方称为类的元数据。
String str="com.Cat";
//获取到Class类对象,表示不确定的java类型
Class<?> clazz=Class.forName(str);
System.out.println(clazz);//显示该clazz对象是哪个类的class对象
Object o=clazz.newInstance();//通过反射创建对象
Field field=clazz.getField("name");//通过反射获取属性
field(obj,"tom");//通过字段对象赋值
2.获取Class类对象
1)前提:已知一个类的全类名,且该类在类路径下,可通过class类静态方法forName()获取,可能抛出ClassNotFoundException,实例:Class cls1=Class.forName("java.lang.Cat");
应用场景:多用于配置文件,读取类全路径,加载类。
2)前提:若已知具体的类,通过类的Class获取,该方式最为安全可靠,程序性能最高,实例:Class cls2=Cat.class;
应用场景:多用于参数传递,比如通过反射得到对应的构造器对象。
3)前提:已知某个类的实例,通过该实例的getClass()方法获取Class对象。实例:Class calzz=对象.getClass();
应用场景:通过创建好的的对象,获取Class对象。
4)其他方式
ClassLoader cl=对象.getClassLoader();
Class clazz4=cl.loacClass("类的全类名");
5)基本数据类型按如下方式的到Class对象:
Class cls=基本数据类型.class;
6)基本数据类型对应的包装类,可以通过TYPE得到Class类对象
Class cls=包装类.TYPE;
如下类型有Class对象
1.外部类,成员内部类,静态内部类,局部内部类,匿名内部类
2.interface:接口
3.数组
4.enum
5.annotation:注解
6.基本数据类型
7.void
三、类加载
1.基本说明
反射机制是java实现动态语言的关键,也就是通过反射实现类动态的加载。
1)静态加载:编译时加载相关的类,如果没有则报错,依赖性强。
2)动态加载:运行时加载需要的类,如果运行时不需要该类,则不报错,降低了依赖性。
2.类加载时机
1)当创建对象时,new//静态加载
2)当子类加载时,父类也被加载
3)调用类中的静态成员时
4)通过反射//动态加载
3.类加载各阶段完成的任务
1)加载:将类的class文件读入内存,并为之创建一个java.lang.Class对象,这个过程由类的加载器完成。
2)链接:将类的二进制数据合并的到jre中。
3)初始化:jvm负责对类进行初始化,这里主要是指静态成员。
4.链接阶段解析:
1)虚拟机将常量池的符号引用替换为直接引用的过程。
2)初始化
- 到初始化阶段才真正开始执行类中定义的java程序代码,此阶段是执行<clinit>()方法的过程
- <clinit>()语句是由编译器按语句在源文件中出现的顺序,依次自动收集类中所有静态变量的赋值动作和静态代码块中的语句,并进行合并。
- 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确的加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行完毕。
java.lang.reflect.Field类
1.getModifiers:以int形式返回修饰符
默认修饰符是0,public是1,private是2,protected是4,static是8,final是16
2.getType:以Class形式返回类型
3.getName:返回属性名
java.lang.reflect.Method类
1.getModifiers:以int形式返回修饰符
默认修饰符是0,public是1,private是2,protected是4,static是8,final是16
2.getReturnType:以Class形式获取返回类型
3.getName:返回方法名
通过反射创建对象:
1.方式一:调用类中的public修饰的无参构造器
2.方式二:调用类中的指定构造器
3Class类相关方法
newInstance:调用类中的无参构造器,获取对应类的对象
getConstructor(Class...clazz):根据参数列表,获取对应的构造器对象
getDecalaredConstructor(Clazz..clazz):根据参数列表,获取对应的构造器对象
4.Constructor类相关方法
setAccessible:暴破
newInstance(Object...obj):调用构造器
package hhh;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ReflecCreateInstance {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//1.先获取到User类的Class对象
Class<?> userClass = Class.forName("hhh.User");
//2.通过public的无参构造器创建实例
Object o = userClass.newInstance();
System.out.println(o);
//3.通过public的有参构造器创建实例
//3.1先得到对应构造器
Constructor<?> constructor = userClass.getConstructor(String.class);
//3.2创建实例,并传入实参
Object hsp = constructor.newInstance("hsp");
System.out.println("hsp"+hsp);
//4.通过非public的无参构造器创建实例
Constructor<?> constructor1 = userClass.getDeclaredConstructor(int.class, String.class);
constructor1.setAccessible(true);//暴破,使用反射可以访问private构造器
Object user2 = constructor1.newInstance(100, "张");
}
}
class User{
private int age=12;
private String name="jjj";
public User(){
}
public User(String name){
this.name=name;
}
private User(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "User{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
访问属性:
1.根据属性名获取Field对象
Field f=clazz对象.getDeclaredField(属性名);
2.暴破:f.setAccessible(true);
3.访问
f.set(o值);
syso(f.get(o));
4.如果是静态属性,则set和get中的参数o可以写成null
import java.lang.reflect.Field;
//演示反射操作属性
public class ReflecAccessProperty {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
//1.得到Student类对应的Class对象
Class<?> stuClass = Class.forName("hhh.Student");
//2.创建对象
Object o = stuClass.newInstance();//o的运行类型就是Student
System.out.println(o.getClass());//Student
//3.使用反射得到age属性对象
Field age = stuClass.getField("age");
age.set(o,88);//通过反射来操作属性
System.out.println(age.get(o));//返回age属性的值
//4.使用反射操作name属性
Field name = stuClass.getDeclaredField("name");
//对name进行暴破,可以操作private属性
name.setAccessible(true);
name.set(o,"啦");//将o写成null也可以,因为静态属性属于所有对象
System.out.println(o);
System.out.println(name.get(o));
System.out.println(name.get(null));//获取属性值,要求name是静态的
}
}
class Student{
public int age;
private static String name;
public Student() {
}
@Override
public String toString() {
return "Student{" +
"age=" + age +"name="+name+
'}';
}
}
访问方法
- 根据方法名和参数列表获取Method方法对象:Method m=clazz.getDeclaredMethod(方法名,XX.class);
- 获取对象:Object o=clazz.newInstance();
- 暴破:m.setAccessible(true);
- 访问:Object returnValue=m.invoke(o,实参列表);
- 注意:如果是静态方法,则invoke的参数o,可以写成null