ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

枚举、注解、反射和代理模式

2020-03-29 15:58:03  阅读:150  来源: 互联网

标签:反射 String System public Person 枚举 注解 class out


一、枚举类

1、枚举类的使用

类的对象只有有限个,确定的,比如季节:春季、夏季、秋季、冬季,当需要一组常量时,强烈建议使用枚举类。

枚举类的实现

  • jdk1.5以前需要自定义枚举类;
  • jdk1.5新增enum关键字用于定义枚举类

若枚举只有一个对象,则可以作为一种单例模式的实现方法。

在jdk1.5之前如何自定义枚举类?

  • 私有化类的构造器,保证不能在类的外部创建对象;
  • 在类的内部创建枚举类的实例,声明为public static final;
  • 对象如果有实例变量,应该声明为pribate final,并在构造器中初始化。
package com.lwj.enumtest;


public class SeasonTest {
    public static void main(String[] args) {
        Season spring = Season.SPRING;
        System.out.println(spring);
        //Season{seasonName='春天', seasonDesc='春暖花开'}
    }
}

//自定义枚举类
class Season {
    private final String seasonName;
    private final String seasonDesc;
    private Season(String seasonName, String seasonDesc) {
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }
    public static final Season SPRING = new Season("春天", "春暖花开");
    public static final Season SUMMER = new Season("夏天", "夏日炎炎");
    public static final Season AUTUMN = new Season("秋天", "秋高气爽");
    public static final Season WINTER = new Season("冬天", "白雪皑皑");

    //获取枚举类对象的属性
    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDesc() {
        return seasonDesc;
    }

    @Override
    public String toString() {
        return "Season{" +
                "seasonName='" + seasonName + '\'' +
                ", seasonDesc='" + seasonDesc + '\'' +
                '}';
    }
}

jdk1.5之后使用enum关键字定义枚举类

  • 使用enum定义的枚举类默认继承了java.lang.Enum类,因此不能再继承其他类;
  • 枚举类的构造器只能使用private关键字;
  • 枚举类的所有实例必须在枚举类中显式列出(,分隔 ;结尾),列出的实例系统会自动添加public static final修饰;
  • 必须在枚举类的第一行声明枚举类对象;
  • jdk1.5中可以在switch表达式中使用Enum定义的枚举类的对象作为表达式,case语句中可以直接使用枚举值的名字,无需添加枚举类作为限定。

使用jdk1.5以前自定义枚举类时,把Object当作父类,重写toString()方法,而jdk1.5以后使用enum关键字定义枚举类,把java.lang.Enum当作父类,不需要重写toString()方法。

package com.lwj.enumtest;


public class SeasonEnumTest {
    public static void main(String[] args) {
        System.out.println(SeasonEnum.SPRING);
        //SPRING

        System.out.println(SeasonEnum.class);
        //class com.lwj.enumtest.SeasonEnum
        System.out.println(SeasonEnum.class.getSuperclass());
        //class java.lang.Enum
    }
}

enum SeasonEnum {
    SPRING("春天", "春暖花开"),
    SUMMER("夏天", "夏日炎炎"),
    AUTUMN("秋天", "秋高气爽"),
    WINTER("冬天", "白雪皑皑");

    private final String seasonName;
    private final String seasonDesc;

    private SeasonEnum(String seasonName, String seasonDesc) {
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }

    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDesc() {
        return seasonDesc;
    }

    //这次没有重写toString()方法
}

switch的参数类型可以为byte、short、int、char、Enum、String,其中Enum是jdk1.5以后新增的,String为jdk1.7新增的特性。

  • case 后面跟的必须是常量;
  • 执行顺序,先case判断,如果匹配成功,执行代码,直到break。

当switch的参数类型为枚举,case 枚举类型:,而不是case 枚举类.枚举类型,比如下面的例子:

package com.lwj.enumtest;


public enum TestEnum {
    TEST_ONE("name1", "one"),
    TEST_TWO("name2", "two"),
    TEST_THREE("name3", "three");

    private final String key;
    private final String value;
    private TestEnum(String key, String value) {
        this.key = key;
        this.value = value;
    }

    public String getKey() {
        return key;
    }

    public String getValue() {
        return value;
    }
}

class Test$Enum {
    public static void main(String[] args) {
        TestEnum t = TestEnum.TEST_ONE;
        switch (t) {
            case TEST_ONE:
                System.out.println(t.getKey() + ":" + t.getValue());
                break;
            case TEST_TWO:
                System.out.println(t.getKey());
                break;
            default :
                System.out.println(t);
        }
    }
}

