JAVA学习9-资源绑定、类加载器、反射、注解

81.资源绑定器

java.util包下提供了一个资源绑定器,便于获取属性配置文件的内容

这个资源绑定器只能获取类路径(src目录)下的属性配置文件(以.properties结尾的文件)

82.类加载器

类加载器:ClassLoad,是专门负责加载类的命令/工具

JDK中自带3个类加载器:

        启动类加载器(父加载器)rt.jar

        扩展类加载器(母加载器)ext/*.jar

        应用类加载器        classpath

假设有这样的代码:

String s="abc";

代码在开始执行之前会将所以需要的类全部加载到JVM中,通过类加载器加载,看到以上代码,类加载器会找到String.class文件,找到就加载;

加载过程:

        首先通过启动类加载器加载,启动类加载器专门加载:xxx/jdk/jre/lib/rt.jar;

        rt.jar中都是JDK最核心的类库

如果通过启动类加载器加载不到的时候,会通过扩展类加载器加载;

扩展类加载器专门加载:xxx/jdk/jre/lib/ext/*.jar;

如果通过扩展类加载器加载不到的时候,会通过应用类加载器加载;

应用类加载器专门加载:classpath中的jar包(class文件),即环境变量里面配置的jar包;

JAVA中为了保证类加载的安全,使用了双亲委派机制,优先从启动类加载器中加载,这个称为父,父无法加载到,再从扩展类加载器中加载,这个称为母,双亲委派,如果都加载不到,才会考虑从应用类加载器中加载,直到加载为止;

83.反射属性

        Class cls = Class.forName("ReflectTest.Movie");

        // 获取类中所有public修饰的field
        Field[] fs = cls.getFields();

        Field[] fs2 = cls.getDeclaredFields();

        for (Field field : fs2) {
            // 获取属性类型
            System.out.print(field.getType().getName() + "---");
            // 获取属性类型简称
            System.out.print(field.getType().getSimpleName() + "---");
            // 获取属性名称
            System.out.print(field.getName() + "---");
            // 获取属性的修饰符,每个修饰符是一个数字,每个数字是修饰符的代号
            int i = field.getModifiers();
            // 将数字代号转成修饰符字符串
            System.out.print(i + "---" + Modifier.toString(i));
            System.out.println();
        }

反编译field属性:

public static void test42() throws Exception {

        Class cls = Class.forName("java.util.Date");

        StringBuilder sb = new StringBuilder();
        sb.append("public class " + cls.getSimpleName() + " {\r\n");

        Field[] fs = cls.getDeclaredFields();
        for (Field field : fs) {
            sb.append("\t" + Modifier.toString(field.getModifiers()) + " ");
            sb.append(field.getType().getSimpleName() + " ");
            sb.append(field.getName() + ";\r\n");
        }
        sb.append("}");
        System.out.println(sb);
    }

84.通过反射机制访问一个对象的属性

给对象属性赋值的三要素:对象、属性、值;

/**
     * 可以通过反射机制,和配置文件,可以灵活的获取和设置对象的属性
     * 反射机制虽然让代码更复杂了,但是可以让程序更加灵活
     */
    public static void test43() throws Exception {
        Class cls = Class.forName("ReflectTest.Movie");
        Object obj = cls.newInstance();
        // 获取name属性
        Field nameField = cls.getDeclaredField("name");
        nameField.set(obj, "寒战");
        System.out.println(nameField.get(obj));

        // 获取私有属性,需要打破封装
        // 缺点:可能会给不法分子留下机会,这样设置之后外部也能访问私有属性
        Field directorField = cls.getDeclaredField("director");
        directorField.setAccessible(true);
        directorField.set(obj, "麦兆辉");
        System.out.println(directorField.get(obj));
    }

85.可变长度参数

语法:类型... args;

可变长度参数的个数是0-N个;

可变长度参数在参数列表中必须在最后一个位置上;

可变长度参数可以当成一个数组看待,也可以传入一个数组参数;

public static void testLength(int... args) {
        for (int i : args) {
            System.out.println("可变长度参数" + i);
        }
}

ReflectTest.Movie.testLength(10, 20, 30);
int[] arr = { 1, 2, 3, 4, 5 };
ReflectTest.Movie.testLength(arr);

86.通过反射机制访问一个类的方法

反编译方法:

