1.反射
正向:知道了类创建了对象,过程:先了解获得了类,进而进行编写代码
逆向:获取了对象,怎么根据对象获取此对象的描述类 知道类名(String)怎么创建对象
类的模板对象 也是一个对象 Class
类也是对象,类的类型是Class
public class Test {
public static void main(String[] args) {
// 类也是对象,类的类型时Class
Class c = String.class;
Class c2 = Test.class;
}
}
2.类加载器(ClassLoader
):一个抽象类
2.1、Java类加载器:
注 : 以jdk8为参考
1.第一种是Bootstrap Loader(引导类加载器)。
它的实现依赖于底层操作系统,由C编写而成,没有继承于ClassLoader
类。根类加载器从系统属性sun.boot.class.path所指定的目录中加载类库。默认为jre目录下的lib目录下的class文件,该加载器没有父加载器。负责加载虚拟机的核心类库,如java.lang.*。Object类就是由根类加载器加载的。
2.第二种是Extended Loader(标准扩展类加载器)。
它的父加载器为根类加载器。由java编写而成,是ClassLoader的子类。它从java.ext.dirs中加载类库,或者从JDK安装目录jre\lib\ext子目录下加载类库。如果把用户创建的jar文件放在该目录下,也会自动由扩展类加载器加载。
3.第三种是AppClass Loader(应用程序类路径类加载器)。
它的父加载器为扩展类加载器。由java编写而成,是ClassLoader的子类,它从环境变量classpath或者系统属性java.class.path所指定的目录中加载类,是用户自定义的类加载器的默认父加载器。
参考资料:
Java类加载器
代码案例:
public class Test {
public static void main(String[] args) {
// 类也是对象,类的类型时Class
Class c = String.class;
Class c2 = Test.class;
// 类名是完整名
System.out.println(c.getName());
System.out.println(c2.getName());
// getClassLoader : 获得类加载器
ClassLoader classLoader = c.getClassLoader();
System.out.println(classLoader);
ClassLoader classLoader1 = c2.getClassLoader();
System.out.println(classLoader1); //jdk.internal.loader.ClassLoaders$AppClassLoader@1f89ab83 $AppClassLoader:内部子类
System.out.println("---------");
//getParent() : 类加载器的父亲
ClassLoader parent = classLoader1.getParent();
System.out.println(parent);
System.out.println("=======");
ClassLoader parent1 = classLoader.getParent();
System.out.println(parent1);
System.out.println("^^^^^^^^^");
ClassLoader parent2 = parent.getParent();
System.out.println(parent2);
}
}
2.2、类的加载机制:自上而下
2.3、双亲委派模型
双亲委派模型是Java加载类的机制:父类加载器先加载,加载不到后代类加载器再去加载;加载到后代不再重复加载。
参考资料:
Java类加载机制
类在内存中有且只有一份
如何破坏一个类只记载一次:打破默认的双亲委派模型,自定类加载器就不往上委托,自己去加载为什么打破:因为有一些热部署的场景。
编程时加载类的触发代码有几种情况:类名.Class
Class.forName("类名")
创建对象
3.学习使用反射
3.1、解析代码
1.获取类的 Class 对象实例
Class pc = Class.forName("com.bigdat.demo3.Pet");
2.根据 Class 对象实例获取 DeclaredField 对象
Field name1 = pc.getDeclaredField("name");
3.使用 c1 对象的 newInstance 方法获取反射类对象
Dog dog = c1.newInstance();
4.利用 invoke 方法调用方法 :调用
int sum = (int) add.invoke(dog2,2, 5);
5.反射 : 使用 setAccessible() 可以临时改变访问权限,就可以获取私有成员变量的值。
// 通过反射得到age字段
Field age = aClass.getDeclaredField("age");
// 反射 : 使用 setAccessible() 可以临时改变访问权限,就可以获取私有成员变量的值。
age.setAccessible(true); // true:可以访问任何权限
6.getModifiers()方法返回int类型值表示该字段的修饰符。
System.out.println(mm.getModifiers()); // 返回数字
3.2、反射案例:有无Declared的区别
Declared:获取本类的方法,字段 ,没有Declared:获取本类及父类的方法,字段
public class Test {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
// 创建对象使用new关键字
Class<Dog> aClass = (Class<Dog>) Class.forName("com.bigdata09.day36.demo3.Dog");
// 第一种
Dog dog = aClass.newInstance();
dog.show();
System.out.println("----------------");
// 第二种 通过反射构造器对象来创建实例
Constructor<Dog> constructor = aClass.getConstructor(); //getConstructor() 与 getDeclaredConstructor() Declared:获取本类的方法,字段 ,没有Declared:获取本类及父类的方法,字段
Dog dog1 = constructor.newInstance();
dog1.show();
System.out.println("==============");
Constructor<Dog> declaredConstructor = aClass.getConstructor(int.class, String.class);
Dog dog2 = declaredConstructor.newInstance(2, "哈士奇");
dog2.show();
System.out.println("^^^^^^^^^^^^^^^^");
dog2.num = 1;
dog2.show();
System.out.println("------------");
// 通过反射得到age字段
Field age = aClass.getDeclaredField("age");
// 反射 : 使用 setAccessible() 可以临时改变访问权限,就可以获取私有成员变量的值。
age.setAccessible(true);
age.set(dog2,3);
dog2.show();
System.out.println("=======");
// Field : 字段
Field name = aClass.getField("num"); // getDeclaredField() 与 getField()
name.setAccessible(true);
name.set(dog2,1212);
dog2.show();
System.out.println("~~~~~~~~~~~");
Class pc = Class.forName("com.bigdata09.day36.demo3.Pet");
Field name1 = pc.getDeclaredField("name"); // getDeclaredField() 与 getField
name1.setAccessible(true);
name1.set(dog2,"易建联");
dog2.show();
// 上面 : 访问字段
System.out.println("@@@@@@@@@@@@");
Method show = aClass.getDeclaredMethod("show");
// invoke() : 调用
show.invoke(dog2);
System.out.println("========");
Method add = aClass.getDeclaredMethod("add", int.class, int.class);
// 反射
add.setAccessible(true);
int sum = (int) add.invoke(dog2, 3, 6);
System.out.println("总和 : "+sum);
// 构造方法 成员变量 普通方法 注解?
Class c2 = Class.forName("com.bigdata09.day36.demo3.Pet");
System.out.println("\n\n");
System.out.println("----------");
Field[] fields = c2.getFields(); //public 包含继承的public
for (Field f : fields) {
System.out.println(f);
}
System.out.println("%%%%%%%%%%%%%%");
Field[] declaredFields = c2.getDeclaredFields(); // 自己类定义的所有
for (int i = 0; i < declaredFields.length; i++) {
System.out.println(declaredFields[i]);
}
System.out.println("**************");
// Method : 方法
Method[] methods = aClass.getMethods();
for (Method m :
methods) {
System.out.println(m);
}
System.out.println("------------------");
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method mm :
declaredMethods) {
// getModifiers() : 打印修饰符
System.out.println(mm.getModifiers());
System.out.println(mm);
System.out.println();
}
// 接口
Class[] interfaces = aClass.getInterfaces();
for (Class c :
interfaces) {
System.out.println(c);
}
}
}
interface Eat{}
class Pet{
private String name;
public int num;
protected int aaa;
int mm;
public Pet() {
}
public void show(){
System.out.println(name+" " + num);
}
private void foo(){
}
protected void abc(){}
}
class Dog extends Pet implements Eat{
private int age;
public String sex;
public Dog() {
}
public Dog(int age, String sex) {
this.age = age;
this.sex = sex;
}
@Override
public void show() {
super.show();
System.out.println(age +" "+sex);
}
private int add(int i, int n){
return i+n;
} // private : 2
void f(){ // 默认 : 0
}
protected void f2(){ //protected : 4
}
static void f3(){} // static : 8
final void f4(){} // final : 16
public static void f5(){} //public static : 9
private static void f6(){} // private static : 10
}
4.注解
一定程度上可以说 :框架=注解+反射+设计模式。
注解:文档注解(SOURCE) 编译注解(CLASS) 运行注解(RUNTIME)
参考资料:
注解
@Retention注解
@Retention注解注解用于指定被修饰的注解可以保留多长时间,即指定JVM策略在哪个时间点上删除当前注解。保留策略值有以下三个:
// 注解
public class Test {
// @注解 也是一个类 只有信息 所以注解是类的元数据 metadata:元数据
// xml @
// 保留范围:文档注解(SOURCE) 编译注解(CLASS) 运行注解(RUNTIME)
// 可注解的位置:
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
Class c = Class.forName("com.bigdata09.day36.demo4.A");
// 获取A上的注解
Annotation[] annotations = c.getAnnotations();
for (Annotation a :
annotations) {
System.out.println(a);
}
System.out.println("-------------------");
Method[] methods = c.getMethods();
for (int i = 0; i < methods.length; i++) {
Method m = methods[i];
System.out.println(m);
for (Annotation a:
m.getAnnotations()) {
System.out.println(a);
}
}
System.out.println("==================");
Method f = c.getMethod("f");
MySecond annotation = f.getAnnotation(MySecond.class);
System.out.println(annotation);
}
}
@Retention(RetentionPolicy.RUNTIME)
// 注解到方法上
@Target(ElementType.METHOD)
@interface MySecond{
int value(); // 对应下面注解里面的参数
}
//@MyFirst
class A{
@MyFirst
public int i;
@MyFirst
@MySecond(13111)
public void f(){
}
}
// 注解到方法(METHOD),字段(FIELD)上
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyFirst {
}