当switch的参数类型是一个String类型时,比如下面的例子:

package com.lwj.enumtest;


public class TestString {
    public static void main(String[] args) {
        String a = "aaa";
        switch (a) {
            case "aaa":
                System.out.println("匹配aaa");
                break;
            case "bbb":
                System.out.println("匹配bbb");
                break;
            default :
                break;
        }
    }
}

Enum类中的常用方法

  • values():返回枚举类型的对象数组;
  • valueOf(String str):把一个字符串转为相应的枚举类对象,要求字符串必须是枚举类对象的名字;
  • toString():返回当前枚举类对象常量的名称;
//Enum类的常用方法
System.out.println(Arrays.toString(SeasonEnum.values()));
//[SPRING, SUMMER, AUTUMN, WINTER]
System.out.println(SeasonEnum.valueOf("WINTER"));
//WINTER
System.out.println(SeasonEnum.SPRING);
//SPRING

使用enum关键字定义的枚举类实现接口

  • 和普通Java类一致,枚举类可以实现一个或多个接口;
  • 若需要每个枚举值在调用实现的接口方法时,呈现出不同的行为方式,则可以让每个枚举值分别实现该方法。
interface Info {
    void show();
}

enum SeasonEnum implements Info{
    SPRING("春天", "春暖花开") {
        @Override
        public void show() {
            System.out.println("SPRING");
        }
    },
    SUMMER("夏天", "夏日炎炎") {
        @Override
        public void show() {
            System.out.println("SUMMER");
        }
    },
    AUTUMN("秋天", "秋高气爽") {
        @Override
        public void show() {
            System.out.println("AUTUMN");
        }
    },
    WINTER("冬天", "白雪皑皑") {
        @Override
        public void show() {
            System.out.println("WINTER");
        }
    };

    private final String seasonName;
    private final String seasonDesc;

    private SeasonEnum(String seasonName, String seasonDesc) {
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }

    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDesc() {
        return seasonDesc;
    }

    //这次没有重写toString()方法
}

二、注解

1、注解的概述

  • 从jdk1.5开始,Java增加了对元数据(MetaData)的支持,也就是Annotation注解;
  • Annotation就是代码中的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理;
  • Annotation可以像修饰符一样被使用,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明,这些信息被保存在Annotation的“name = value”对中;
  • 注解的本质就是一个继承了 Annotation 接口的接口。

在JavaSE中,注解的使用比较简单,例如标记方法过时,忽略警告等,在JavaEE/Android中,注解占据了重要角色,例如用于配置应用程序的切面,代替JavaEE旧版中的XML配置;
未来的开发模式都是基于注解的,注解是一种趋势,在一定程度上可以说,框架 = 注解 + 反射 + 设计模式

解析一个类或方法的注解有两种形式,一种是编译期的扫描,一种是=运行期反射,先讨论注解的编译期扫描,

@Override
public String toString() {
    return "Hello Annotation";
}

@Override注解告诉编译器这个方法是一个重写方法,如果父类中不存在此方法,编译器就会报错,如果我不加@Override注解,并且toString()方法错写成toStrring(),那么执行结果和期望有偏差,所以使用注解有利于提高程序的可读性。

2、常见的Annotation示例

使用Annotation时要在其前面增加@符号,并把@Annotation当成一个修饰符使用。

示例一:生成文档相关的注解

  • @author:标明开发该类的作者,多个作者之间用,分隔;
  • @version:标明该类的版本;
  • @see:参考转向;(参考其他类)
  • @since:从哪个版本开始增加;
  • @param:对方法中参数的说明;(@param 形参名 形参类型 形参说明)
  • @return:对方法返回值的说明;(@return 返回值类型 返回值说明)
  • @exception:对方法抛出的异常进行说明;(@exception 异常类型 异常说明)
  • @param和@exception可以并列多个。
package com.lwj.annotest;

import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * @author shawn
 * @version 1.0
 * @date 2020/3/3 12:58
 * @see Math
 */
public class JavaDocTest {
    /**
     * 程序的入口
     * @param args String[] 命名行参数
     */
    public static void main(String[] args) {
    }

    /**
     * 求圆的面积
     * @param radius double 半径值
     * @return double 圆的面积
     * @throws IOException IO异常
     */
    public static double getArea(double radius) throws IOException {
        try {
            throw new FileNotFoundException();
        } finally {
            return Math.pow(radius, 2.0) * Math.PI;
        }        
    }
}

