1.什么是反射机制
反射就是通过获取到该类的字节码文件对象---->Class类对象,通过Class类对象获取该类里面的一些属性(成员变量)、构造方法、成员方法。
面试题:
如何获取类的字节码文件对象,并且有几种方式呢?
1)Object类中的getClass()方法,表示正在运行的那个类:Class类
2)数据类型的class属性 举例:String.class,Student.class
3)Class类中的特有方法:forName(String className):(重点,数据库加载驱动:Drivers)
public static Class<?> forName(String className):获取字节码文件对象
开发中常使用的方式,因为第三种里面的参数是一个字符串...,字符串的内容是一个类的全路径名称
class Demo{
private static Student s = new Student() ; //成员变量位置:实例字段
private String name ;
}
Person p = new Person() ;
2.通过反射获取构造方法并使用它
3.之前创建对象:
给对象的成员属性赋值
Person p = new Person("高圆圆",27,"户县")
System.out.println(p) ;
通过反射获取构造方法并且给成员属性赋值
4.通过反射获取私有的构造方法,并使用
Person p = new Preson("高圆圆") ;
System.out.println(p) ;
6.通过反射获取成员变量并使用
之前使用对象调用成员方法
Person p = new Person() ;
p.show() ;
3.写一个方法,
public void setProperty(Object obj, String propertyName, Object value){},此方法可将obj对象中名为propertyName的属性的值设置为value
4.
反射就是通过获取到该类的字节码文件对象---->Class类对象,通过Class类对象获取该类里面的一些属性(成员变量)、构造方法、成员方法。
面试题:
如何获取类的字节码文件对象,并且有几种方式呢?
1)Object类中的getClass()方法,表示正在运行的那个类:Class类
2)数据类型的class属性 举例:String.class,Student.class
3)Class类中的特有方法:forName(String className):(重点,数据库加载驱动:Drivers)
public static Class<?> forName(String className):获取字节码文件对象
开发中常使用的方式,因为第三种里面的参数是一个字符串...,字符串的内容是一个类的全路径名称
class Demo{
private static Student s = new Student() ; //成员变量位置:实例字段
private String name ;
}
Person p = new Person() ;
System.out.println(p) ;
public class Demo {
public static void main(String[] args) throws ClassNotFoundException {
//创建Person对象
Person p1 = new Person() ;
Class c1 = p1.getClass() ;//---->得到Person.class字节码文件对象
//创建对象
Person p2 = new Person() ;
Class c2 = p1.getClass() ;//------->Person.class字节码文件对象
System.out.println(p1==p2);//false
System.out.println(c1==c2);//true
System.out.println("--------------------");
//第二种方式:通过数据类型的Class属性
Class c3 = Person.class ;//---->加载并且获得person.class字节码文件对象
System.out.println(c3==c1);
System.out.println("----------------------");
//第三种方式获取类的字节码文件
//注意:参数不是一个类名
// Class c4 = Class.forName("Person") ;//java.lang.ClassNotFoundException
//参数需要类路径:类的全路径名称
Class c4 = Class.forName("org.westos.reflect_01.Person") ;
//class org.westos.reflect_01.Person
// System.out.println("c4:"+c4);
System.out.println(c4==c1);
}
}
public class Person {
//成员变量
private String name ;
int age ;
public String address ;
public Person(){}
private Person(String name) {
this.name = name;
}
Person(String name,int age){
this.name = name ;
this.age = age ;
}
public Person(String name,int age,String address){
this.name = name ;
this.age =age ;
this.address = address ;
}
//提供一些成员方法
public void show(){
System.out.println("show");
}
public void method(String s){
System.out.println("method"+s);
}
public String getString(String s, int i) {
return s + "---" + i;
}
private void function(){
System.out.println("function");
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", address=" + address
+ "]";
}
}
2.通过反射获取构造方法并使用它
public class ReflectDemo {
public static void main(String[] args) throws Exception {
//获取一个类中的构造方法,首先找到这个类的class文件对象,通过反射
//获取Person类的Class类对象
Class c = Class.forName("org.westos.reflect_01.Person") ;
//使用该Class类对象中的一些成员方法:
//public Constructor<?>[] getConstructors(),返回的是一个构造方法所在数组
// public Constructor<?>[] getDeclaredConstructors():获取的是当前字节码文件对象中所有的构造方法
//表示的类的所有公共构造方法
/**
* public org.westos.reflect_01.Person(java.lang.String,int,java.lang.String)
public org.westos.reflect_01.Person()
*/
// Constructor[] con = c.getConstructors() ;
//获取当前Pereson类中所有的构造方法
// Constructor[] con = c.getDeclaredConstructors() ;
//增强for
// for(Constructor constructor:con){
// System.out.println(constructor);
// }
//如何获取单个的构造方法:
//public Constructor<T> getConstructor(Class<?>... parameterTypes)
// 返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法
//参数表示:参数数据类型的字节码文件对象:获取指定构造方法里面有参数,需要写上数据类型的字节码文件对象
// 假设 :String.class
//没有学习的反射的时候
//Person p = new Person()
//System.out.println(p) ;
//获取构造器对象:Constructor
Constructor con = c.getConstructor() ;
// System.out.println(con);
//调用构造器对象里面的一个方法:newInstance()
//public T newInstance(Object... initargs):传递的是实际参数
//表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例
Object obj = con.newInstance() ; //-->通过反射--->Person.class---->getConstructor()
System.out.println(obj);
}
}
3.之前创建对象:
给对象的成员属性赋值
Person p = new Person("高圆圆",27,"户县")
System.out.println(p) ;
通过反射获取构造方法并且给成员属性赋值
public class ReflectDemo2 {
public static void main(String[] args) throws Exception {
//获取Person类的字节码文件对象
Class c = Class.forName("org.westos.reflect_01.Person") ;
//通过反射获取指定构造方法:getConstructor(Class ...parameterTyps) 参数:参数类型.class
Constructor con = c.getConstructor(String.class, int.class,
String.class);
//创建构造器的实例对象,来给他指定的字节码文件对象里面的成员变量赋值
Object obj = con.newInstance("高圆圆",27,"户县") ;//实际参数
System.out.println(obj);
}
}
4.通过反射获取私有的构造方法,并使用
Person p = new Preson("高圆圆") ;
System.out.println(p) ;
public class ReflectDemo3 {
public static void main(String[] args) throws Exception {
//1)获取Person类的字节码文件对象
Class c = Class.forName("org.westos.reflect_01.Person") ;
//2)如何获取私有的?
// public Constructor<T> getConstructor(Class<?>... parameterTypes)
//该方法获取当前指定的公共的构造方法
//public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
//表示获取类或者接口中的指定的构造方法
Constructor con = c.getDeclaredConstructor(String.class) ;
//public void setAccessible(boolean flag):在访问的时候取消java语言访问检查(强制性)
//将此对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查
//取消 Java 语言访问检查
con.setAccessible(true) ;//在去给构造创建实例对象之前就应该取消检查
//创建构造器的实例对象,可以去传递实际参数
Object obj = con.newInstance("高圆圆") ;
/**
* java.lang.IllegalAccessException: 非法的访问异常
* Class org.westos.reflect_02.ReflectDemo3 can not access a member of
* class org.westos.reflect_01.Person with modifiers "private"
* 私有的构造方法是不能直接访问的
*/
System.out.println(obj);
}
}
5.通过反射获取成员变量并使用
public class ReflectDemo {
public static void main(String[] args) throws Exception {
// 1)通过反射获取字节码文件对象
Class c = Class.forName("org.westos.reflect_01.Person") ;
//2)获取所有的公共的成员变量public Field[] getFields():所有的公共的可访问的字段,返回的是Field对象数组
//public Field[] getDeclaredFields():获取当前字节码文件对象中所有的公共的或者私有的字段
// Field[] fields = c.getFields() ;
// Field[] fields = c.getDeclaredFields() ;
// for(Field f :fields){
// //public java.lang.String org.westos.reflect_01.Person.address
// System.out.println(f);
// }
//反射之前:给一个类的成员变量赋值:公共的字段
// Person p = new Person() ;
//p.address = "北京" ;
//获取到这个Field对象之前,还获取构造器对象(通过无参构造创建Person类的实例)
Constructor con = c.getConstructor() ;
//创建构造器实例
Object obj = con.newInstance() ;//Person的实例对象
System.out.println(obj);
//反射:
//获取单个成员变量(Field类对象)
//给成员变量address赋值
//public Field getField(String name):获取公共的指定的字段 参数为当前成员变量名称"address"
Field addressFiled = c.getField("address") ;
//有了Field对象,给当前obj实例对象设置一个参数
//将指定对象变量上此 Field 对象表示的字段设置为指定的新值public void set(Object obj, Object value)
//给obj实例对象里面的成员变量设置一个实际参数---->value
addressFiled.set(obj, "北京") ;
System.out.println(obj);
System.out.println("-----------------------------");
//给name赋值并使用
//NoSuchFieldException :该成员变量是一个私有的
// Field nameField = c.getField("name") ;
//public Field getDeclaredField(String name):获取类或接口中已经声明的指定的字段
Field nameField = c.getDeclaredField("name") ;
//给name属性设置值
//java.lang.IllegalAccessException
nameField.setAccessible(true) ;//取消Java语言访问检查
nameField.set(obj, "高圆圆") ;
System.out.println(obj);
System.out.println("-------------------------------");
//给age字段赋值并使用
Field ageField = c.getDeclaredField("age") ;
//给ageFiled在当前实例对象obj设置一个参数值
//取消访问检查
ageField.setAccessible(true) ;//取消Java语言访问检查
ageField.set(obj, 27) ;
System.out.println(obj);
}
}
6.通过反射获取成员变量并使用
之前使用对象调用成员方法
Person p = new Person() ;
p.show() ;
public class ReflectDemo {
public static void main(String[] args) throws Exception {
//1)通过反射获取字节码文件对象并使用
Class c = Class.forName("org.westos.reflect_01.Person") ;
//获取成员方法(Method)
//public Method[] getMethods():获取当前该字节码文件对象(Person.class)中自己本身以及它父类中所有的公共成员方法
// Method[] methods = c.getMethods() ;
// public Method[] getDeclaredMethods():获取当前字节码文件对象本身所有的成员方法
// Method[] methods = c.getDeclaredMethods() ;
// for(Method m :methods){
// System.out.println(m);
// }
//获取构造器对象,通过构造器创建当前字节码文件的实例对象
Constructor con = c.getConstructor() ;
Object obj = con.newInstance() ; //Person对象
//获取单个成员方法
//public void show() {}
/**
* public Method getMethod(String name,Class<?>... parameterTypes):指定公共成员方法
* 参数1:表示方法名
* 参数2:该方法的参数类型的Class对象(数据类型的class属性) String.class
*/
Method m1 = c.getMethod("show") ;
// obj.m1() ;//错误的写法
/**
* public Object invoke(Object obj, Object... args)
* 参数1:表示当前针对哪个以实例对象进行方法的调用
* 参数2:当前调用该方法的时候里面传递的实际参数
*/
m1.invoke(obj) ;
System.out.println("-------------------------");
//public void method(String s){}
//调用Method方法
Method m2 = c.getMethod("method", String.class) ;
//调用
m2.invoke(obj, "word") ;
System.out.println("----------------------------");
/*
* public String getString(String s, int i) {
return s + "---" + i;
}
* */
//调用getString()方法
Method m3 = c.getMethod("getString", String.class,int.class) ;
//调用
// Object objString = m3.invoke(obj, "hello",100) ;
// System.out.println(objString);
String objString = (String) m3.invoke(obj, "hello",100) ; //向上转型
System.out.println(objString);
System.out.println("--------------------------");
//private void function(){
//System.out.println("function");
//}
//调用function()方法
//该方法是私有的,public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
//获取的是已经声明的指定的方法
//oSuchMethodException:
Method m4 = c.getDeclaredMethod("function") ;
//IllegalAccessException:
//调用
m4.setAccessible(true) ;//取消Java语言的访问检查
m4.invoke(obj) ;
}
}
反射的应用:
1.我给你ArrayList<Integer>的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢?
反射的应用:
1)在集合中应用
2)用来反射区读取配置文件来加载里面的内容(MySQL,Oracle等等,SQLServer,MangoDB)
public class ArrayListTest {
public static void main(String[] args) throws
NoSuchMethodException,
SecurityException, IllegalAccessException,
IllegalArgumentException,
InvocationTargetException {
//创建ArrayList对象
ArrayList<Integer> array = new ArrayList<Integer>() ;
//给集合中添加元素
// array.add(100) ;
// array.add("hello") ;
//利用反射类来给集合中添加字符串数据
//获取该ArrayList类的字节码文件对象
Class c = array.getClass() ;
//有了ArrayList.class字节码文件对象了
//已经有了构造器对象:array
//可以通过反射在获取公共的访问:add(E e)
Method m = c.getMethod("add",Object.class) ;
//调用用底层invoke(当前字节码文件对象的实例,实际参数)
m.invoke(array, "hello") ;
m.invoke(array, "world") ;
m.invoke(array, "java") ;
System.out.println(array);
}
}
public class Student {
public void love(){
System.out.println("学生爱学习,爱Java");
}
}
public class Teacher {
public void love(){
System.out.println("老师Java,爱高圆圆");
}
}
public class Worker {
public void love(){
System.out.println("工人爱生活,爱老婆");
}
}
public class Test {
public static void main(String[] args) throws Exception {
//反射之前的作用:
// Student s = new Student() ;
// s.love() ;
// Teacher t = new Teacher() ;
// t.love() ;
// Worker w = new Worker() ;
// w.love() ;
//使用反射的做法:
//文本文件是一种键值对象的形式...
//需要将文本文件中的内容加载属性集合类中(键值对都是String类型)
//创建一个属性集合类
Properties prop = new Properties() ;
//加载
FileReader fr = new FileReader("class.txt") ;
prop.load(fr) ;
//关闭资源
fr.close() ;
//通过键获取值,使用它的特有功能getProperty("String key")
String className = prop.getProperty("className") ;
String methodName = prop.getProperty("methodName") ;
//获取字节码文件对象
Class c = Class.forName(className) ;
//通过反射获取构造器对象,并且通过构造器对象创建该类的实例
Constructor con = c.getConstructor() ;
Object obj = con.newInstance() ;
//获取成员方法
Method m = c.getMethod(methodName) ;
//调用
m.invoke(obj) ;
}
}
3.写一个方法,
public void setProperty(Object obj, String propertyName, Object value){},此方法可将obj对象中名为propertyName的属性的值设置为value
public class ToolDemo {
public static void main(String[] args) throws NoSuchFieldException,
SecurityException,
IllegalArgumentException,
IllegalAccessException {
//创建Person类对象
Person p = new Person() ;
Tool t = new Tool() ;
//设置name属性
t.setProperty(p, "name", "高圆圆") ;
t.setProperty(p, "age", 27) ;
System.out.println(p);
Dog d = new Dog() ;
t.setProperty(d, "gender",'公' ) ;
t.setProperty(d, "price", 34.56F) ;
System.out.println(d);
}
}
public class Tool {
public void setProperty(Object obj, String propertyName, Object value) throws
NoSuchFieldException,
SecurityException,
IllegalArgumentException,
IllegalAccessException{
//获取类的字节码文件对象
Class c = obj.getClass() ;
//通过字节码文件对象获取Field对象
//为了防止可能后有一些私有的成员变量:getDeclaredField("字段名称")
Field field = c.getDeclaredField(propertyName) ;
//通过field对象给指定对象obj设置属性值
//防止出现 IllegalAccessException:设置Java语言取消访问检查
field.setAccessible(true) ;
field.set(obj, value) ;
}
}
4.
public class Test {
public static void main(String[] args) {
//创建用户操作的对象
UserDao ud = new UserDaoImpl() ;
ud.add() ;
ud.delete();
ud.update() ;
ud.serach() ;
System.out.println("------------------");
//给ud对象设置代理对象
//Proxy类中的方法创建动态代理类对象
//public static Object newProxyInstance
//(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
//InvocationHandler是代理对象需要实现的接口---->需要自定义一个类实现这个接口
//创建代理对象
MyInvocationHandler handler = new MyInvocationHandler(ud) ;
UserDao proxy = (UserDao) Proxy.newProxyInstance(ud.getClass().getClassLoader(),
ud.getClass().getInterfaces(),handler) ;
//参数1:需要类加载器:首先获取当前目标对象的字节码文件对象,在去调用getClassLoser()
//参数2:获取目标的字节码文件对象,调用方法得到给目标对象提供一组接口
proxy.add();
proxy.delete() ;
proxy.update() ;
proxy.serach() ;
//登录注册,增删改查 等等...
}
}
public interface UserDao {
//增
public abstract void add() ;
//删
public abstract void delete() ;
//update:修改
public abstract void update() ;
//查
public abstract void serach() ;
}
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("增加功能");
}
@Override
public void delete() {
System.out.println("删除功能");
}
@Override
public void update() {
System.out.println("修改功能");
}
@Override
public void serach() {
System.out.println("查询功能");
}
}
public class MyInvocationHandler implements InvocationHandler {
//目标对象(需要给哪一个对象设置这个代理呢?)
private Object target ;
//无参构造
public MyInvocationHandler(Object target){
this.target = target ;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//权限校验
System.out.println("权限校验");
Object result = method.invoke(target, args) ;
//日志记录
System.out.println("日志记录");
return result; //返回值就是代理对象
}
}