1、反射机制 1.1、反射机制有什么用? 通过java语言中的反射机制可以操作字节码文件。 有点类似于黑客。(可以读和修改字节码文件。) 通过反射机制可以操作代码片段。(class文件。)
1.2、反射机制的相关类在那个包下? java.lang.reflect.*;
1.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; } }
2、关于JDK中自带的类加载器: 2.1、什么是类加载器? 专门负责加载类的命令/工具。 ClassLoader
2.2、JDK中自带了三个类加载器 启动类加载器 扩展类加载器 应用类加载器
2.3、假设有一段代码 String s="abc" ;
代码在开始执行之前,会将所有的类全部加载到JVM当中。 通过类加载器加载,看到以上代码类加载器会找String.class 文件,找到就加载,那么是怎么进行加载的呢?
首先通过“启动类加载器”加载 注意:启动类加载器专门加载:E:\java tools\JDK8\jdk1.8.0_101\jre\lib\rt.jar rt.jar中都是JDK最核心的类库。
如果通过“启动类加载器”加载不到的时候。 会通过“扩展类加载器”加载。 注意:扩展类加载器专门加载:E:\java tools\JDK8\jdk1.8.0_101\jre\lib\ext\*.jar
如果通过“扩展类加载器”没有加载到的时候 会通过“应用类加载器”加载。 注意:应用类加载器专门加载:classpath中的类。
2.4、java为了保证类加载的安全,使用了双亲委派机制。 优先从启动类加载器中加载,这个称为“父” “父”无法加载的时候,再从扩展类加载器中 加载,这个称为“母”。双亲委派。如果都加载 不到,才会考虑从应用类加载器中加载,直到加载 到为止。
要操作一个类的字节码,需要首先获取到这个类的字节码,怎么获取java.lang.Class实例? 三种方式: 第一种:Class c=Class.forName("完整的类名带包名"); 第二种:Class c=引用.getClass(); 第三种:Class c=任何类型.class属性
Class.forName() 1、静态方法 2、方法参数是一个字符串 3、字符串需要的是一个完整类名 4、完整类名必须带有包名。
c1=Class.forName("java.lang.String"); //c1代表String.Class文件,或者说c1代表String类
java中任何一个对象都有一个方法:getClass();
String s="abc"; Class x=s.getClass();
java语言中任何一种类型,包括基本数据类型,都有.Class属性。
Class z= String.class;
获取到Class,能干什么? 通过Class的newInstance()方法来实例化对象。 注意:newInstance()方法内部实际上调用了无参数构造方法,必须保证无参数构造存在才可以。
通过反射机制获取Class,通过Class来实例化对象
Class c=Class.forName("home.mjjhy.java.bean.User");
//重点是:newInstance()调用的是无参构造,必须保证无参构造是存在的。 Object obj=c.newInstance();//home.mjjhy.java.bean.User@6bdf28bb
验证反射机制的灵活性: Java代码写一遍,在不改变java源代码的基础之上,可以做到不同对象的实例化。 非常灵活。(符合OCP开闭原则:对扩展开放,对修改关闭)
后期要学习高级框架,而工作过程中,也都是使用高级框架。 包括: ssh ssm Spring SpringMVC MyBatis Spring Struts Hibernate 这些高级框架底层实现原理:都采用了反射机制。所以反射机制还是重要的。 学会了反射机制有利于理解剖析框架底层源代码。
FileReader reader=new FileReader("chapter31\\classinfo.properties");
Properties pro=new Properties();
pro.load(reader);
reader.close();
//通过key获取value String className=pro.getProperty("className"); //System.out.println(ClassName);
//通过反射机制实例化对象 Class c=Class.forName(className); Object obj=c.newInstance(); System.out.println(obj);
classinfo.properties文件
className=java.util.Date
研究:Class.forName()发生了什么? 重点: 如果你只希望一个类的静态代码块执行,其他代码一律不执行, 可以使用: Class.forName("完整类名"); 这个方法执行会导致类加载,类加载时,静态代码块执行。
提示: 后面JDBC技术的时候还需要。
反射Student类当中的field
辅助类
public class Student { //Field翻译为字段,其实就是属性/成员 public String name; private int no; protected int age; boolean sex; public final static double MATH_PI=3.1415926;
public Student(){ System.out.println("无参构造已经执行"); }
public Student(String name,int no,int age,boolean sex){ this.name=name; this.no=no; this.age=age; this.sex=sex; } }
Class studentClass =Class.forName("home.mjjhy.java.bean.Student");
Field[] fields=studentClass.getDeclaredFields();
//获取完整的类名 String studentClassName=studentClass.getName(); System.out.println("完整类名"+studentClassName);
//获取简类名 String studentClassSimpleName=studentClass.getSimpleName(); System.out.println("简类名"+studentClassSimpleName);
for(Field field:fields){ //获取所有属性的修饰符列表 int modifiers=field.getModifiers(); String modifiersName=Modifier.toString(modifiers); //System.out.println(modifiersName); //获取所有属性的类型 //filed.getType(); Class fieldClass=field.getType(); String fieldClassName=fieldClass.getSimpleName(); //System.out.println(fieldClassName);
String fieldName=field.getName(); //System.out.println(fieldName);
System.out.println(modifiersName+" "+fieldClassName+" "+fieldName); }
通过反射机制,反编译一个类的属性Field
StringBuilder s=new StringBuilder();
//Class studentClass =Class.forName("home.mjjhy.java.bean.Student"); Class studentClass =Class.forName("java.lang.String");
s.append(Modifier.toString(studentClass.getModifiers())+" class "+studentClass.getSimpleName()+"{");
Field[] fields=studentClass.getDeclaredFields();
for (Field field:fields){ s.append("\n"); s.append("\t"+Modifier.toString(field.getModifiers())+" "+field.getType().getSimpleName()+" "+field.getName());
} s.append("\n"); s.append("}"); System.out.println(s);
重点: 怎么通过反射机制访问一个java对象的属性? 给属性赋值set 获取属性的值get
//使用反射机制,怎么访问一个对象的属性 Class studentClass=Class.forName("home.mjjhy.java.bean.Student");
//创建Student对象 Object obj=studentClass.newInstance();
//公开的属性 Field nameField=studentClass.getDeclaredField("name"); nameField.set(obj,"zhangsan"); System.out.println(nameField.get(obj));
//私有的属性 Field noField=studentClass.getDeclaredField("no");
//打破封装(反射机制的缺点:打破封装,可能会给不法分子留下就会!!!!) noField.setAccessible(true); noField.set(obj,1111); System.out.println(noField.get(obj));
反射Method
辅助类
/** * 用户业务类 */ public class UserService { /** * * @param name 用户名 * @param password 密码 * @return true表示登录成功呢,false表示登录失败!!! */ public boolean login(String name,String password){ if("admin".equals(name)&&"123".equals(password)) return true; else return false; }
/** * 退出系统的方法 */ public void logout(){ System.out.println("系统已经安全退出"); }
}
Class UserClass=Class.forName("home.mjjhy.java.service.UserService");
Method[] methods=UserClass.getDeclaredMethods();
for(Method method:methods){ //获得所有方法的修饰列表 int i=method.getModifiers(); String methodModifier=Modifier.toString(i); System.out.println(methodModifier);
//获得所有方法类型 String methodType=method.getReturnType().getSimpleName(); System.out.println(methodType);
//获得方法名 String methodName=method.getName(); System.out.println(methodName);
//获取参数列表 Class[]parameterTypes=method.getParameterTypes(); for(Class parameterType:parameterTypes){ System.out.println(parameterType.getSimpleName()); }
反编译一个类
StringBuilder s=new StringBuilder(); //Class userClass=Class.forName("home.mjjhy.java.service.UserService"); Class userClass=Class.forName("java.lang.String");
s.append(Modifier.toString(userClass.getModifiers())+" class "+userClass.getSimpleName()+"{"); Method[] methods=userClass.getDeclaredMethods();
for(Method method:methods){ s.append("\n"); s.append("\t"); s.append(Modifier.toString(method.getModifiers())+" "+method.getReturnType().getSimpleName()+" "+method.getName()+"(");
Class[] methodParameterTypes=method.getParameterTypes(); if (methodParameterTypes.length==0){ s.append("){}"); }else{ for(Class parameterType:methodParameterTypes){ s.append(parameterType.getSimpleName()); s.append(","); } s.deleteCharAt(s.length()-1); s.append(")"); s.append("{}"); }
}
s.append("\n"); s.append("}"); System.out.println(s);
通过反射机制,调用一个对象的方法。
反射机制,让代码具有通用性,可变化的内容都是写在配置文件当中, 将来修改配置文件之后,创建对象不一样了,调用方法也不同了,但是 java代码不需要改动。
//获取类 Class userServiceClass=Class.forName("home.mjjhy.java.service.UserService");
//创建对象 Object obj=userServiceClass.newInstance();
//获取Method Method loginMethod=userServiceClass.getDeclaredMethod("login", String.class, String.class);
//调用方法 /* 要素分析: 要素1:对象usrService 要素2:login方法名 要素3:实参列表 要素4:返回值 */ Object reValue=loginMethod.invoke(obj,"admin","123"); System.out.println(reValue);
反编译一个类的Constructor构造方法
辅助类
public class Vip { int no; public String name; String birth; boolean sex;
public Vip(){ }
public Vip(int no){ this.no=no; }
public Vip(int no, String name) { this.no = no; this.name = name; }
public Vip(int no, String name, String birth) { this.no = no; this.name = name; this.birth = birth; }
public Vip(int no, String name, String birth, boolean sex) { this.no = no; this.name = name; this.birth = birth; this.sex = sex; }
StringBuilder s=new StringBuilder();
//获取vip类 //Class vipClass=Class.forName("home.mjjhy.java.bean.Vip"); Class vipClass=Class.forName("java.lang.String");
s.append(Modifier.toString(vipClass.getModifiers())+" class "+ vipClass.getSimpleName()+"{");
//获取构造方法 Constructor[] constructors =vipClass.getDeclaredConstructors();
for(Constructor constructor:constructors){ s.append("\n\t");
s.append(Modifier.toString(constructor.getModifiers())+"\t"+vipClass.getSimpleName()+" (");
Class[] parameterTypes=constructor.getParameterTypes(); if(parameterTypes.length==0){ s.append(")"); }else{ for(Class parameterType:parameterTypes){ s.append(parameterType.getSimpleName()); s.append(","); } s.deleteCharAt(s.length()-1); s.append("){}"); } } s.append("\n}"); System.out.println(s);
通过反射机制创建对象(使用有参构造方法)
Class vipClass=Class.forName("home.mjjhy.java.bean.Vip");
//调用无参数构造方法 vipClass.newInstance();
//调用有参构造 Constructor constructor=vipClass.getDeclaredConstructor(int.class,String.class,String.class,boolean.class); Object obj=constructor.newInstance(110,"jackson","1990-10-11",true); System.out.println(obj);
重点:给你一个类,怎么获取这个类的父类,已经实现了那些接口
public static void main(String[] args) throws Exception{ //String举例 Class stringClass=Class.forName("java.lang.String");
//获取String类的父类 Class superclass=stringClass.getSuperclass(); System.out.println(superclass.getName());
//获取String类的所有接口 Class[] stringClassInterfaces=stringClass.getInterfaces();
for(Class stringClassInterface:stringClassInterfaces){ System.out.println(stringClassInterface.getName()); } }
/* 研究文件路径问题: 怎么获取一个文件的绝对路径。以下这种方式是通用的,但前提是:文件需要在类路径下。 */ public class AboutPath { public static void main(String[] args) throws Exception { //这种方式的路径缺点是:移植性差,在IDEA中默认的当前路径是project的根。 //这个代码假设离开了IDEA,换到其他位置,可能当前路径就不是project的根了。这时这个路径就无效了。 //FileReader reader=new FileReader("chapter31\\classinfo.properties");
//通用的一种路径,即使代码换位置,这样编写仍然是通用的。 //使用以下通用方式的前提是:这个文件必须是类路径下的。 //凡是在src下的,都是类路径下的。 //src是类的根路径。
/* 解释: Thread.currentThread() 当前线程对象 getContextClassLoader() 是线程对象的方法,可以获取当前线程的类加载对象 getRResource() 【获取资源】这是类加载对象的方法,当前线程的类加载器默认从类的根路径下加载资源。 */ String path=Thread.currentThread().getContextClassLoader(). getResource("classinfo2.properties").getPath();//这种方式获取文件绝对路径通用的方法 System.out.println(path);
String path2=Thread.currentThread().getContextClassLoader(). getResource("home/mjjhy/java/bean/db.properties").getPath(); System.out.println(path2);
} }
/* java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容。 使用以下这种方式的时候,属性配置文件*****.properties必须放到类路径下。 */ public class ResourceBundleTest { public static void main(String[] args) {
//资源绑定器,只能绑定***.properties文件,并且文件必须在类路径下,文件扩展扩展名必须是properties //并且在写路径的时候,路径后面的扩展名不能写 //ResourceBundle bundle= ResourceBundle.getBundle("classinfo2");
ResourceBundle bundle= ResourceBundle.getBundle("home/mjjhy/java/bean/db");
String className=bundle.getString("className"); System.out.println(className);
} }