示例二:在编译时进行格式检查(jdk内置的三个基本注解)

  • @Override:限定重写父类方法,该注解只能用于方法;
  • @Deprecated:表示所修饰的元素(类、方法)已经过时,通常是因为有更好的选择;
  • @SuppressWarnings:抑制编译器警告;
package com.lwj.annotest;

public class AnnotationTest {
    public static void main(String[] args) {
        @SuppressWarnings("unused")
        int a = 10;
        new AnnotationTest().print();
    }
    
    @Deprecated
    public void print() {
        System.out.println("过时的方法");
    }
    
    @Override
    public String toString() {
        return "重写的toString";
    }
}

示例三:替代配置文件的功能

Servlet 3.0中提供了注解,不再需要在web.xml中配置Servlet,@WebServlet("/login")。

Spring框架 @Bean...

3、自定义注解

  • 定义新的Annotation类型使用@interface关键字;
  • 自定义注解自动继承java.lang.annotation.Annotation接口;
  • (接口中的方法,默认权限修饰符为public,所以不考虑,只考虑方法名,返回值类型和参数)
  • 注解只有成员变量,没有方法,注解的成员变量以无参数的方法来声明,其方法名对应成员变量名,返回值类型对应成员变量类型,我们称之为配置参数,类型只能是八种基本数据类型、String类型、Class类型、enum类型、Annotation类型、以上所有类型的数组;
  • 可以在定义注解的成员变量时为其指定初始值,指定成员变量的初始值使用default关键字;
  • 如果只有一个参数成员,建议使用参数名为value;
  • 在使用时,格式:"参数名 = 参数值",如果只有一个参数成员,且名称为value,可以省略"value = ";
  • 没有成员变量的注解称为标记,包含成员变量的注解称为元数据注解;
  • 注解是不支持继承的,因为注解在编译后,编译器会自动继承java.lang.annotation.Annotation。

4、jdk中的元注解

  • jdk的元注解用于修饰其他注解的定义;
  • jdk1.5提供了4个标准的mata-annotation类型,分别是:@Retention、@Target、@Documented、@Inherited;

@Retention

指定该Annotation的生命周期,@Retention包含一个RetentionPolicy类型的成员变量,使用@Retention时必须为该value成员变量指定值;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {

    RetentionPolicy value();
}
public enum RetentionPolicy {
    SOURCE,
    CLASS,
    RUNTIME
}

SOURCE:源文件中有效;

CLASS:class文件中有效,并不会加载到JVM中,如Java内置注解,@Override,@Deprecated,(编译器检查),默认值;

RUNTIME:运行时有效,当运行Java程序时,JVM会保留注解,程序可以通过反射获取该注解,如SpringMVC中的@Controller、@RequestMapping、@Autowired。

@Target

指定被修饰的Annotation可以作用于哪些程序元素上;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    
    ElementType[] value();
}
public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE, //类、接口、枚举

    /** Field declaration (includes enum constants) */
    FIELD, //属性

    /** Method declaration */
    METHOD, //方法

    /** Formal parameter declaration */
    PARAMETER, //方法参数

    /** Constructor declaration */
    CONSTRUCTOR, //构造方法

    /** Local variable declaration */
    LOCAL_VARIABLE, // 局部变量

    /** Annotation type declaration */
    ANNOTATION_TYPE, //注解类型

    /** Package declaration */
    PACKAGE, //包

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}

@Document

指定修饰的Annotation将被javadoc工具提取成文档;

@Inherited

Inherited 是继承的意思,但是它并不是说注解本身可以继承,而是说如果一个超类使用了@Inherited 注解,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。使用较少。

使用最多的是@Retention(RetentionPolicy.RUNTIME) 和 @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})这两个注解。

数组使用大括号。

5、通过反射获取注解信息

等到反射再讲

package com.lwj.annotest;

import java.lang.annotation.*;

@MyAnnotation(value = "Byte Dancing")
public class MyAnnotationTest {
    @MyAnnotation("西安")
    private String name;

    @MyAnnotation
    public static void main(String[] args) {
        
    }
}

@MyAnnotation("哔哩哔哩")
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
    String value() default "MyAnnotation";
}

6、jdk8.0注解新特性

可重复注解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
    
    Class<? extends Annotation> value();
}
  • 在MyAnnotation上声明@Repeatable,成员变量value为MyAnnotations.class;
  • @MyAnnotation和@MyAnnotations上的@Target和@Retention相同。
