反射
类的加载概述
程序要使用某个类,如果类还未加载到内存中,系统会通过加载,连接,初始化来实现对这个类进行初始化
加载,将class文件读入内存,并创建一个Class对象。任何类被使用时系统都会创建一个Class对象
连接
验证:是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并且设置默认值
解析 将类的二进制数据符号引用替换为直接引用
加载时机
创建类的实例
访问类的静态变量,或者为静态变量赋值
调用类的静态方法
使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
初始化某个子类
直接使用java.exe来运行某个主类
--------------------------------------------------------------------------------------
类加载器分类:负责将.class文件加载到内存中,并为之生成对应的Class对象
类加载器分类:
Bootstrap ClassLoader 根类加载器 引导类加载器 java核心类的加载System String 在jdk下的jre中的rt.jar
Extension ClassLoader 扩展类加载器 负责jre扩展目录下的jar包的加载,在JDK中JRT的lib目录下的ext目录加载
System ClassLoader 系统类加载器 负责在JVM启动时候加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径
------------------------------------------------------------------------------------
反射概述
JAVA反射机制就是在运行过程中,对任意一个类,都能知道这个类所有的属性和方法
对于任意的一个对象,都能够调用它的任意一个方法和属性
这种动态获取信息以及动态调用对象方法的方式成为JAVA的反射机制
想要解刨一个类,必须要获取到该类的字节码文件对象
而解刨使用的就是Class类中的方法,所以要获取每一个字节码文件对应的Class文件
三种方式
Object类的getClass()方法,判断两个对象是否是同一个字节码文件
静态对象class,锁对象
Class类中的静态方法,读取配置文件
源文件阶段 字节码文件 创建配置文件
Person.java Person.class Person p = new Person();
Class clazz = class.forName("类名");
Class clazz = Person.class;
Class clazz = p.getClass();
读取配置文件,只改配置文件就可以获取不同的属性和方法
字节码文件 当作静态方法锁对象
判断是否是同一个字节码对象
------------------------------------------------------------------------------------
Class.forName()读取配置文件
榨汁机Juicer榨汁,分别有水果 苹果 香蕉 橘子 榨汁
package com.ysu.reflect;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class Reflect {
public static void main(String[] args) throws ClassNotFoundException, IOException, InstantiationException, IllegalAccessException {
//没用反射,只在使用动态
// Juicer juicer = new Juicer();
// juicer.run(new Apple());
// 用发射和配置文件
BufferedReader br = new BufferedReader(new FileReader("config.properties"));
Class clazz = Class.forName(br.readLine());
Fruit f = (Fruit) clazz.newInstance(); //父类引用指向子类对象
Juicer juicer = new Juicer();
juicer.run(f);
}
}
interface Fruit{
public void squeeze();
}
class Juicer {
public void run(Fruit f){
f.squeeze();
}
}
class Apple implements Fruit{
@Override
public void squeeze(){
System.out.println("榨出一杯苹果汁");
}
}
class Orange implements Fruit{
public void squeeze(){
System.out.println("榨出橘子酱");
}
}
------------------------------------------------------------------------------------
反射获取构造方法
Constructor
Class类的newInstance()方法是使用该类无参数的构造方法创建对象,一个类没有无参数构造函数,不能使用该方法,但是可以调用Class类的getConstructor()创建
获取有参构造创建对象
Class clazz = Class.forName("com.ysu.reflect.Person");
Constructor c= clazz.getConstructor(int.class,String.class);
Person p = (Person) c.newInstance(23,"张三");
System.out.println(p);
----------------------------------------------------------------------------------
通过反射获取成员变量
Class clazz = Class.forName("com.ysu.reflect.Person");
Method method = clazz.getMethod("eat");
Person p = (Person) clazz.newInstance();
method.invoke(p);
Method method2 = clazz.getMethod("eat", int.class);
method2.invoke(p, 10);
------------------------------------------------------------------------------------
通过反射越过泛型检查(泛型擦除)
ArrayList<Integer> 中添加一个字符串对象
ArrayList<Integer> list = new ArrayList<>();
list.add(11);
//泛型只在编译器有效,在运行期会被擦除掉
// list.add("abc");
Class clazz = Class.forName("java.util.ArrayList"); //获取字节码对象
Method m = clazz.getMethod("add", Object.class);//获取add方法
m.invoke(list, "abc");
System.out.println(list);
--------------------------------------------------------------------------------
修改通用属性方法
package com.ysu.reflect;
import java.lang.reflect.Field;
public class Tool {
public void setProperty(Object obj,String propertyName,Object value) throws Exception, Exception{
// 获取字节码对象
Class clazz = obj.getClass();
// 暴力反射获取字段
Field f = clazz.getDeclaredField(propertyName);
f.setAccessible(true);
f.set(obj, value);
}
}
-------------------------------------------------------------------------------------
1、xxx.properties获取属性类
2、利用反射获取相关属性,并运行方法
-------------------------------------------------------------------------------------反射的动态代理
package com.ysu.reflect;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("1111");
// 執行被代理的Target
method.invoke(target, args);
System.out.println("2222");
return null;
}
}
-----------------------------------------------------------------------------------
package com.ysu.reflect;
import java.lang.reflect.Proxy;
public class Test4 {
public static void main(String[] args) {
UserImp ui = new UserImp();
//动态代理
// 本来自己要做的请别人做,被请的人就是代理对象
// 在程序运行过程中产生这个对象,而程序运行过程中产生的对象就是我们刚才反射讲解的内容,所以动态代理就是通过反射生成一个代理
// Proxy 和 InvocationHandler接口,通过类和接口可以生成动态代理对象
MyInvocationHandler m = new MyInvocationHandler(ui);
User u = (User) Proxy.newProxyInstance(ui.getClass().getClassLoader(), ui.getClass().getInterfaces(), m);
u.add();
u.delete();
}
}
-----------------------------------------------------------------------------------
末班模式
就是定义一个算法的骨架,将具体的算法延迟到子类实现,抽象类不希望被重写的方法用final修饰
如果有算法骨架需要被修改的话,有抽象类
------------------------------------------------------------------------------------
枚举抽象类
一共有三种方法
利用单例模式
class Week {
public staitc Week MON = new Week();
private Week(){}
}
class abstract Week2{
public staitc final Week MON = new Week(){
public void show(){
System.out.println();
}
}
private Week(){}
public abstract void show();
}
--------------------------------------------------------------
第一种 创建Enum类,直接写入对象就可以实现
第二种 public enum Week2{
MON("")
private String name;
private Week2(String name){
this.name = name;
}
}
//可以写入get set方法
测试 Week mon = Week.MON ;
第三种是写入方法,用匿名实现子类方法
public enum Week3{
MON(""){
public void show(){
System.out.println("xxxxxxx");
}
};
private String name;
private Week2(String name){
this.name = name;
}
}
测试 Week mon = Week.MON ;
mon.show();
注意 枚举项要放在第一行,枚举类可以有抽象方法,但是枚举项必须重写该方法
-----------------------------------------------------------------------------
枚举类的常见方法
ordinal() 返回枚举常量的序数 (枚举项都是有编号的)
compareTo() 枚举项比较的是编号
name() 获取枚举项的名称
toString() 也是打印名称,
valueOf Week mon = Week.value(Week.class,"MON");
System.out.println(mon);通过字节码文件
-------------------------------------------------------------------------------
JDK7的新特性
二进制字面量
数字字面量出现下划线
switch 语句可以用字符串
泛型简化,菱形泛型
异常的多个catch合并,每个异常就用或|
try with resources语句(自动关闭流)
-----------------------------------------------------------------------------
JDK1.8 接口中可以书写具有方法体的方法。如果不是静态方法 必须要用default修饰
用default修饰的就要采用实现类调用该方法,而静态方法可以接口名直接调用
局部内部类: