文章目录
- 类加载器
- 类加载时机
- Java代码的3个阶段
- 反射
- 关于Class
- 配置文件(.properties)
- Properties类
- 通过反射获取构造方法(Constructor)
- 通过反射获取成员变量(Field)
- 通过反射获取成员方法(Method)
- 其他API
- 自定义类加载器
- 反射的应用
类加载器
分类:
-
Bootstrap ClassLoader
根类加载器- 负责Java运行时核心类的加载,JDK中JRE的lib目录下rt.jar
-
Extension ClassLoader
扩展类加载器- 负责JRE的扩展目录中jar包的加载,在JDK中JRE的lib目录下ext目录
-
Sysetm(App) ClassLoader
系统类加载器/应用加载器- 负责加载自己定义的Java类
逻辑上的父子关系:
类加载时机
-
创建类的实例(首次创建该类对象)
-
访问类的静态变量(首次)
-
调用类的静态方法(首次)
-
加载某个类的子类,会先触发父类的加载
-
直接使用
java.exe
命令来运行某个主类,也就是执行了某个类的main()
方法 -
使用反射方式来强制创建某个类或接口对应的
java.lang.Class
对象
Java代码的3个阶段
反射
获取运行时类信息的一种手段,反射的起点是字节码文件对象。
获取字节码文件对象的几种方式:
对象.getClass()
类名.class
Class.forName(String className)
全限定名- ClassLoader里的
loadClass(String className)
无论通过什么方式获取的字节码文件对象,都是同一个。
eg:
public class Demo {public static void main(String[] args) throws ClassNotFoundException {// 对象.getClass()A a = new A();Class<? extends A> c1 = a.getClass();// 类名.classClass<A> c2 = A.class;System.out.println(c1 == c2);// Class.forName(String className) 全限定名Class<?> c3 = Class.forName("com.cskaoyan.Demo0112.A");System.out.println(c1 == c3);// ClassLoader里的loadClass(String className)ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();Class<?> c4 = systemClassLoader.loadClass("com.cskaoyan.Demo0112.A");System.out.println(c1 == c4);}
}class A{}
注:
类名.class
没有执行静态代码块Class.forName()
执行静态代码块
eg:
@Testpublic void test1() throws ClassNotFoundException {// 类名.class// 没有执行静态代码块Class<B> b1 = B.class;// Class.forName()// 执行静态代码块Class<?> b2 = Class.forName("com.cskaoyan.Demo0112.B");}class B{static{System.out.println("this is static");}
}
关于Class
-
Class
类的实例表示正在运行的 Java 应用程序中的类和接口 -
Class
没有公共构造方法。Class
对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass
方法自动构造的。
配置文件(.properties)
-
配置文件的几种格式
.properties
、.xml
、.yml
-
配置文件的作用: 放配置信息的 (数据库的, 第三方服务的配置信息)
-
.properties
的格式:- 键值对(
key-value
) key=value
key
是不能重复的- 注释是
#
- 文件里面全是
String
- 键值对(
Properties类
Properties
类表示了一个持久的属性集。Properties
可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。
在文件 ---> 资源包
创建可得
eg:
# 数据库的配置信息
url=jdbc:mysql://localhost:3306/test
host=localhost
port=3306
user= root
password=1234567
构造方法:
Properties() // 创建一个无默认值的空属性列表。
成员方法:
eg:
public class Demo {public static void main(String[] args) throws IOException {// 1. 创建Properties对象Properties properties = new Properties();// 2. loadproperties.load(new FileInputStream("People.properties"));// 3. 获取属性值// getProperty(String key)String port = properties.getProperty("port");String user = properties.getProperty("user");String password = properties.getProperty("password");String host = properties.getProperty("host");System.out.println(port);System.out.println(password);System.out.println(user);System.out.println(host);}
}
或
public class Demo {public static void main(String[] args) throws IOException {// 1. 创建Properties对象Properties properties = new Properties();// 2. 通过类加载器URL systemResource = ClassLoader.getSystemResource("");System.out.println(systemResource); // "" 在这里面,把"People.properties"应该放这里输出的路径下面InputStream in = ClassLoader.getSystemResourceAsStream("People.properties");// 3. loadproperties.load(in);// 4. 获取属性值// getProperty(String key)String port = properties.getProperty("port");String user = properties.getProperty("user");String password = properties.getProperty("password");String host = properties.getProperty("host");System.out.println(port);System.out.println(password);System.out.println(user);System.out.println(host);}
}
输出中文的情况:
public class Demo {public static void main(String[] args) throws IOException {// 创建Properties对象Properties properties = new Properties();// loadproperties.load(new InputStreamReader(new FileInputStream("People.properties"),"GBK"));// 获取属性String user = properties.getProperty("user");System.out.println(user);}
}
通过反射获取构造方法(Constructor)
通过反射获取所有构造方法:
Constructor[] getConstructors() // 获取的是public的构造方法Constructor[] getDeclaredConstructors() // 获取所有的构造方法
获取指定构造方法:
Constructor<T> getConstructor(Class<?>... parameterTypes)Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
使用Constructor创建对象:
Person p = new Person("zs",20,true)newInstance(参数列表)
暴力破解:
上面的方法创建不了private
的对象,这里可以
setAccessible(true) // 忽略java语法检查
先定义一个Person类:
public class Person {public String name;private int age;boolean gender;public Person(String name, int age, boolean gender) {this.name = name;this.age = age;this.gender = gender;}private Person(String name, int age) {this.name = name;this.age = age;}public Person(String name) {this.name = name;}public Person() {}public void eat() {System.out.println("eat food");}private void eat(String food) {System.out.println("eat" + food);}private String sleep() {return "sleep";}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +", gender=" + gender +'}';}
}
eg:
public class Demo {public static void main(String[] args)throws ClassNotFoundException, NoSuchMethodException,InvocationTargetException, InstantiationException, IllegalAccessException {// 1. 获取字节码文件对象Class<?> a1 = Class.forName("com.cskaoyan.Demo0112.Person");// 2. 获取所有的构造方法// Constructor[] getConstructors()Constructor<?>[] constructors = a1.getConstructors();for (Constructor<?> constructor : constructors) {System.out.println(constructor);}// Constructor[] getDeclaredConstructors()Constructor<?>[] declaredConstructors = a1.getDeclaredConstructors();for (Constructor<?> declaredConstructor : declaredConstructors) {System.out.println(declaredConstructor);}// 3. 获取指定构造方法// Constructor<T> getConstructor(Class<?>... parameterTypes)Constructor<?> constructor = a1.getConstructor(String.class, int.class, boolean.class);System.out.println(constructor);// Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)Constructor<?> declaredConstructor = a1.getDeclaredConstructor(String.class, int.class);System.out.println(declaredConstructor);// 4. 使用Constructor创建对象// 通过构造方法对象创建对象//newInstance(参数列表)Object o = constructor.newInstance("zs", 20, true);System.out.println(o);// 5. setAccessible(true)declaredConstructor.setAccessible(true);Object o1 = declaredConstructor.newInstance("ww", 18);System.out.println(o1);}
}
通过反射获取成员变量(Field)
通过反射获取所有成员变量:
Field[] getFields()Field[] getDeclaredFields()
获取指定成员变量:
Field getField(String name) // 获取的是public权限Field getDeclaredField(String name) // 获取的是所有权限
通过Field读写对象的成员变量(可暴力破解):
Object get(Object obj) // 获取值,传入对象void set(Object obj, Object value) // 赋值,传入对象
eg:
public class Demo {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {// 1. 拿到字节码文件对象Class<?> a1 = Class.forName("com.cskaoyan.Demo0112.Person");// 2. 通过反射获取所有的成员变量// Field[] getFields()Field[] fields = a1.getFields();for (Field field : fields) {System.out.println(field);}// Field[] getDeclaredFields()Field[] declaredFields = a1.getDeclaredFields();for (Field declaredField : declaredFields) {System.out.println(declaredField);}// 3. 获取指定成员变量// Field getField(String name)Field nameField = a1.getField("name");System.out.println(nameField);// Field getDeclaredField(String name)Field ageField = a1.getDeclaredField("age");System.out.println(ageField);// 4. 通过Field读写对象的成员变量(可暴力破解)// void set(Object obj, Object value):赋值,传入对象Constructor<?> declaredConstructor = a1.getDeclaredConstructor();Object o = declaredConstructor.newInstance();nameField.set(o,"zs");System.out.println(o);// Object get(Object obj):获取值,传入对象Object o1 = nameField.get(o);System.out.println(o1);}
}
通过反射获取成员方法(Method)
获取所有成员方法:
Method[] getMethods()// 父类的也能获取到,只能获取public的方法Method[] getDeclaredMethods() // 能够获取所有的方法
获取指定的成员方法:
Method getMethod(String name, Class<?>... parameterTypes)Method getDeclaredMethod(String name, Class<?>... parameterTypes)
利用Method调用对象的方法:
Object invoke(Object obj, Object... args)
eg:
public class Demo {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {// 1. 获取字节码文件对象Class<?> a1 = Class.forName("com.cskaoyan.Demo0112.Person");// 2. 获取所有成员方法// Method[] getMethods()// 父类的也能获取到Method[] methods = a1.getMethods();for (Method method : methods) {System.out.println(method);}// Method[] getDeclaredMethods()Method[] declaredMethods = a1.getDeclaredMethods();for (Method declaredMethod : declaredMethods) {System.out.println(declaredMethod);}// 3. 获取指定的成员方法// Method getMethod(String name, Class<?>... parameterTypes)Method eatMethod = a1.getMethod("eat");System.out.println(eatMethod);// Method getDeclaredMethod(String name, Class<?>... parameterTypes)Method eatDeclaredMethod = a1.getDeclaredMethod("eat", String.class);System.out.println(eatDeclaredMethod);// 利用Method调用对象的方法// Object invoke(Object obj, Object... args)Constructor<?> declaredConstructor = a1.getDeclaredConstructor();Object o = declaredConstructor.newInstance();eatMethod.invoke(o);}
}
其他API
注:可以通过Class直接实例化 , 但是要有一个无参构造方法
eg:
public class Demo {public static void main(String[] args)throws ClassNotFoundException, InstantiationException, IllegalAccessException {Class<?> a1 = Class.forName("com.cskaoyan.Demo0112.A");// 通过class对象直接实例化对象Object o = a1.newInstance();System.out.println(o);}
}class A {int a;public A(int a) {this.a = a;}public A() {}
}
eg:
public class Demo {public static void main(String[] args)throws ClassNotFoundException, NoSuchFieldException,NoSuchMethodException {// 1. 先获取字节码文件对象Class<?> a1 = Class.forName("com.cskaoyan.Demo0112.Person");Class<?> a2 = Class.forName("java.io.OutputStream");// 2. 获取类名System.out.println(a1.getName());// 3. 获取简单名System.out.println(a1.getSimpleName());// 4. 获取父类System.out.println(a1.getSuperclass());System.out.println(a1.getSuperclass().getSimpleName());// 5. 获取实现的接口Class<?>[] interfaces = a2.getInterfaces();for (Class<?> anInterface : interfaces) {System.out.println(anInterface);}// 6. 获取成员变量Field nameField = a1.getDeclaredField("name");// 获取成员变量的类型System.out.println(nameField.getType());int modifiers = nameField.getModifiers();System.out.println(modifiers); // 1 就是publicSystem.out.println(Modifier.toString(modifiers));// 7. 获取method对象Method eatMethod = a1.getDeclaredMethod("eat", String.class);// 方法的返回值类型System.out.println(eatMethod.getReturnType());// 方法的参数类型Class<?>[] parameterTypes = eatMethod.getParameterTypes();for (Class<?> parameterType : parameterTypes) {System.out.println(parameterType);}}
}
自定义类加载器
步骤:
- 继承
ClassLoader
- 重写
findClass
方法
eg:
MyClassLoader自定义类加载器:
public class MyClassLoader extends ClassLoader {// 成员String classPath;public MyClassLoader(String classPath) {this.classPath = classPath;}@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {Class<?> aClass = null;// 读取.class文件try {byte[] data = getData();// defineClass(String name, byte[] b, int off, int len)// 将一个 byte 数组转换为 Class 类的实例。aClass = defineClass(name, data, 0, data.length);} catch (IOException e) {e.printStackTrace();}// 最终要返回一个Class对象return aClass;}private byte[] getData() throws IOException {// 读取字节码文件// 创建FileInputStreamFileInputStream in = new FileInputStream(classPath);// 使用ByteArrayOutputStreamByteArrayOutputStream outputStream = new ByteArrayOutputStream();int readCount;byte[] bytes = new byte[1024];while ((readCount = in.read(bytes)) != -1 ){outputStream.write(bytes,0,readCount);}/*byte[] toByteArray()创建一个新分配的byte数组*/byte[] bytes1 = outputStream.toByteArray();// 返回字节码文件里的数据return bytes1;}
}-----------------------------------------------------public class Demo {public static void main(String[] args) throws ClassNotFoundException,NoSuchMethodException, InstantiationException,IllegalAccessException, InvocationTargetException {// 定义加载路径String classPath ="D:\\Test.class";// 创建自定义类加载器MyClassLoader myClassLoader = new MyClassLoader(classPath);// 通过loadClass方法加载类Class<?> testLc = myClassLoader.loadClass("Test");// 得到字节码文件对象// 拿到Method对象Method funcMethod = testLc.getDeclaredMethod("func");// invokeObject o = testLc.newInstance();funcMethod.invoke(o);}
}
反射的应用
-
通过反射获取注解信息
-
动态代理
-
ORM(Object Relational Mapping)框架
, 数据库框架