package com.lwj.annotest;

import java.lang.annotation.*;

@MyAnnotation(value = "JD")
@MyAnnotation(value = "Byte Dancing")
public class MyAnnotationTest {
    @MyAnnotation("西安")
    private String name;

    @MyAnnotation
    public static void main(String[] args) {

    }
}

@Repeatable(MyAnnotations.class)
@MyAnnotation("哔哩哔哩")
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
    String value() default "MyAnnotation";
}


@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotations {
    MyAnnotation[] value();
}

再举一个例子,一个人有很多种角色,父亲,儿子,朋友,等等。

@Target(ElementType.TYPE) 
@Retention(RetentionPolicy.RUNTIME)
public @interface Persons {
	Person[] value();
}

@Persons注解可以给一个类进行注解。

@Repeatable(Persons.class)
public @interface Person{
	String role() default "";
}

在@Person注解上使用@Repeatable注解,标注是@Person是可重复的。

使用:

@Person(role="CEO")
@Person(role="husband")
@Person(role="father")
@Person(role="son")
public class Man {
	String name="";
}

类型注解

jdk8.0之后,关于元注解@Target的参数类型ElementType枚举值多了两个,TYPE_PARAMETER、TYPE_USE。

三、反射

1、Java反射机制概述

  • 反射机制允许程序在执行期通过Reflection API取得任何类的内部信息,并能直接操作任何对象的内部属性和方法;
  • 加载完类后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息,这个对象就像一面镜子,透过这个镜子可以看到类的结构,所以我们称为反射。

反射相关的主要API

  • java.lang.Class:代表一个类
  • java.lang.reflect.Method:代表类的方法
  • java.lang.reflect.Field:代表类的成员变量
  • java.lang.reflect.Constructor:代表类的构造器
  • ...
package com.lwj.java;


public class Person {
    private String name;
    public int age;

    public Person() {
    }

