关于JAVA反射机制的学习
关于JAVA反射机制的学习
1、基本概念:反射机制(比较简单,因为只要会查帮助文档,就可以了。)
2.1、反射机制有什么用?
通过java语言中的反射机制可以操作字节码文件。
优点类似于黑客。(可以读和修改字节码文件。)
通过反射机制可以操作代码片段。(class文件。)
2.2、反射机制的相关类在哪个包下?
java.lang.reflect.*;
2.3、反射机制相关的重要的类有哪些?
java.lang.Class:代表整个字节码,代表一个类型,代表整个类。
java.lang.reflect.Method:代表字节码中的方法字节码。代表类中的方法。
java.lang.reflect.Constructor:代表字节码中的构造方法字节码。代表类中的构造方法
java.lang.reflect.Field:代表字节码中的属性字节码。代表类中的成员变量(静态变量+实例变量)。
java.lang.Class:
public class User{
// Field
int no;
// Constructor
public User(){
}
public User(int no){
this.no = no;
}
// Method
public void setNo(int no){
this.no = no;
}
public int getNo(){
return no;
}
}
3、关于JDK中自带的类加载器:(聊一聊,不需要掌握,知道当然最好!)
3.1、什么是类加载器?
专门负责加载类的命令/工具。
ClassLoader
3.2、JDK中自带了3个类加载器
启动类加载器:rt.jar
扩展类加载器:ext/*.jar
应用类加载器:classpath
3.3、假设有这样一段代码:
String s = "abc";
代码在开始执行之前,会将所需要类全部加载到JVM当中。
通过类加载器加载,看到以上代码类加载器会找String.class
文件,找到就加载,那么是怎么进行加载的呢?
首先通过“启动类加载器”加载。
注意:启动类加载器专门加载:C:\Program Files\Java\jdk1.8.0_101\jre\lib\rt.jar
rt.jar中都是JDK最核心的类库。
如果通过“启动类加载器”加载不到的时候,
会通过"扩展类加载器"加载。
注意:扩展类加载器专门加载:C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\*.jar
如果“扩展类加载器”没有加载到,那么
会通过“应用类加载器”加载。
注意:应用类加载器专门加载:classpath中的类。
3.4、java中为了保证类加载的安全,使用了双亲委派机制
优先从启动类加载器中加载,这个称为“父”
“父”无法加载到,再从扩展类加载器中加载,
这个称为“母”。双亲委派。如果都加载不到,
才会考虑从应用类加载器中加载。直到加载
到为止。
1、回顾反射机制
1.1、什么是反射机制?反射机制有什么用?
反射机制:可以操作字节码文件
作用:可以让程序更加灵活。
1.2、反射机制相关的类在哪个包下?
java.lang.reflect.*;
1.3、反射机制相关的主要的类?
java.lang.Class
java.lang.reflect.Method;
java.lang.reflect.Constructor;
java.lang.reflect.Field;
1.4、在java中获取Class的三种方式?
第一种:
Class c = Class.forName("完整类名");
第二种:
Class c = 对象.getClass();
第三种:
Class c = int.class;
Class c = String.class;
1 /** 2 * 反射 3 * 要操作一个类的字节码,首先要获取这个类的字节码,怎么获取java.lang.Class实例 4 * 三种方式 5 * @author yumu 6 * 7 */ 8 public class ReflectTest01 { 9 10 public static void main(String[] args) throws ClassNotFoundException { 11 /* 12 * 1.第一种 Class.forName() 13 * Class.forName(0 14 * 1.静态方法 15 * 2.方法的参数是一个字符串 16 * 3.字符串需要一个完整类名 17 * 4.完整类名必须带有包名 18 */ 19 Class c1=Class.forName("java.lang.String"); 20 Class c2=Class.forName("java.util.Date"); 21 Class c3=Class.forName("java.lang.Integer"); 22 Class c4=Class.forName("java.lang.System"); 23 24 //第二种 java中任何一个对象都有一个方法:getClass() 25 String s="abc"; 26 Class x=s.getClass();//x代表String.class字节码文件,x代表String类型 27 System.out.println(c1==x); //true 28 29 Date time=new Date(); 30 Class d=time.getClass(); 31 System.out.println(c2==d); //true 32 33 //第三种 java语言任何一种类型,包括基本数据类型,它都有.class属性 34 Class z=String.class; //z代表String类型 35 Class k=Date.class; //k代表Date类型 36 Class f=int.class; //f代表int类型 37 Class e=double.class; //e代表double类型 38 39 System.out.println(x==z); //true 40 System.out.println(k==d); 41 } 42 43 }
1.5、获取了Class之后,可以调用无参数构造方法来实例化对象
//c代表的就是日期Date类型
Class c = Class.forName("java.util.Date");
//实例化一个Date日期类型的对象
Object obj = c.newInstance();
一定要注意:
newInstance()底层调用的是该类型的无参数构造方法。
如果没有这个无参数构造方法会出现"实例化"异常。
验证反射机制的灵活性
1 /** 2 * 验证反射机制的灵活性 3 * java代码写一遍,再不改变java源代码的基础上,可以做到不同对象的实例化 4 * @author yumu 5 * 6 */ 7 public class ReflectTest03 { 8 9 public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException { 10 //通过IO流读取classinfo.properties文件 11 FileReader reader=new FileReader("classinfo.properties"); 12 //创建属性类对象Map 13 Properties pro=new Properties(); 14 //加载 15 pro.load(reader); 16 //关闭流 17 reader.close(); 18 19 //通过key获取value 20 //配置文件的改变会实例不同的对象 21 String className = pro.getProperty("className"); 22 //通过反射机制实例化对象 23 Class c=Class.forName(className); 24 Object obj=c.newInstance(); 25 System.out.println(obj); 26 27 } 28 29 }
1.6、如果你只想让一个类的“静态代码块”执行的话,你可以怎么做?
Class.forName("该类的类名");
这样类就加载,类加载的时候,静态代码块执行!!!!
在这里,对该方法的返回值不感兴趣,主要是为了使用“类加载”这个动作。
1.7、关于路径问题?
String path = Thread.currentThread().getContextClassLoader()
.getResource("写相对路径,但是这个相对路径从src出发开始找").getPath();
String path = Thread.currentThread().getContextClassLoader()
.getResource("com/bjpowernode/test.properties").getPath();
//必须保证src下有com目录,com目录下有bjpowernode目录。
//bjpowernode目录下有test.properties文件。
这种方式是为了获取一个文件的绝对路径。(通用方式,不会受到环境移植的影响。)
但是该文件要求放在类路径下,换句话说:也就是放到src下面。
src下是类的根路径。
直接以流的形式返回:
InputStream in = Thread.currentThread().getContextClassLoader()
.getResourceAsStream("com/bjpowernode/test.properties");
1 public class ReflectTest05 { 2 3 public static void main(String[] args) throws IOException { 4 //上面学的路径移植性太差 , 5 //接下来说一种通用的方式 6 //注意:以下方法的前提,这个文件必须在类路径下 7 //src是类的根路径 8 /* 9 * 解释: 10 * Thread.currentThread() 当前线程对象 11 * getContextClassLoader() 是线程对象方法,可以获取当前线程的类加载器对象 12 * getResource [获取资源] 这是类加载器对象的方法,当前线程的类加载器默认从当前类的根路径下加载资源 13 */ 14 //这是一个获取文件的绝对路径 15 //String path = Thread.currentThread().getContextClassLoader().getResource("classinfo.properties").getPath(); 16 //FileReader reader =new FileReader(path); 17 //System.out.println(path); 18 //这是一种直接以流的形式获取 19 InputStream reader =Thread.currentThread().getContextClassLoader().getResourceAsStream("classinfo.properties"); 20 21 Properties pro = new Properties(); 22 pro.load(reader); 23 reader.close(); 24 String className = pro.getProperty("className"); 25 System.out.println(className); 26 27 28 } 29 30 }
1.8、IO + Properties,怎么快速绑定属性资源文件?
//要求:第一这个文件必须在类路径下
//第二这个文件必须是以.properties结尾。
ResourceBundle bundle = ResourceBundle.getBundle("com/bjpowernode/test");
String value = bundle.getString(key);
1 /** 2 * java.util包下提供了一个资源绑定器,便于获取属性文件中的内容 3 * 使用以下方法,属性配置文件xxx.properties必须放到类路径下 4 * 并且,写路径的时候,路径下面的扩展名不写 5 * @author yumu 6 * 7 */ 8 public class ResourceBundleTest01 { 9 10 public static void main(String[] args) { 11 ResourceBundle bundle=ResourceBundle.getBundle("classinfo"); 12 String str = bundle.getString("className"); 13 System.out.println(str); 14 } 15 16 }
2、今日反射机制的重点内容
2.1、通过反射机制访问对象的某个属性。
1 /** 2 * 必须掌握: 3 * 怎么通过反射机制访问一个java对象的属性? 4 * 给属性赋值, 5 * 获取属性的值get 6 * @author yumu 7 * 8 */ 9 public class ReflectTest08 { 10 11 public static void main(String[] args) throws Exception { 12 //不使用反射机制,怎么去访问一个对象的属性 13 Student s=new Student(); 14 s.no=100; 15 System.out.println(s.no); 16 17 //使用反射机制 18 Class studentClass=Class.forName("javase.bean.Student"); 19 Object obj = studentClass.newInstance(); 20 //获取属性 靠名字气氛 21 Field field = studentClass.getDeclaredField("no"); 22 /* 23 * 虽然是反射机制,但三要素还是一样 24 * 要素1:obj对象 25 * 要素2:no属性 26 * 要素3:222值 27 * 注意:反射机制让代码复杂了,但更灵活,这也是值了 28 * 29 */ 30 //设置属性值 31 field.set(obj, 22222); 32 //获取属性值 33 System.out.println(field.get(obj)); 34 35 //可以访问私有的属性吗 36 Field nameField = studentClass.getDeclaredField("name"); 37 //打破封装(反射机制的缺点) 38 //这样留下安全隐患.外部可以访问private 39 nameField.setAccessible(true); 40 nameField.set(obj, "zhangsan"); 41 System.out.println(nameField.get(obj)); 42 43 } 44 45 }
2.2、通过反射机制调用对象的某个方法。
1 /** 2 * 重点:通过反射机制调方法 3 * @author yumu 4 * 5 */ 6 public class ReflectTest10 { 7 8 /** 9 * @param args 10 * @throws Exception 11 */ 12 /** 13 * @param args 14 * @throws Exception 15 */ 16 public static void main(String[] args) throws Exception { 17 //不使用反射机制,怎么调用方法 18 UserService UserService=new UserService(); 19 boolean login = UserService.login("admin", "1213"); 20 //System.out.println(login ? "登录成功":"登录失败"); 21 22 //使用反射机制来调用一个对象的方法 23 Class UserServiceClass=Class.forName("javase.service.UserService"); 24 25 //创建对象 26 Object obj=UserServiceClass.newInstance(); 27 //获取Method 28 @SuppressWarnings("unchecked") 29 Method loginMethod=UserServiceClass.getDeclaredMethod("login",String.class,String.class); 30 //调用方法有4要素 31 //反射机制中最最最重要的内容,必须记住 32 /* 33 * 四要素: 34 * loginMethod方法 35 * opbj对象 36 * "admin","123"实参 37 * returnValue 返回值 38 */ 39 Object returnValue = loginMethod.invoke(obj, "admin","123"); 40 System.out.println(returnValue); 41 } 42 43 }
2.3、通过反射机制调用某个构造方法实例化对象。
1 public class ReflectTest12 { 2 3 public static void main(String[] args) throws Exception{ 4 //不使用反射机制怎么创建对象 5 Vip v1=new Vip(); 6 Vip v2=new Vip(110,"张三","20200-10-10",true); 7 System.out.println(v2); 8 9 //使用反射机制怎么创建对象 10 Class c=Class.forName("javase.bean.Vip"); 11 //调用无参数构造方法 12 Object obj=c.newInstance(); 13 //调用有参数构造方法 14 //第一步,先获取有参数构造方法 15 Constructor con = c.getDeclaredConstructor(int.class,String.class,String.class,boolean.class); 16 Object obj1 = con.newInstance(110,"masan","1999-7-2",false); 17 System.out.println(obj1); 18 19 20 } 21 22 }
2.4、通过反射机制获取父类以及父类型接口。
1 /** 2 * 重点:获取父类,以及父类实现的接口 3 * @author yumu 4 * 5 */ 6 public class ReflectTest13{ 7 8 public static void main(String[] args) throws Exception { 9 //String 类举例 10 Class stringClass=Class.forName("java.lang.String"); 11 //获取String父类 12 Class superClass=stringClass.getSuperclass(); 13 System.out.println(superClass); 14 15 //获取String类实现的所有接口 16 Class [] interfaces =stringClass.getInterfaces(); 17 for(Class in : interfaces){ 18 System.out.println(in.getName()); 19 20 } 21 System.out.println("-------------"); 22 System.out.println(interfaces); 23 } 24 25 }
了解:
1.反编译一个类的属性field
1 ** 2 * 反射机制 3 * 反编译一个类的属性field 4 * @author yumu 5 * 6 */ 7 public class ReflectTest07 { 8 9 public static void main(String[] args) throws ClassNotFoundException { 10 StringBuilder sb=new StringBuilder(); 11 //获取整个类 12 Class studentClass=Class.forName("javase.bean.Student"); 13 // 修饰符 //类名 14 sb.append(Modifier.toString(studentClass.getModifiers())+" "+"class"+" "+studentClass.getSimpleName()+"{"+"\n"); 15 //获取所有元素 16 Field[] fields=studentClass.getDeclaredFields(); 17 for(Field field:fields){ 18 sb.append("\t"); 19 //权限修饰符 20 sb.append(Modifier.toString(field.getModifiers())); 21 sb.append(" "); 22 //数据类型 23 sb.append(field.getType().getSimpleName()); 24 sb.append(" "); 25 //数据名 26 sb.append(field.getName()); 27 sb.append(";\n"); 28 } 29 sb.append("}"); 30 System.out.println(sb); 31 32 } 33 34 }
2.反编译 Method
1 /** 2 * 反编译 Method 3 * @author yumu 4 * 5 */ 6 public class ReflectTest09{ 7 public static void main(String[] args) throws Exception { 8 StringBuilder s=new StringBuilder(); 9 //获取类 10 Class UserServiceClass =Class.forName("java.lang.String"); 11 // 修饰符 //类名 12 s.append(Modifier.toString(UserServiceClass.getModifiers())+"class"+UserServiceClass.getSimpleName()+"\n"); 13 //获取所有的Method 包括私有的 14 Method[] methods=UserServiceClass.getDeclaredMethods(); 15 //遍历Method 16 for(Method method:methods){ 17 s.append("\t"); 18 //获取修饰符列表 19 s.append(Modifier.toString(method.getModifiers())); 20 s.append(" "); 21 //获取返回值类型 22 s.append(method.getReturnType().getSimpleName()); 23 s.append(" "); 24 //获取方法名 25 s.append(method.getName()); 26 s.append("("); 27 //参数列表 28 Class[] parameterTypes=method.getParameterTypes(); 29 for(Class parameterType:parameterTypes){ 30 s.append(parameterType.getSimpleName()); 31 s.append(","); 32 33 } 34 //删除字符串最后一个 35 s.deleteCharAt(s.length()-1); 36 s.append("){}\n"); 37 38 } 39 s.append("}"); 40 System.out.println(s); 41 } 42 }
3.反编译一个类的构造方法,costructor
/** * 反编译一个类的构造方法,costructor * @author yumu * */ public class ReflectTest11{ public static void main(String[] args) throws Exception{ StringBuilder s=new StringBuilder(); Class vipClass=Class.forName("javase.bean.Vip"); s.append(Modifier.toString(vipClass.getModifiers())+" "); s.append("class "); s.append(vipClass.getSimpleName()); s.append("{\n"); //拼接构造方法 //public Vip(int no, String name, String birth, boolean sex) { Constructor[] constructors = vipClass.getDeclaredConstructors(); for(Constructor constructor:constructors){ s.append("\t"); s.append(Modifier.toString(constructor.getModifiers())); s.append(" "); s.append(vipClass.getSimpleName()); s.append("("); //拼接参数 Class[] paramenterTypes=constructor.getParameterTypes(); for(Class paramenterType:paramenterTypes){ s.append(paramenterType.getSimpleName()); s.append(","); } if(paramenterTypes.length>0){ s.deleteCharAt(s.length()-1); } s.append("){}\n"); } s.append("}"); System.out.println(s); } }