一、反射机制
1.反射机制的作用
通过java语言中的反射机制可以操作字节码文化。
2.反射机制的相关类在哪个包下?
java.lang.reflect.*;
3.反射机制相关的重要的类有哪些?
java.lang.Class:代表整个字节码,代表一个类型
java.lang.reflect.Method:代表字节码中的方法字节码,代表类中的方法
java.lang.reflect.Constructor:代码字节码中的构造方法字节码,代表类中的构造方法
java.lang.reflect.Field:代码字节码中的属性字节码,代表类中的成员变量(静态变量)
4.获取到一个类的字节码的三种方式:
第一种:通过Class这个类中的静态方法forName;
Class c = Class.froName("类名");
Class.froName():
(1)静态方法
(2)方法的参数是一个字符串
(3)字符串需要的是一个完整类名
(4)完整类名必须带有包名,java.lang包也不能省略
第二种:java中任何一个对象都有一个方法:getClass()
Class c = 对象.getClass();
第三种:java中任何一种类型,包括基本数据类型,它都有.class属性
Class c = 任何类型.class;
5.使用反射机制获取实例化对象
//不使用反射机制,创建对象
User user = new User();
System.out.println(user);
//使用反射机制创建对象
//通过反射机制,获取Class,通过Class来实例化对象
Class c = Class.forName("com.heimao.java.bean.User");//c表示user类型
//newInstance() 这个方法会调用User这个类的无参数构造方法,完成对象的创建。
//重点是:newInstance()调用的是无参构造,必须保证无参构造是存在的
Object obj = c.newInstance();
System.out.println(obj);
使用反射机制获取实例化对象的优点:可以在不改变java源代码的基础上,可以做到不同对象的实例化,非常灵活(符合OCP开闭原则)
6.Class.forName()
如果你只希望一个类的静态代码块执行,其它代码一律不执行,可以使用:
Class.forName(“完整类名”);
这个方法的执行会导致类加载,类加载时,静态代码块执行。
7.获取类路径下文件的绝对路径
使用的前提是必须在类路径下
/*
Thread.currentThread() 当前线程对象
getContextClassLoader() 是线程对象的方法,可以获取到当前线程的类加载器对象
getResource() 这是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源
*/
String path = Thread.currentThread().getContextClassLoader().getResource("").getPath();
8.以流的形式返回
//获取一个文件的绝对路径,把绝对路径传进去以流的形式返回
/*String path = Thread.currentThread().getContextClassLoader().getResource("").getPath();
FileReader reader = newFileReader(path); */
可以省略为
//直接以流的形式返回。
InputStream reader = Thread.currentThread().getContextClassLoader().getResource("");
9.资源绑定器
java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容。
使用这种方式,属性配置文件xxx.properties必须放在类路径下
//资源绑定器,只能绑定xxx.properties文件,并且这个文件必须在类路径下,文件扩展名也必须是properties
//并且在写路径的时候,路径后面的扩展名不能写
ResourceBundle bundle = ResourceBundle.getBundle("classinfo");
String className = bundle.getString("key");
System.out.println(className);
10.通过反射机制访问对象属性
给属性赋值set;
获取属性的值get
(1)先创建一个学生类,里面有四个属性
先不使用反射机制,访问一个对象的属性
Student s = new Student();
//给属性赋值
s.no = 110;
//读属性值
System.out.println(s.no);
使用反射机制,访问一个对象的属性
Class studentClass = Class.froName("com.heimao.java.bean.Student");
Object obj = studentClass.newInstance(); //此时obj就是Student对象(底层调用无参构造方法)
//获取属性(根据属性的名称来获取Field)
Field noFiled = studentClass.getDeclaredField("no");
//给obj对象的属性赋值
/*
需要三要素:
要素1:obj对象
要素2:属性
要素:220
反射机制虽然会让代码变复杂,但是会更灵活
*/
noFiled.set(obj,220);
//读取属性的值
System.out.println(noFiled.get(obj));
注意:反射机制有个缺点是如果想要获取私有属性,给私有属性赋值必须要执行noFiled.setAccessible(true); 打破封装
11.通过反射机制调用一个对象的方法
先创建一个UserService类
不使用反射机制,怎么调用方法
UserService userService = new UserService();
boolean loginSuccess = userService.login("admin", "123");
System.out.println(loginSuccess ? "登录成功" : "登录失败");
使用反射机制,调用方法
Class userServiceClass = Class.forName("com.heimao.java.service.UserService");
//创建对象
Object obj = userServiceClass.newInstance();
//获取Method
Method loginMethod = userServiceClass.getDeclaredMethod("login", String.class,String.class);
//反射机制最重要的一个方法
Object retValue = loginMethod.invoke(obj,"admin", "123");
12.使用反射调用构造方法
先创建一个VIP类,里面有编号和姓名两个属性。
不使用反射机制创建对象
Vip v1 = new Vip();
Vip v2 = new Vip(111,"zhangsna")
使用反射机制创建对象
Class c = Class.forName("com.heimao.java.bean.Vip");
//调用无参构造方法
Object obj = c.newInstance();
//调用有参构造方法
//先获取这个有参数构造方法
Constructor con = c.getDeclaredConstructor(int.class, String.class);
//然后调用构造方法new对象
Object newobj = con.newInstance(110,"zhangsan");
System.out.println(newObj);
//获取无参数构造方法
Constructor con2 = c.getDeclaredConstructor();
Object newObj2 = con2.newInstance();
System.out.println(newObj2);
13.怎么获取一个类的父类和实现的接口?
获取父类:使用.getSuperclass()方法
获取接口:使用.getInterfaces()方法
扩展
1.类加载器
(1)什么是类加载器?
专门负责加载器的命令/工具
ClassLoader
(2)JDK中自带了3个类加载器
启动类加载器
扩展类加载器
应用类加载器
(3)类加载器执行过程
首先通过“启动类加载器”加载
(注意:启动类加载器专门加载jre\lib\rt.jar,rt.jar中都是JDK最核心的类库)
如果通过“启动类加载器”加载不到的时候
才会通过“扩展类加载器”加载。
(注意:扩展类加载器专门加载jre\lib\ext*.jar)
如果通过“扩展类加载器”加载不到的时候
才会通过“应用类加载器”加载
(注意:应用类加载器专门加载classpath中的jar包(class文件))
(4)java中为了保证类加载的安全,使用了双亲委派机制。
优先从启动类加载器中加载,这个称为“父”,“父”无法加载到,再从扩展类加载器中加载,这个称为“母”。双亲委派,如果都加载不到,才会考虑从应用类加载器中加载。直到加载到为止。
2.通过反射机制,反编译一个类的属性Field
先创建一个学生类,里面有四个属性
//*
* 通过反射机制,反编译一个类的属性Field
*/
//创建这个是为了拼接字符串
StringBuilder s = new StringBuilder();
ClassstudentClass = Class.froName("com.heimao.java.bean.Student");
//s.append("public class Student {");
//s.append("public class "+ studentClass.getSimpleName() + " {"); //动态获取类名
s.append(Modifier.toString(studentClass.getModifiers()) class "+ studentClass.getSimpleName() + " {");//动态获取修饰符,并且转换为字符串
Field[] fields = stduentClass.getDeclaredFields();
for(Field field : fields){
s.append("\t");
s.append(Modifier.toString(field.getModifiers())); //输出四个属性的修饰符
s.append(" ");
s.append(field.getType().getSimpleName());
s.append(" ");
s.append(field.getName());
s.append(";\n");
}
s.append("}");
3.可变长参数
int… args
语法:类型… (注意:一定是3个点)
(1)可变长度参数要求的参数个数是:0~N个。
(2)可变长度参数在形式参数中只能出现一个,并且必须在最后一个位置上
(3)可变长度参数可以当做一个数组来看待,也可以传一个数组。