    private Person(String name) {
        this.name = name;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void show() {
        System.out.println("show()方法");
    }

    private String showNation(String nation) {
        System.out.println("我的国籍" + nation);
        return nation;
    }
}
package com.lwj.java;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectTest {
    public static void main(String[] args) throws Exception{
        Person p = new Person("张三", 22);
        p.age = 10;
        p.show();
        //反射之前,在Person类外部不能通过Person类的对象调用内部私有结构
        //比如name属性,私有构造器,showNation()方法
        //封装性的限制

        //反射之后
        Class<Person> clazz = Person.class;
        Constructor<Person> constructor = clazz.getConstructor(String.class, int.class);
        Person person = constructor.newInstance("张三", 22);
        Field age = clazz.getDeclaredField("age");
        age.set(person, 10);
        Method show = clazz.getDeclaredMethod("show");
        show.invoke(person);
        
        //反射的强大--->调用类的内部结构
        Field name = clazz.getDeclaredField("name");
        name.setAccessible(true);
        name.set(person, "李四"); //访问私有属性
        System.out.println(person.getName()); //李四
        
        Constructor<Person> declaredConstructor = clazz.getDeclaredConstructor(String.class);
        declaredConstructor.setAccessible(true);
        Person person1 = declaredConstructor.newInstance("王五"); //私有构造器创建对象
        System.out.println(person1.getName()); //王五

        Method showNation = clazz.getDeclaredMethod("showNation", String.class);
        showNation.setAccessible(true);
        showNation.invoke(person1, "China"); //执行私有方法
    }
}

如何看待反射和封装性之间的矛盾?

不矛盾。

2、理解Class类并获取Class类的实例

关于类的加载过程:

程序经过javac.exe命令后,生成字节码文件(.class),使用java.exe命令对某个字节码文件进行解释运行,相当于将某个字节码文件加载到内存中,此过程称为类的加载。

加载到内存中的类,称为运行时类,作为Class类的实例。

获取Class实例的四种方式:

public void test() {
    //方式一:调用运行时类的属性
    Class<Person> clazz = Person.class;
    System.out.println(clazz); //class com.lwj.java.Person
    //方式二:通过运行时类的对象
    Person p = new Person();
    Class<? extends Person> clazz1 = p.getClass();
    System.out.println(clazz1); //class com.lwj.java.Person
    System.out.println(clazz == clazz1); //true
    //方式三:调用Class的静态方法
    try {
        Class<?> clazz2 = Class.forName("com.lwj.java.Person");
        System.out.println(clazz2 == clazz); //true
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    //方式四:类的加载器
    try {
        Class<?> clazz3 = ReflectTest.class.getClassLoader().loadClass("com.lwj.java.Person");
        System.out.println(clazz3 == clazz); //true
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

Class类

  • Class对象只能由系统建立对象;
  • 一个加载的类在JVM中只会有一个Class实例;
  • 一个Class对象对应的是一个加载到JVM的中的一个.class文件;

哪些类型可以有Class对象?

  • class(外部类、成员(成员内部类、静态内部类)、局部内部类、匿名内部类)
  • interface接口
  • []数组
  • enum枚举
  • annotation注解
  • 基本数据类型
  • void
public void test1() {
    Class<Object> c1 = Object.class;
    System.out.println(c1); //class java.lang.Object
    Class<Comparable> c2 = Comparable.class;
    System.out.println(c2); //interface java.lang.Comparable
    Class<String[]> c3 = String[].class;
    System.out.println(c3); //class [Ljava.lang.String;   L表示对象类型
    Class<int[][]> c4 = int[][].class;
    System.out.println(c4); //class [[I
    Class<ElementType> c5 = ElementType.class;
    System.out.println(c5); //class java.lang.annotation.ElementType
    Class<Override> c6 = Override.class;
    System.out.println(c6); //interface java.lang.Override
    System.out.println(c6.getSuperclass()); //null
    Class<Integer> c7 = int.class;
    System.out.println(c7); //int
    Class<Void> c8 = void.class;
    System.out.println(c8); //void
    Class<Class> c9 = Class.class;
    System.out.println(c9); //class java.lang.Class

    int[] a = new int[10];
    int[] b = new int[100];
    Class<? extends int[]> aClass = a.getClass();
    Class<? extends int[]> bClass = b.getClass();
    System.out.println(aClass == bClass); //true
    //只要数组的元素类型和维度一样,就是一个Class
}

3、类的加载和ClassLoader的理解

在这里插入图片描述在这里插入图片描述

package com.lwj.java;


public class ClassLoadingTest {
    public static void main(String[] args) {
        System.out.println(A.m);
        //100
    }
}

class A {
    static {
        m = 300;
    }
    static int m = 100;
}
//第二步:链接结束后 m=0
//第三步:初始化后, m 的值由 <clinit> 方法执行决定
//这个 A 的类构造器 <clinit> 方法由类变量的赋值和静态代码块中的语句按照顺序合并
//产生,类似于
// <clinit>(){
// m = 300;
// m = 100;
// }

在这里插入图片描述

package com.lwj.java;


public class ClassLoadingTest {
    public static void main(String[] args) {
        //主动引用:一定会导致类A和Father的初始化
        // A a = new A();
        // Class.forName("com.lwj.java.A");
        // System.out.println(A.m);

        //被动引用
        A[] array = new A[5];
        //不会导致类A和Father的初始化
        System.out.println(A.b);
        //只会初始化Father
        System.out.println(A.M);
        //不会导致A和Father的初始化
        
        
        //main所在的类:main方法所在的类主动初始化
        //父类被加载:调用A.b,初始化父类,而非子类
        //2
        //1:不会初始化A和Father
    }

    static {
        System.out.println("main所在的类");
    }
}

class Father {
    static int b = 2;

    static {
        System.out.println("父类被加载");
    }
}

class A extends Father{
    static {
        System.out.println("子类被加载");
        m = 300;
    }
    static int m = 100;
    static final int M = 1;
}

在这里插入图片描述

package com.lwj.java;


public class ClassLoadingTest {
    public static void main(String[] args) throws ClassNotFoundException {
        ClassLoader classLoader = ClassLoadingTest.class.getClassLoader();
        System.out.println(classLoader);
        //获取当前自定义类ClassLoadingTest的类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);
        //获取系统类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);
        //获取系统类加载器的父类加载器,即扩展类加载器:sun.misc.Launcher$ExtClassLoader@1b6d3586
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);
        //获取扩展类加载器的父类加载器,即引导类加载器:null,不是没有,而是我们获取不到

        ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(classLoader1);
        //查看Object类是由哪个类加载器加载:null

        //关于类加载器的一个重要方法:getResourceAsStream(String str),获取类路径下指定文件的输入流
        ClassLoadingTest.class.getClassLoader().getResourceAsStream("");
    }
}

4、创建运行时类的对象

在这里插入图片描述

package com.lwj.java;


import java.lang.reflect.Constructor;

public class NewInstanceTest {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.lwj.java.Person");
        Object obj = clazz.newInstance();
        System.out.println(obj);
        //Person{name='null', age=0} clazz.newInstance(),调用空参构造器,创建对象

        Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(String.class, int.class);
        Object lisi = declaredConstructor.newInstance("李四", 22);
        System.out.println(lisi);
        //Person{name='李四', age=22}
    }
}

5、获取运行时类的完整结构

Field、Method、Constructor、SuperClass、Interface、Annotation。

1、提供更丰富的Person类

接口:

package com.lwj.java1;


public interface MyInterface {
    void info();
}

注解:

package com.lwj.java1;


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "Hello";
}

父类:

package com.lwj.java1;


import java.io.Serializable;

public class Creature<T> implements Serializable {
    private char gender;
    public double weight;

