目录
08 反射机制
1、关于反射机制
1.1 反射机制有什么用?
1.2 反射机制的相关类在哪个包下?
1.3 反射机制相关的重要的类有哪些?
1.4 在java中获取Class的三种方式
1.5 获取了Class之后,可以调用无参数构造方法来实例化对象
1.6、如果你只想让一个类的“静态代码块”执行的话,你可以怎么做?
1.7 关于路径问题
1.8 IO流方式读取配置文件
1.9 直接以流的形式返回:
1.10 IO + Properties快速绑定属性资源文件
2、关于JDK中自带的类加载器:
2.1 什么是类加载器?
2.2 JDK中自带了3个类加载器
2.3 关于三个类加载器
2.4 java中为了保证类加载的安全,使用了双亲委派机制。
3、Field
3.1 通过反射机制获取属性的常用方法
3.2 通过反射机制反编译一个类的属性
3.3 通过反射机制访问一个java对象的属性(重点)
4、可变长度参数
5、Method
5.1 通过反射机制获取类的Method的常用方法、
5.2 通过反射机制访问一个java对象的属性(重点)
6、Construct
6.1 Construct常用的方法
6.2 通过反射机制new对象
7、获取类的父类和接口
通过java语言中的反射机制可以操作字节码文件。
优点类似于黑客。(可以读和修改字节码文件。)
通过反射机制可以操作代码片段。(class文件。)
反射机制使得对象的创建更加地灵活,可以通过读取配置文件的方式随意改变new的对象,不会将java代码写死
java.lang.reflect.*;
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;
}
}
第一种:
Class c = Class.forName("完整类名");
//这个完整的类名可以通过读取配置文件的方式获取
第二种:
Class c = 对象.getClass();
第三种:
Class c = int.class;
Class c = String.class;
//c代表的就是日期Date类型
Class c = Class.forName(“java.util.Date”);
//实例化一个Date日期类型的对象
Object obj = c.newInstance();
一定要注意:
newInstance()底层调用的是该类型的无参数构造方法。
如果没有这个无参数构造方法会出现"实例化"异常。
Class.forName(“该类的类名”);
这样类就加载,类加载的时候,静态代码块执行!!!!
在这里,对该方法的返回值不感兴趣,主要是为了使用“类加载”这个动作。
String path = Thread.currentThread().getContextClassLoader()
.getResource("写相对路径,但是这个相对路径从src出发开始找").getPath();
String path = Thread.currentThread().getContextClassLoader()
.getResource("abc").getPath(); //必须保证src下有abc文件。
String path = Thread.currentThread().getContextClassLoader()
.getResource("a/db").getPath(); //必须保证src下有a目录,a目录下有db文件。
String path = Thread.currentThread().getContextClassLoader()
.getResource("com/bjpowernode/test.properties").getPath();
//必须保证src下有com目录,com目录下有bjpowernode目录。
//bjpowernode目录下有test.properties文件。
这种方式是为了获取一个文件的绝对路径。(通用方式,不会受到环境移植的影响。),但是该文件要求放在类路径下,换句话说:也就是放到src下面。
src下是类的根路径。
//获取一个文件的绝对路径
String path = Thread.currrntThread().getContextClassLoader()
.getResource("classinfo2.properties").getPath();
FileReader reader = new FileReader(path);
Properties pro = new Properties();
pro.load(reader);
reader.close();
//通过key获取value
String class Name = pro.getProperty("className");
//这里就得到了配置文件中的Name信息,从而可以使用反射机制来new对象
System.out.println(className);
InputStream reader = Thread.currentThread().getContextClassLoader()
.getResourceAsStream("com/bjpowernode/test.properties");
Properties pro = new Properties();
pro.load(reader);
reader.close();
//通过key获取value
String class Name = pro.getProperty("className");
System.out.println(className);
//要求:第一这个文件必须在类路径下
//第二这个文件必须是以.properties结尾。
ResourceBundle bundle = ResourceBundle.getBundle("com/bjpowernode/test");
String value = bundle.getString(key);
专门负责加载类的命令/工具。ClassLoader
启动类加载器:rt.jar
扩展类加载器:ext/*.jar
应用类加载器:classpath
假设有这样一段代码:
String s = "abc";
代码在开始执行之前,会将所需要类全部加载到JVM当中。
通过类加载器加载,看到以上代码类加载器会找String.class文件,找到就加载,那么是怎么进行加载的呢?
优先从启动类加载器中加载,这个称为“父”。“父”无法加载到,再从扩展类加载器中加载,这个称为“母”。双亲委派。如果都加载不到,才会考虑从应用类加载器中加载。直到加载到为止。
获取到所有public修饰的属性
Field[] fields = 类名.getFields();
获取到属性名
String fieldName = fields[1].getName();//com.bjpowernode.java.bean.Stu
String fieldName = fields[1].getSimpleName();//Stu
获取所有的属性
Field[] fields = 类名.getDeclaredFields();
获取属性的类型
Class fieldType = fields[1].getType();
String fName = fieldType.getName();
获取属性的修饰符列表
int i = fields[1].getModifiers();//返回的是修饰符的代号
String modifierString = Modifier.toString(i);
package reflect;
//通过反射机制反编译一个类的属性
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class reflextTest07 {
public static void main(String[] args) throws ClassNotFoundException {
StringBuilder stringBuilder = new StringBuilder();//为了拼接字符串
Class studentClass = Class.forName("reflect.Student");
Field[] fields = studentClass.getDeclaredFields();
stringBuilder.append(Modifier.toString(studentClass.getModifiers()) + " class " + studentClass.getSimpleName() + "{" + "\n");
for(Field field:fields){
stringBuilder.append("\t");
stringBuilder.append(Modifier.toString(field.getModifiers()) + " ");
stringBuilder.append(field.getType() + " ");
stringBuilder.append(field.getName());
stringBuilder.append(";" + "\n");
}
stringBuilder.append("}");
System.out.println(stringBuilder);
}
}
给属性赋值set、获取属性的值get
三要素:给s对象的no属性赋值1111
要素1:对象5
要素2:no属性
要素3:1111
我们不使用反射机制,怎么去访问一个对象的属性呢?
//创建对象
Student s = new Student();
//给属性赋值
s.no = 1111;
//读属性值
System.out.println(s.no);
使用反射机制,怎么去访问一个对象的属性。(set get)
//获取类,通过类实例化对象
Class studentClass = Class.forName("com.bjpowernode.java.bean.Student");
Object obj=studentClass.newInstance();// obj就是student对象。(底层调用无参数构造方法)
//获取no属性(根据属性的名称来获取Field)
Field noFiled = studentClass.getDeclaredField("no");
// 给obj对象(Student对象)的no属性赋值
noFiled.set(obj,22222);//给obj对象的no属性赋值2222
//读取属性的值
Object obj = noFiled.get(obj);
**出现的问题:**私有属性不可以通过上述访问进行赋值访问
**解决方案:**打破封装(缺点),这样设置之后,在外部也可以访问私有属性
Field noFiled = studentClass.getDeclaredField("no");
noField.setAccessible(true);
int…args这就是可变长度参数
语法是:类型…(注意:一定是3个点。)1、可变长度参数要求的参数个数是:0~N个。
2、可变长度参数在参数列表中必须在最后一个位置上,而且可变长度参数只能有1个。
3、可变长度参数可以当中一个数组来看待
m2(100);
m2(200, "abe");
m2(200, "abc", "def");
m2(200, "abe", "def", "xyz");
m3("ab","de","kk","ff");
String[] strs = {"a","b","c"};
//也可以传1个数组
m3(strs);
m3(new String[]{“我","是","中","国",“人"});
public static void m(int... angs){
System.out.println("m方法执行了!");
}
//必须在最后,只能有1个。
public static void m2(int a, String... args1){
}
public static void m3(String.…. args)
//args有ength属性,说明args是一个数组!
//可以将可变长度参数当做一个数组来看。
for(int i = 0; i < args.length; i++){
System.out.println(args[i]);
}
}
获取所有的Method(包括私有的!)
Method[] methods = userServiceClass.getDeclaredMethods();
获取修饰符列表
Modifier.toString(method.getModifiers());
获取方法的返回值类型
method.getReturnType().getSimpleName();
获取方法名
method.getName();
方法的修饰符列表(一个方法的参数可能会有多个。
Class[] parameterTypes = method.getParameterTypes();
//获取类
Class userServiceClass = Class.forName("com.UserService");
//获取所有的Method(包括私有的!)
Method[] methods = userServiceClass.getDeclaredMethods();
//System.out.println(methods.Length);// 2
// 通历Method
for(Method method:methods){
//获取修饰符列表
System.out.println(Modifier.toString(method.getModifiers()));
//获取方法的返回值类型
System.out.printIn(method.getReturnType().getSimpleName());
//获取方法名
System.out.println(method.getName());
//方法的修饰符列表(一个方法的参数可能会有多个。)
Class[] parameterTypes = method.getParameterTypes();
for(Class parameterType : parameterTypes){
System.out.println(parameterType.getSimpleName());
}
}
不使用反射机制,怎么调用方法
//创建对象
UserService userService = new UserService();
//调用方法
boolean loginSuccess = userService.login( name: "admin", password: "123");
//System.out.printLn(LoginSuccess);
System.out.println(loginSuccess?“登录成功":"登录失数");
使用反射机制来调用一个对象的方法该怎么做?
Class userServiceClass = Class.forName("com.bjpowernode.java.service.UserService");
// 创建对象
Object obj = userServiceClass.newInstance();
// 获取Method(区分Method的有参数名和参数列表)
Method loginMethod = userServiceClass.getDeclaredMethod( "login", String.class);
Method loginMethod2 = userServiceClass.getDeclaredMethod("login", int.class);
//调用方法有几个要素?也需要4要素。
//反射机制中最最最最最重要的一个方法,必须记住。
//四要素:LoginMethod方法
//LoginMethod方法
//obj对象
//"odmin","123”实参
//retVal ue 返回值
obj invoke loginMethod方法(“admin”,“123”)
//调用方法
Object retValue = loginMethod.invoke(obj,"admin","123");
获取无参构造方法
Construct[] constructors = 类名.getDeclaredConstructors();
String s = Modifier.toString(constructor.getModifiers());
不使用反射机制怎么创建对象
Vip v1 = new Vip();
Vip v2 = new Vip( 10, "zhangsan", "2001-10-11", true);
使用反射机制怎么创建对象呢?
Class c = Class.forName("com.bjpowernode.java.bean.Vip");
//调用无参数构造方法
Object obj = c.newInstance();//jdk8
System.out.println(obj);
//获取无参数构造方法
Constructor con2 = c.getDeclaredConstructor();//jdk13
Object newObj2 = con2.newInstance();
//调用有参数的构造方法怎么办?
//第一步:先获取到这个有参数的构造方法
Constructor con = c.getDeclaredConstructor(int.class, String.class, String.class,boolean.class);
//第二步:调用构造方法new对象
Object newObj = con.newInstance(110,"jackson","1990-10-11",true)
System.out.println(newObj);
获取String的父类
Class superClass = 类名.getSuperclass();
System.out.println(superClass.getName());
获取所有的接口
//获取String类实现的所有接口(一个类可以实现多个接口。)
Class[] interfaces = stringClass.getInterfaces();
for(Class in:interfaces){
System.out.println(in.getName());
}