public static void test45() throws Exception {
        Class cls = Class.forName("java.lang.String");
        Method[] mts = cls.getMethods();
        StringBuffer sb = new StringBuffer();
        sb.append(Modifier.toString(cls.getModifiers()) + " class " + cls.getSimpleName() + "{\r\n");
        for (Method method : mts) {
            // 获取权限修饰符
            sb.append("\t" + Modifier.toString(method.getModifiers()) + " ");
            // 获取返回值类型
            sb.append(method.getReturnType().getSimpleName() + " ");
            // 获取方法名
            sb.append(method.getName() + "(");
            // 获取参数类型数组
            Class[] paramTypes = method.getParameterTypes();
            for (Class pt : paramTypes) {
                // 获取参数类型字符串
                sb.append(pt.getSimpleName() + ",");
            }
            // 删除指定下标位置的字符
            sb.deleteCharAt(sb.length() - 1);
            sb.append("){}\r\n");
        }
        sb.append("}\r\n");

        System.out.println(sb);
    }

对象获取方法的4个要素:对象、方法名、参数列表、返回值类型;

反射机制调用方法:

public static void test46() throws Exception {
        Class cls = Class.forName("ReflectTest.Movie");

        // 获取Method
        Method mtd = cls.getMethod("buy", String.class, int.class);
        Object obj = cls.newInstance();
        Object retVal = mtd.invoke(obj, "3303271991", 1107);
        System.out.println(retVal);
}

反射机制,让代码更具有通用性,可变化的内容都是写在配置文件中的,将来修改配置文件之后,创建的对象就不一样了,调用的方法也不同了,但是java代码不需要做任何改动,这就是反射机制的魅力;

87.反射构造方法

反编译构造方法

public static void test47() throws Exception {
        Class cls = Class.forName("ReflectTest.Movie");
        StringBuffer sb = new StringBuffer();
        sb.append(Modifier.toString(cls.getModifiers()) + " class " + cls.getSimpleName() + "{\n\t");
        Constructor[] cs = cls.getDeclaredConstructors();
        for (Constructor constructor : cs) {
            sb.append("\t" + Modifier.toString(constructor.getModifiers()) + " " + cls.getSimpleName() + "(");
            Class[] csTypes = constructor.getParameterTypes();
            for (Class cst : csTypes) {
                sb.append(cst.getSimpleName() + ",");
            }
            if (csTypes.length > 0) {
                sb.deleteCharAt(sb.length() - 1);
            }
            sb.append("){}\n\t");
        }
        sb.append("}\n\t");
        System.out.println(sb);
    }

通过反射机制创建对象

public static void test48() throws Exception {
        Class cls = Class.forName("ReflectTest.Movie");
        // 调用无参构造方法
        Object obj = cls.newInstance();
        // 获取有参数的构造方法
        Constructor cs = cls.getDeclaredConstructor(String.class, double.class, String.class);
        Constructor cs2 = cls.getDeclaredConstructor(String.class, String.class, String.class, boolean.class);
        // 调用构造方法创建对象
        cs.newInstance("寒战2", 35.6, "买摘花");
        cs2.newInstance("无间道", "刘德华", "麦兆辉", true);
        Constructor cs3 = cls.getDeclaredConstructor();
        cs3.newInstance();
}

88.反射机制获取父类与接口

public static void test49() throws Exception {
        Class cls = Class.forName("java.lang.String");
        // 获取父类
        Class clsParent = cls.getSuperclass();
        System.out.println(clsParent.getName());
        // 获取所有实现的接口
        Class[] ims = cls.getInterfaces();
        for (Class class1 : ims) {
            System.out.println(class1.getName());
        }
    }

89.注解-annotation

注解,或者叫注释类型,也是一种引用类型,编译后生成.class文件

注解定义时的语法格式:

        [修饰符列表] @interface 注解类型名{}

注解使用时的语法格式:

    @注解类型名

注解可以出现在类上、方法上、属性上、变量上,形参上、枚举上等 。。。,注解还可以出现在注解类型上;

默认情况下,注解可以出现在任意位置;

@Override:这个注解只能注解方法,这个注解是给编译器参考的,和运行阶段没关系,凡是java中的方法带有这个注解的,编译器都会进行编译检查,如果这个方法不是重写父类的方法,编译器报错;

用来标注注解类型的注解,称为元注解;

下面的Targer就是元注解;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

常见的元注解:

        Targer:用来标注被标注的注解可以出现在哪些位置上;

        Retention:用来标注被注解的文件最终保存到哪里;

@Target(ElementType.METHOD)表示被标注的注解只能出现在方法上;

@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})表示该注解可以出现在构造方法上、字段上、局部变量上、方法上、包上、模块上、参数上、类上

@Retention(RetentionPolicy.SOURCE)表示该注解只被保留在java源文件中;

@Retention(RetentionPolicy.CLASS)表示该注解被保存在class文件中;