    private void breath() {
        System.out.println("生物呼吸");
    }

    public void eat() {
        System.out.println("生物吃东西");
    }
}

Person类:

package com.lwj.java1;

@MyAnnotation("Person")
public class Person extends Creature<String> implements Comparable<String>, MyInterface {

    private String name;
    int age;
    public int id;

    public Person() {
    }

    @MyAnnotation("constructor")
    private Person(String name) {
        this.name = name;
    }

    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @MyAnnotation
    private String show(String nation) {
        System.out.println("我的国籍是" + nation);
        return nation;
    }

    public String display(String interesting) {
        return interesting;
    }

    @Override
    public int compareTo(String o) {
        return 0;
    }

    @Override
    public void info() {
        System.out.println("MyInterface接口的info方法");
    }
}

2、Field

Field[] clazz.getFields():获取父类、本类中声明为Public的属性

Field[] clazz.getDeclaredFields():获取本类中声明的所有属性

package com.lwj.java2;


import com.lwj.java1.Person;

import java.lang.reflect.Field;

public class FieldTest {
    public static void main(String[] args) {
        Class<Person> personClass = Person.class;
        Field[] fields = personClass.getFields();
        // 获取父类、本类中声明为public的属性
        for (Field field : fields) {
            System.out.println(field);
            //public int com.lwj.java1.Person.id
            //public double com.lwj.java1.Creature.weight
        }
        Field[] declaredFields = personClass.getDeclaredFields();
        // 获取本类声明的全部属性
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
            //private java.lang.String com.lwj.java1.Person.name
            //int com.lwj.java1.Person.age
            //public int com.lwj.java1.Person.id
        }
    }
}

获取属性的修饰符、类型、变量名

package com.lwj.java2;


import com.lwj.java1.Person;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class FieldTest {
    public static void main(String[] args) {
        Class<Person> personClass = Person.class;
        Field[] declaredFields = personClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            //权限修饰符
            int modifiers = declaredField.getModifiers();
            System.out.print(Modifier.toString(modifiers) + "\t");
            //public default private
            //数据类型
            Class<?> type = declaredField.getType();
            System.out.print(type + "\t");
            //变量名
            String name = declaredField.getName();
            System.out.println(name);
        }
    }
}

3、Method

Method[] getMethods()

Method[] getDeclaredMethods()

package com.lwj.java2;


import com.lwj.java1.Person;

import java.lang.reflect.Method;

public class MethodTest {
    public static void main(String[] args) {
        Class<Person> personClass = Person.class;
        Method[] methods = personClass.getMethods();
        //获取当前类及其父类中声明的所有public方法
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("-------------------");
        //获取当前类声明的所有方法
        Method[] declaredMethods = personClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
            //public int com.lwj.java1.Person.compareTo(java.lang.String)
            //public int com.lwj.java1.Person.compareTo(java.lang.Object)
            //public void com.lwj.java1.Person.info()
            //public java.lang.String com.lwj.java1.Person.display(java.lang.String)
            //private java.lang.String com.lwj.java1.Person.show(java.lang.String)
        }
    }
}

获取运行时类方法的内部结构

package com.lwj.java2;


import com.lwj.java1.Person;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class MethodTest {
    public static void main(String[] args) {
        Class<Person> personClass = Person.class;
        Method[] declaredMethods = personClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            //方法上的注解
            Annotation[] annotations = declaredMethod.getAnnotations();
            for (Annotation annotation : annotations) {
                System.out.print(annotation + "\t");
            }
            //权限修饰符
            int modifiers = declaredMethod.getModifiers();
            System.out.print(Modifier.toString(modifiers) + "\t");
            //返回值类型
            Class<?> returnType = declaredMethod.getReturnType();
            System.out.print(returnType + "\t");
            //方法名
            String name = declaredMethod.getName();
            System.out.print(name + "(");
            //形参列表
            Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
            if (!(parameterTypes == null || parameterTypes.length == 0)) {
                for (int i = 0; i < parameterTypes.length; i++) {
                    if (i == parameterTypes.length - 1) {
                        System.out.print(parameterTypes[i].getName());
                        break;
                    }
                    System.out.print(parameterTypes[i].getName() + ",");
                }
            }
            System.out.print(")");
            //异常
            Class<?>[] exceptionTypes = declaredMethod.getExceptionTypes();
            if (!(exceptionTypes == null || exceptionTypes.length == 0)) {
                System.out.print("  throws");
                for (int j = 0; j < exceptionTypes.length; j++) {
                    if (j == exceptionTypes.length - 1) {
                        System.out.print(exceptionTypes[j]);
                        break;
                    }
                    System.out.print(exceptionTypes[j] + ",");
                }
            }
            System.out.println();
        }
    }
}

4、Constructor

package com.lwj.java2;


import com.lwj.java1.Person;

import java.lang.reflect.Constructor;

public class ConstructorTest {
    public static void main(String[] args) {
        Class<Person> personClass = Person.class;
        Constructor<?>[] constructors = personClass.getConstructors();
        //获取当前运行时类中声明为public的构造器
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
            //public com.lwj.java1.Person()
        }
        Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors();
        //获取当前运行时类中声明的所有构造器
        for (Constructor declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
            //com.lwj.java1.Person(java.lang.String,int)
            //private com.lwj.java1.Person(java.lang.String)
            //public com.lwj.java1.Person()
        }
    }
}

5、运行时类的父类

package com.lwj.java2;


import com.lwj.java1.Person;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class SuperClassTest {
    public static void main(String[] args) {
        Class<Person> personClass = Person.class;
        Class<? super Person> superclass = personClass.getSuperclass();
        //获取运行时类的父类
        System.out.println(superclass);
        //class com.lwj.java1.Creature
        Type genericSuperclass = personClass.getGenericSuperclass();
        //获取运行时类的泛型父类
        System.out.println(genericSuperclass);
        //com.lwj.java1.Creature<java.lang.String>

        //获取泛型类型参数数组
        ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
        System.out.println(actualTypeArguments[0].getTypeName());
        //java.lang.String
    }
}

6、接口

package com.lwj.java2;


import com.lwj.java1.Person;

public class InterfaceTest {
    public static void main(String[] args) {
        Class<Person> personClass = Person.class;
        Class<?>[] interfaces = personClass.getInterfaces();
        for (Class interfaceIns : interfaces) {
            System.out.println(interfaceIns);
            //interface java.lang.Comparable
            //interface com.lwj.java1.MyInterface
        }
    }
}

7、所在的包

package com.lwj.java2;


import com.lwj.java1.Person;

public class PackageTest {
    public static void main(String[] args) {
        Class<Person> personClass = Person.class;
        Package aPackage = personClass.getPackage();
        System.out.println(aPackage);
        //package com.lwj.java1
    }
}

8、类上的注解

package com.lwj.java2;

import com.lwj.java1.Person;

import java.lang.annotation.Annotation;


public class AnnoTest {
    public static void main(String[] args) {
        Class<Person> personClass = Person.class;
        Annotation[] annotations = personClass.getAnnotations();
        System.out.println(annotations[0]);
        //@com.lwj.java1.MyAnnotation(value=Person)
    }
}

6、调用运行时类指定结构

1、属性

package com.lwj.java3;


import com.lwj.java1.Person;

import java.lang.reflect.Field;

public class FieldTest {
    public static void main(String[] args) throws Exception {
        Class<Person> personClass = Person.class;
        Person person = personClass.newInstance();
        Field name = personClass.getDeclaredField("name");
        //获取运行时类指定的属性
        name.setAccessible(true);
        name.set(person, "李四");
        //Field对象的set(obj, value),obj:给哪个对象赋值,value,给对象的Field属性赋哪个值
        System.out.println(name.get(person));
        //获取当前对象的Field属性值
        //李四
    }
}

2、方法(最重要)

package com.lwj.java3;


import com.lwj.java1.Person;

import java.lang.reflect.Method;