@Retention(RetentionPolicy.RUNTIME)表示该注解被保存在class文件中,并且可以被反射机制所读取;

@Deprecated这个注解标识的元素已过时,这个注解的作用主要是向其他程序员传达一个信息,告知已过时,有更好的解决方案存在;

注解里面如果有属性的话,必须给属性赋值,如果属性有设置默认值,可以不用赋值;

public @interface MovieAnnotation {

    /**
     * 我们通常在注解当中可以定义属性,一下这个是MovieAnnotation当中的name属性
     * 看着像方法,但我们称之为属性name
     * 电影名称
     * 
     * @return
     */
    String name();

    double price();

    int year() default 2009;
}

//使用
@MovieAnnotation(value = "国产电影", name = "寒战2", price = 22.5)
public static void doOther() {
        System.out.println("doOther");
}

如果一个注解的属性名是value,并且只有这个属性名的时候,那么该注解在使用的可以只写一个值;

public @interface MovieAnnotation {
    String value();
}

@MovieAnnotation("国产电影")

注解当中的属性类型:byte,short,int,long,char,boolean,float,double,String,枚举,Class,与及以上每种数据类型的数组形式;

注解的数组用大括号:

public @interface MovieAnnotation {

    String name();

    int year();

    String[] players();
}

@MovieAnnotation(name = "寒战2", year = 2015, players = { "郭富城", "刘家辉" })
public static void doOther() {
    System.out.println("doOther");
}

//如果数组中只有一个元素,大括号可以省略
@MovieAnnotation(name = "寒战2", year = 2015, players = "郭富城")
public static void doOther2() {
    System.out.println("doOther");
}

枚举类型的使用:

public @interface MovieAnnotation {
    String name();

    Mtype[] mts();
}

enum Mtype {
    爱情,
    战争,
    悬疑
}

@MovieAnnotation(name = "寒战2",mts = {Mtype.爱情,Mtype.悬疑})
public static void doOther3() {}

@MovieAnnotation(name = "寒战2", mts = Mtype.爱情)
public static void doOther4() {}

//数组只有一个元素
public @interface MovieAnnotation {
    Mtype[] value();
}

@MovieAnnotation({ Mtype.爱情, Mtype.悬疑 })
public static void doOther3() {}

@MovieAnnotation(Mtype.爱情)
public static void doOther4() {}

通过反射获取注解

//标注该注解只能使用在方法和类上
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
// 标注该注解可以被反射
@Retention(RetentionPolicy.RUNTIME)
public @interface MovieAnnotation {

    String name() default "电影注解咚咚咚";

    int year() default 2018;
}

@Deprecated
@MovieAnnotation(name = "后天")
public class MovieAnnotationTest {}

public static void test50() throws Exception {
        Class stringClass = Class.forName("ReflectTest.MovieAnnotationTest");
        // 先判断类上是否有MovieAnnotation注解
        if (stringClass.isAnnotationPresent(MovieAnnotation.class)) {
            // 获取注解类
            MovieAnnotation ma = (MovieAnnotation) stringClass.getAnnotation(MovieAnnotation.class);
            System.out.println("注解:" + ma);
            // 获取注解的属性值
            System.out.println(ma.name());
        }

        // 获取方法上的注解属性
        Method mtd = cls.getDeclaredMethod("doSome");
        if (mtd.isAnnotationPresent(MovieAnnotation.class)) {
            MovieAnnotation ma2 = mtd.getAnnotation(MovieAnnotation.class);
            System.out.println(ma2.name());
            System.out.println(ma2.year());
        }
}

注解标识的类必须包含int id属性,否则抛出异常,demo

//该注解标识的类必须包含int id属性,否则抛出异常
//只能要来标识类
@Target(ElementType.TYPE)
// 编译后保留在class文件中,并且可以被反射
@Retention(RetentionPolicy.RUNTIME)
public @interface HasIdAnnotation {}

@HasIdAnnotation
public class TestAnnotation {
    int id;
    String name;
    int age;
}

public static void test51() throws Exception {
        Class cls = Class.forName("ReflectTest.TestAnnotation");
        if (cls.isAnnotationPresent(HasIdAnnotation.class)) {
            boolean flag = false;
            Field[] fs = cls.getDeclaredFields();
            for (Field field : fs) {
                if ("id".equals(field.getName()) && "int".equals(field.getType().getSimpleName())) {
                    flag = true;
                }
            }
            if (!flag) {
                throw new NotHasIdException("该类必须包含int id字段");
            }
        }
}

你可能感兴趣的:(java,后端)