public class MethodTest {
    public static void main(String[] args) throws Exception {
        Class<Person> personClass = Person.class;
        Method show = personClass.getDeclaredMethod("show", String.class);
        //获取指定的方法对象 getDeclaredMethod(方法名,参数列表的Class对象)
        Person person = personClass.newInstance();
        show.setAccessible(true);
        Object china = show.invoke(person, "China");
        //调用person对象的show()方法并传递"China"参数,invoke()方法的返回值就是show()方法的返回值
        System.out.println(china);
    }
}

静态方法

在Person类中新增静态方法。

private static void showDesc() {
    System.out.println("我是一个静态方法");
}
package com.lwj.java3;


import com.lwj.java1.Person;

import java.lang.reflect.Method;

public class MethodTest {
    public static void main(String[] args) throws Exception {
        Class<Person> personClass = Person.class;
        Method showDesc = personClass.getDeclaredMethod("showDesc");
        showDesc.setAccessible(true);
        Object invoke = showDesc.invoke(Person.class);
        //我是一个静态方法
        System.out.println(invoke);
        //如果此方法没有返回值,那么invoke()方法返回null
    }
}

静态属性与静态方法类似,set(Person.class, "李四");

3、构造器

package com.lwj.java3;


import com.lwj.java1.Person;

import java.lang.reflect.Constructor;

public class ConstructorTest {
    public static void main(String[] args) throws Exception {
        Class<Person> personClass = Person.class;
        Constructor<Person> declaredConstructor = personClass.getDeclaredConstructor(String.class);
        declaredConstructor.setAccessible(true);
        Person person = declaredConstructor.newInstance("张三");
        System.out.println(person);
        //Person{name='张三', age=0, id=0}
    }
}

Class类的一个实例代表加载到内存中的一个运行时类。

7、反射的应用:动态代理

Spring框架的AOP的原理:动态代理。

静态代理举例:

package com.lwj.java4.staticproxy;

interface ClothFactory {
    void produceCloth();
}

//代理类
class ProxyClothFactory implements ClothFactory {

    private ClothFactory clothFactory;

    public ProxyClothFactory(ClothFactory clothFactory) {
        this.clothFactory = clothFactory;
    }

    @Override
    public void produceCloth() {
        System.out.println("代理工厂做一些准备工作");
        clothFactory.produceCloth();
        System.out.println("代理工厂做一些收尾工作");
    }
}

//被代理类
class NikeClothFactory implements ClothFactory {

    @Override
    public void produceCloth() {
        System.out.println("Nike工厂生产Air Force 1");
    }
}

//代理类与被代理类在编译期就确定下来了
public class StaticProxyTest {
    public static void main(String[] args) {
        NikeClothFactory nikeClothFactory = new NikeClothFactory();
        ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nikeClothFactory);
        proxyClothFactory.produceCloth();
    }
}

动态代理举例:

package com.lwj.java4.dynamicproxy;


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface Human {
    String getBelief();
    void eat(String food);
}

//被代理类
class SuperMan implements Human {

    @Override
    public String getBelief() {
        return "I believe i can fly.";
    }

    @Override
    public void eat(String food) {
        System.out.println("我喜欢吃" + food);
    }
}

//动态代理类:根据被代理类,动态创建一个跟你实现一样接口的类,代理对你的执行
/*
* 要想实现动态代理,需要解决的两大问题:
* 1、如何根据加载到内存中的被代理类,动态的创建一个代理类和对象;
* 2、当通过代理类对象调用方法时,如何动态的去调用被代理类中的同名方法;*
* */

class ProxyFactory {
    //调用此方法,返回一个代理类对象
    public static Object getProxyInstance(Object obj) { //obj被代理类的对象
        MyInvocationHandler handler = new MyInvocationHandler();
        handler.setObj(obj);
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
    }
}

class MyInvocationHandler implements InvocationHandler {

    private Object obj;
    //obj代表被代理对象
    public void setObj(Object obj) {
        this.obj = obj;
    }

    //当我们通过代理类对象,调用方法getBelief/eat时,就会自动的调用如下的方法invoke()
    //将被代理类要执行的方法声明为invoke()方法中
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(obj, args);
    }
}

public class ProxyTest {
    public static void main(String[] args) {
        SuperMan superMan = new SuperMan();
        Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
        System.out.println(proxyInstance.getBelief());
        proxyInstance.eat("火锅");
    }
}

标签:反射,String,System,public,Person,枚举,注解,class,out
来源: https://www.cnblogs.com/shawnyue-08/p/12592671.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有