枚举、注解、反射、内省【学习笔记】

1、枚举

1.1枚举简介

JDK1.5引入了新的类型——枚举。
在JDK1.5之前,我们定义常量都是: public static fian1.... 。很难管理。
枚举,可以把相关的常量分组到一个枚举类型里,而且枚举提供了比常量更多的方法。

用于定义有限数量的一组同类常量,例如: 

    错误级别:
    	低、中、高、急
    一年的四季:
    	春、夏、秋、冬
    商品的类型:
    	美妆、手机、电脑、男装、女装...
    	
在枚举类型中定义的常量是该枚举类型的实例。

1.2枚举的定义格式

权限修饰符 enum 枚举名称{
	实例1,实例2,实例3,实例4;
}

1.3了解:枚举的演化

jdk1.5之前要实现类似枚举的功能:

实现:描述级别(同一种类型的有限个常量)
这时候还没有枚举的类型,所有的功能需要自己实现
首先要创建一个描述级别的类:然后里面设置常量
public class Level {
    public static final Level LOW = new Level(1);
    public static final Level MEDIUM = new Level(50);
    public static final Level HIGH = new Level(100);
    private int levelValue;
    private Level(int levelValue){
        this.levelValue = levelValue;
    }
    public int getLevelValue() {
        return levelValue;
    }
    public void setLevelValue(int levelValue) {
        this.levelValue = levelValue;
    }
}

public static void main(String[] args) {
        System.out.println(Level.MEDIUM.getLevelValue());
    }

通常,还要有许多其它的需求,比如比较枚举值的大小等等…这就需要往里面再添加大量的方法,每当有一个枚举的需求,就需要做大量这样重复的工作…

于是jdk1.5以后就出现了枚举的类型

jdk1.5以后枚举的写法:

public enum Level2 {
    LOW(1),MEDIUM(50),HIGH(100);
    private int levelValue;
    private Level2(int levelValue){
        this.levelValue = levelValue;
    }
    public int getLevelValue() {
        return levelValue;
    }
    public void setLevelValue(int levelValue) {
        this.levelValue = levelValue;
    }
}
//上述定义方式与之前的功能一样的

//甚至可以这么写
public enum Level3 {//这么简单的定义方式反而更加常用,根据字面描述就已经可看出大小了
    LOW,MEDIUM,HIGH;
}

1.4枚举类的常用方法

Enum抽象类常见方法

在这里插入图片描述

实例:

public class demo {
    public static void main(String[] args) {
        System.out.println(Level3.LOW.compareTo(Level3.HIGH));//-2
        System.out.println(Level3.LOW.compareTo(Level3.MEDIUM));//-1
        System.out.println(Level3.HIGH.compareTo(Level3.LOW));//2
        System.out.println(Level2.LOW.compareTo(Level2.HIGH));//-2
        System.out.println(Level2.LOW.name());//LOW
        System.out.println(Level2.LOW.toString());//LOW
        System.out.println(Level2.LOW.ordinal());//0
        System.out.println(Level2.MEDIUM.equals(Level2.MEDIUM));//true

        //参数1:要返回的枚举类型,参数2:要获取枚举类型中哪个常量对象(提供对象的名字的String)
        Level2 x = Enum.valueOf(Level2.class,"HIGH");
        System.out.println(x.name());//HIGH
    }
    //枚举的常用操作(方法),枚举中经常使用switch来进行操作,如下:
    public static void haha(Level2 l ){
        switch (l){
            case LOW:break;
            case MEDIUM:break;
            case HIGH:break;
        }
    }
}

1.5实现接口的枚举类

当使用enum创建枚举类时,是默认继承了Enum抽象类的。java中是不允许多继承的,所以使用enum定义的类不能继承其它类,只能通过实现接口。、

实例:

//定义一个接口
interface LShow{
    void show();
}
//
public enum Level3 implements LShow{
    LOW,MEDIUM,HIGH;
    @Override
    public void show() {
        System.out.println("级别对象在调用show方法,该对象的级别为:"+this.name());
    }
}
//main方法
public static void main(String[] args) {
        Level3.LOW.show();//级别对象在调用show方法,该对象的级别为:LOW
    }

虽然可以这样操作,但是每个枚举都是一个独立的对象,按照java的逻辑,应该是对象调用不同方法,而不是多个对象共享同一个方法。于是 枚举中:可以 为每个枚举对象 添加自己独特的 对于接口方法的实现:
    public enum Level3 implements LShow{
    LOW(){
        @Override
        public void show() {
            System.out.println("低等级在调用show方法");
        }
    },MEDIUM(){
        @Override
        public void show() {
            System.out.println("中等级在调用show方法");
        }
    },HIGH(){
        @Override
        public void show() {
            System.out.println("高等级在调用show方法");
        }
    }
}

1.6枚举的注意事项

  • 一旦定义了枚举,最好不要妄图修改里面的值,除非修改是必要的。
  • 枚举类默认继承的是java.lang.Enum类而不是Object类
  • 枚举类不能有子类,因为其枚举类默认被final修饰
  • 只能有private构造方法
  • switch中使用枚举时,直接使用常量名,不用携带类名
  • 不能定义name属性,因为自带name属性
  • 不要为枚举类中的属性提供set方法,不符合枚举最初设计初衷。

2、注解

2.1注解简介

Java注解(Annotation) 又称Java标注,JDK5.0引入的一注释机制。

Java语言中的类、方法、变量、参数和包等都可以被标注。和注释不同,Java标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容。当然它也支持自定义Java标注。

理解:相当于一种可以被编译进class文件的注释,注释是给人看的,注解是给机器看的。

  • 主要用于
    • 编译格式检查
    • 反射中解析
    • 生成帮助文档
    • 跟踪代码依赖

2.2学习的重点

理解Annotation的关键是理解其语法和用法。

学习步骤:

  1. 概念
  2. 怎么使用内置注解
  3. 怎么自定义注解
  4. 反射中怎么获取注解内容

2.3内置注解

  • @Override : 重写 *

    • 定义在java.lang.Override

    • 作用:编译格式检查注解,当方法的注解是@Override,该方法必须是重写了父类的方法。

  • @Deprecated:废弃 *

    • 定义在java.lang,Deprecated
  • @SafeVarags

    • Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
  • @FunctionalInterface:函数式接口 *

    • Java 8开始支持,标识一个匿名函数或函数式接口(比如Runnable)
  • @Repeatable:标识某注解可以在同一声明上使用多次

    • Java 8开始支持,标识某注解可以在同一声明上使用多次
  • @SuppressWarnings:抑制编译时的警告信息

    • 定义在java.lang.SuppressWarnings

    • 三种使用方式

      1.@SuppressWarnings("unchecked")[^抑制单类型的警告]
      2.@SuppressWarnings("unchecked","rawtypes")[^抑制多类型的警告]
      3.@SuppressWarnings("all")[^抑制所有类型的警告]
      
    • 参数列表:

    关键字用途
    all抑制所有警告
    boxing抑制装箱、拆箱操作时候的警告
    cast抑制映射相关的警告
    dep-ann抑制启用注释的警告
    deprecation抑制过期方法警告
    fallthrough抑制确在switch中缺失breaks的警告
    finally抑制finally模块没有返回的警告
    hiding抑制相对于隐藏变量的局部变量的警告
    incomplete- switch忽略没有完整的switch语句
    nls忽略非nls格式的字符
    null忽略对nul的操作
    rawtypes使用generics时忽略没有指定相应的类型
    restriction抑制禁止使用劝阻或禁止引用的警告
    serial忽略在serializable类中没有声明serialVersionUID变量
    static-access抑制不正确的静态访问方式警告
    synthetic-access抑制子类没有按最优方法访问内部类的警告
    unchecked抑制没有进行类型检查操作的警告
    unqualified-field-access抑制没有权限访问的域的警告
    unused抑制没被使用过的代码的警告

2.4元注解

1、简介

作用在其它注解的注解

2、有哪些

  • @Retention - 标识这个注解怎么保存(持久化方案):

    • 是只在代码中
    • 编译入class文件
    • 还是在运行时可以通过反射访问
  • @Documented - 标记这些注解是否包含在用户文档中。

  • @Target - 标记这个注解应该是那种Java成员(类/方法/一句代码/等)

  • @Inherited - 标记这个注解是自动继承的

    • 1. 子类会继承父类使用的注解中被@Inherited修饰的注解
      2. 接口继承关系中,子接口不会继承父接口中的任何注解,不管父接口中使用的注解有没有被@Inherited修饰
      3. 类实现接口时不会继承任何接口中定义的注解
      

2.5自定义注解

1.注解架构

在这里插入图片描述

Annotation与RetentionPolicy与ElementType

每一个Annotation对象,都会有唯一的RetentioPolicy属性;至于ElementType属性,则有1-n个。

ElementType(注解的用途类型)

“每一个Annotation”都与“1-n个ElementType”关联。当Annotation与某个ElementType关联时,就意味着:Annotation有了某种用途。

​ 例如:若一个Annotation对象是METHOD类型,则该Annotation只能用来修饰方法。

package java.lang.annotation;
public enum ElementType {

    TYPE,//类、接口(包括注释类型)或枚举声明
    
    FIELD,//字段声明(包括枚举常量)

    METHOD,//方法声明

    PARAMETER,//参数声明

    CONSTRUCTOR,//构造方法声明

    LOCAL_VARIABLE,//局部变量声明

    ANNOTATION_TYPE,//注释类型声明
    
    PACKAGE,//包声明

    TYPE_PARAMETER,
   
    TYPE_USE,

    MODULE
}
RententionPolicy(注解作用域策略)

“每一个Annotation”都与“一个RententionPolicy”关联

若Annotation的类型为:

  • SOURCE:则意味着,Annotation仅存在于编译器处理期间,编译器处理完之后就没用了。

​ 例如: “@Override”标志就是一个Annotation。当它修饰一个方法的时候,就意味着该方法覆盖父类的方法;并且编译期间会进行语法检查!编译器处理完后,“@Override”就没有任何作用了。

  • CLASS:则意味着,编译器将Annotation存储于类对应的.class文件中,它是Annotation的默认行为。

  • RUNTIME:则意味着,编译器将Annotation存储与class文件中,并且可由JVM读入。

public enum RetentionPolicy {

    SOURCE,

    CLASS,

    RUNTIME
}

2.定义格式

@interface 自定义注解名{}

3.注意事项

1.定义的注解,自动实现了java.lang annotation.Annotation接口

2.注解中的每一一个方法,实际是声明的注解配置参数

  • 方法的名称就是配置参数的名称
  • 方法的返回值类型,就是配置参数的类型。只能是:基本类型/Class/String/enum

3.可以通过default来声明参数的默认值

4.如果只有一个参数成员,一般参数名为value(传参时缺省参数名,默认为value,前提是只有一个参数)

5.注解元素必须要有值,我们定义注解元素时,经常使用空字符串、0作为默认值。

4.案例

@Retention(RetentionPolicy.CLASS)//持久化方案:
@Documented()//有此注解,表示包含在用户文档
@Target({ElementType.TYPE,ElementType.METHOD})//作用对象(或范围)
@Inherited//有此注解,表示自动继承
public @interface MYAnnotation {
    //参数:
    //	格式:参数类型 参数名() default 默认值;
    String value() default "";//参数名传入时缺省默认为value。
}
@interface

​ 使用@interface定义注解时,意味着它实现了java.lang.annotation.Annotation 接口,即该注解就是一个
Annotation。

​ 定义Annotation时,@interface是必须的。

​ 注意:它和我们通常的implemented实现接口的方法不同。Annotation 接口的实现细节都由编译器完成。通过@interface定义注解后,该注解不能继承其他的注解或接口。

@Documented

​ 类和方法的Annotation在缺省情况下是不出现在javadoc中的。如果使用@Documented修饰该Annotation,
则表示它可以出现在javadoc中。

​ 定义Annotation时,@Documented可有可无;若没有定义,则Annotation不会出现在javadoc中。

@Target(ElementType.TYPE)

​ 前面我们说过,ElementType是Annotation的类型属性。而@Target的作用,就是来指定Annotation的类型属性。

​ @Target(ElementType.TYPE)的意思就是指定该Annotation的类型是ElementType.TYPE.这就意味着,MyAnnotation是来修饰"类、接口(包括注释类型)或枚举声明”的注解。

​ 定义Annotation时,@Target可有可无,若有@Target,则该Annotation只能用于它所指定的地方;若没有@Target,则该Annotation可以用于任何地方。

@Retention(RetentionPolicy.RUNTIME)

​ 前面我们说过,RetentionPolicy是Annotation的策略属性,而@Retention的作用,就是指定Annotation的策略属性。

​ @Retention(RetentionPolicy.RUNTIME)的意思就是指定该Annotation的策略是RetentionPollcy.RUNTIME。这就意味着,编译嚣会将该Annotation信息保留在.class文件中,并且能被虚拟机读取。

​ 定义Annotation时,@Retention可有可无。若没有@Retention,则默认是RetentionPolicy.CLASS.

3、反射

1、概述

JAVA反射机制是在运行状态中:
	获取任意一个类的结构,
		创建对象
			得到方法
				执行方法、调用属性
这种在运行状态动态获取信息以及动态调用对象方法的功能被称为java语言的反射机制。
使用场景/需求:
场景,已知程序开发中必定要创建一个Person类来存储对象,但是开发计划中是在半个月后创建Person,但是现在我们需要使用Person类的对象、方法、属性,先完成业务逻辑的编码。但是我们现在不能直接使用Person p = new Person()来创建对象,因为Person类还没有创建。
	虽然Person还没创建,但是我们已经知道Person存储的包、属性和方法。于是可以使用反射机制,来操作Person类。

2、类加载器

Java类加载器(Java Classloader)是Java运行时环境(Java Runtime Environment)的一部分,负责动态加载Java类到Java虚拟机的内存空间中。

java默认有三种类加载器,BootstrapClassLoader、ExtensionClassLoader、AppClassLoader。

BootstrapClassLoader(引导启动类加载器):
	嵌在JVM内核中的加戟器,该加献器是用C++语言写的(无法用Java代码直接使用),主要负载加载JAVA_HOME/1ib下的类库,引导启动类加载器无法被应用程序直接使用。

ExtensionClassLoader(扩展类加载器):
	ExtensionClassLoader是用JAVA编写,且它的父类加载器是Bootstrap。
	是由sun.misc.Launcher$ExtClassLoader实现的,主要加载JAVA_HOME/lib/ext目录中的类库。
	它的父加载器是BootstrapClassLoader
	
AppClassLoader(应用类加载器):
AppClassLoader是应用程序类加载器,负责加载应用程序classpath目录下的所有jar和class文件。它的父加载
器为ExtClassLoader

在这里插入图片描述

	类通常是按需加载,即第一次使用该类时才加载。由于有了类记载其,Java运行时系统不需要知道文件与文件系统。学习类加载器时,掌握Java的委派概念很重要。

	双亲委派模型:如果一个类加载器收到了一个加载请求,它不会自己去尝试加载这个类,而是把这个请求专拣给父类加载器去完成。因为所有的类加载请求都应该传递到最顶层的启动类加载器中,只有到父类加载器反馈自己无法完成这个加载请求(在它得分搜索范围内没有找到这个类)时,子类加载器才会尝试自己去加载。委派的好处就是避免有些类被重复加载。
  • 实例:
public class ClassLoaderDemo {
    /**
     * 类加载器demo
     * @param args
     */
    public static void main(String[] args) throws IOException {
        //使用类加载器去加载配置文件
        //获取类加载器的方式:某个类名.class.getClassLoader()就能在内存中获取到这个类的类加载器
        System.out.println(ClassLoaderDemo.class.getClassLoader());//测试输出:jdk.internal.loader.ClassLoaders$AppClassLoader@1f89ab83
        //获取一个在资源根目录(相对路径)下的(如果没有资源根目录,就获取src下的)文件的输入流
        InputStream is = ClassLoaderDemo.class.getClassLoader().getResourceAsStream("./com/zx/fanshe/demo1.txt");
        BufferedReader bf = new BufferedReader(new InputStreamReader(is));
        String text = bf.readLine();
        System.out.println(text);
        bf.close();
    }
}

3、所有类型的Class对象

当定义一个Person类时,程序运行时Person类会加载到内存中:
	Person = new Person();
	这时候在内存中p对象指向的是一个地址,这块内存地址存储的是一个Person类型的数据,这些数据也就是p对象的内容。

上面就是Person的对象在内存中的存储方式,是以Person类型对象存储的;

那么,Person类在内存中是如何存储的呢?
答:也是以对象的形式存储的,这个对象是的类型是Class。调用Person.class,就可以得到这个class对象,java中所有的类都是以Class类的一个对象。

总结:	
	Person是一个类型,其对象是Person类型的对象,存储	Person  类型的数据
	Class也是一个类型,其对象是Class类型的对象,存储    类	   类型的数据
要想了解一个类,必须先获取到该类的字节码文件对象。
在Java中,每一个字节码文件,被加载到内存后,都会存在其一个对应的Class类型的对象

4、得到Class的几种方式

	1.	如果拥有类的对象,可以通过
		Class 对象.getClass() 得到一个类的 类对象
		
	2.	如果在编写代码时,知道类的名称,可以通过
		Class.forName(包名+类名):得到一个类的 类对象
		
	3.	如果在编写代码时,知道类的名称,且类已经存在,可以通过
		包名.类名.class 得到一个类的 类对象
		
上述三种方式,在调用时,如果类在内存中不存在,则会加载到内存!如果类已经在内存中存在,不会重复加载,而是重复利用!

(一个class文件 在内存中不会存在两个类对象)

特殊的类对象:
	
	基本数据类型的类对象:
		基本数据类型.class
		包装类.type
	基本数据类型包装类对象:
		包装类.class
//实例:
		
		//1.    如果拥有类的对象,可以通过: Class c = 对象.getClass() 得到一个类的 类对象
        Person p = new Person("0001", "张三", 18);
        Class class1 = p.getClass();
        System.out.println(class1);
        //2.	如果在编写代码时,知道类的名称,可以通过:  Class.forName(包名+类名):得到一个类的 类对象
        Class Class2 = Class.forName("com.zx.Person");//这种方式是可以在Person对象并不存在的时候去使用的
        System.out.println(Class2);
        //3.	如果在编写代码时,知道类的名称,且类已经存在,可以通过:   包名.类名.class 得到一个类的 类对象
        Class<Person> class3 = com.zx.Person.class;
        System.out.println(class3);


		System.out.println(class1==class3 &&class1==class2);//true

5、获取Constructor(构造方法)

通过class对象 获取一个类的构造方法
1.	通过指定的参数类型,获取指定的单个构造方法
	getConstructor(参数类型的class对象数组);
2.	获取构造方法数组
	getConstructors();
3.	获取所有权限的单个构造方法
	getDeclaredConstructor(参数类型的class对象数组);
4.	获取所有权限的构造方法数组
	getDeclaredConstructors();
	
实例1//1.通过指定的参数类型,获取指定的单个构造方法:getConstructor(参数类型的class对象数组)
    	//Person中实现了一个一参的private构造方法,由于private限制,这里需要获取所有权限getDeclaredConstructor,并且忽略权限检查setAccessible。
        Class<Person> aClass1 = (Class<Person>) Class.forName("com.zx.Person");
		Constructor<Person> c1 = aClass1.getDeclaredConstructor(String.class);
        //忽略权限的检查
        c1.setAccessible(true);
        Person p =  c1.newInstance("0001");
        System.out.println(p);

实例2:上面的代码可以修改为,这段代码可以在Person类不存在的情况下使用
    	//获取类对象
        Class aClass1 = Class.forName("com.zx.Person");
        //获取一个构造方法
        Constructor c1 = aClass1.getConstructor();
        //构造一个对象
        Object o = c1.newInstance();
        System.out.println(o);
Constructor—创建对象
Constructor的常用方法就是上面实例中的:
	1.	newInstance(Object... pata)
			调用这个构造方法,把对应的对象创建出来
			参数:是一个Object类型的可变参数,传递的参数顺序必须匹配构造方法中形式参数列表的顺序!
	2.	setAccessible(boolean flag)
			如果flag为true 则表示忽略访问权限检查!(可以访问任何权限的方法)

6、获取Method(方法)

通过class对象获取一个类的方法
1. 	getMethod(string methodName,class.. clss)
	根据参数列表的类型和方法名,得到一个方法(public修饰的)
	
2. 	getMethods();
	得到一个类的所有方法(public修饰的)
	
3. 	getDeclaredMethod(string methodName,class.. clss)
	根据参数列表的类型和方法名,得到一个方法(除继承以外所有的:包含私有,共有,保护,默认)
	
4. 	getDeclaredMethods() ;
	得到一个类的所有方法(除继承人外所有的:包含私有,共有,保护,默认)
Method执行方法
invoke(Object o,0bject... para) :
	调用方法
	参數1.要调用方法的对象
	参数2.要传递的参数列表
	
getName()
	获取方法的方法名称
	
setAccessible(boolean flag)
	如果flag为true则表示忽略访问权限检查 ! (可以访问任何权限的方法)
实例:
		//获取类对象
        Class aClass1 = Class.forName("com.zx.Person");
        //获取一个构造方法
        Constructor c1 = aClass1.getConstructor();
        //构造一个对象
        Object o = c1.newInstance();

        //使用类对象获取方法:getDeclaredMethod——根据参数列表类型和方法名
        Method method = aClass1.getDeclaredMethod("PrivateMethod");
        //忽略权限检查
        method.setAccessible(true);
        method.invoke(o);

7、获取Field

通过class对象获取一个类的属性
1. getDeclaredField(String filedName)
	根据属性的名称,获取一个属性对象(所有属性)
	
2. getDeclaredFields ()
	获取所有属性
	
3. getField(String filedName)
	根据属性的名称,获取一个属性对象(public属性)
	
4. getFields()
	获取所有属性(public)
Field属性的对象类型
常用方法:

	1. get(object o);
		参数:要获取属性的对象
		获取指定对象的此属性值
		
	2.set(object o,object value);
		参数1.要设置属性值的对象
		参数2.要设置的值
		设置指定对象的属性的值
		
	3. getName()
		获取属性的名称
		
	4. setAccessible(boolean flag)
		如果flag为true则表示忽略访问权限检查 ! (可以访问任何权限的属性)
实例:
		//获取类对象
        Class aClass1 = Class.forName("com.zx.Person");
        //获取一个构造方法
        Constructor c1 = aClass1.getConstructor();
        //构造一个对象
        Object o = c1.newInstance();

        //使用类对象获取属性:getDeclaredField——根据属性名获取所有权限的属性
        Field field = aClass1.getDeclaredField("name");
        //忽略权限检查
        field.setAccessible(true);

        //1.get()方法:获取指定对象的此属性值,o:要获取该属性的对象
        System.out.println(field.get(o));
        //2、set()方法:设置指定对象的属性值,参数1:要设置属性的对象;参数2:要设置的值
        field.set(o,"张三");
        System.out.println(field.get(o));
        //3、getName()方法:获取属性的名称
        System.out.println(field.getName());

8、获取注解信息

后续学习框架的原理就是:

通过反射的,获取到某个类,扫描类的注解、扫描类的属性的注解,获取到注解传入的参数值,从而对这个类的对象进行更好的处理。好处就是,不需要再对该类的每个对象进行相同的处理。

获取类/属性/方法的全部注解对象
Annotation [] as = (Class/Field/Method类型的)对象.getAnnotations();
for(Annotation a : as){
	System.out.println(a);
}
根据类型获取类/属性/方法的注解对象
注解类型 对象名 = (注解类型) c.getAnnotation(注解类型.class);
实例
//1.定义两个注解
    //注解1:这是一个作用在类上的,映射到数据库 表 的注解
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    public @interface TableAnnotation {
        String value();
    }
    //注解2:这是一个作用在属性上,映射到数据库 字段属性 的注解
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    @Documented
    public @interface ColunmAnnotation {
        String column_name();
        String type();
        String length();
    }

//2.定义一个Book实体类,并添加注解,将这个实体类的各种属性映射到数据库
    @TableAnnotation("Test_Book")
    public class Book {
        @ColunmAnnotation(column_name = "id", type = "varchar", length = "20")
        private String id;
        @ColunmAnnotation(column_name = "name", type = "varchar", length = "36")
        private String name;
        @ColunmAnnotation(column_name = "info", type = "varchar", length = "36")
        private String info;

        public Book(String id, String name, String info) {
            this.id = id;
            this.name = name;
            this.info = info;
        }
        @Override
        public String toString() {
            return "Book{" +
                    "id='" + id + '\'' +
                    ", name='" + name + '\'' +
                    ", info='" + info + '\'' +
                    '}';
        }
    }

//3.main方法:
//		(1)获取Book类的class对象。
//		(2)通过class对象,获取其:class类上的注解、属性上的注解。
//		(3)通过注解的参数值,获取到该类的【类本身、属性】与数据库的映射关系
public class Demo5_GetAnnotation {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException {
        //获取class对象
        Class aClass = Class.forName("com.zx.fanshe.Book");
        //获取class类的注解
        TableAnnotation ta = (TableAnnotation) aClass.getDeclaredAnnotation(TableAnnotation.class);
        String value = ta.value();
        System.out.println("类名:"+aClass.getSimpleName()+";    数据库中的表名:"+value);

        //根据class对象获取其属性
        Field[] fields = aClass.getDeclaredFields();
        for(Field f:fields){
            //获取属性的注解
            ColunmAnnotation ca = f.getAnnotation(ColunmAnnotation.class);
            String column_name = ca.column_name();
            String length = ca.length();
            String type = ca.type();
            System.out.println(f.getName()+"属性:\n    对应数据库中的字段:"+column_name+";  数据类型:"+type+";   长度:"+length);
        }
    }
}

//输出:
/*

类名:Book;    数据库中的表名:Test_Book
id属性:
    对应数据库中的字段:id;  数据类型:varchar;   长度:20
name属性:
    对应数据库中的字段:name;  数据类型:varchar;   长度:36
info属性:
    对应数据库中的字段:info;  数据类型:varchar;   长度:36
    
*/

4、内省

简介

基于反射 , java所提供一套应用到JavaBean的API

	一个定义在包中的类 , 
			拥有无参构造器
			所有属性私有
			所有属性提供get/set方法
			实现了序列化接口
	这种类,我们称其为 bean类.
	
Java提供了一套java.beans包的api , 对于反射的操作们进行了封装!
Introspector
获取Bean类信息

方法:
	BeanInfo getBeanInfo(Class cls)
	
	通过传入的类信息,得到这个Bean类的封装对象.
BeanInfo
常用的方法:
	MethodDescriptor [] getPropertyDescriptors():
	获取bean类的get/set方法 数组
MethodDescriptor
常用方法:
1. Method getReadMethodO;
   获取一个get方法
2. Method getWr iteMethodO;
   获收一个set方法

实例

//1.创建一个Bean类Express
//属性:
public class Express implements Serializable {
    private String id;
    private String name;
    private boolean flag;
}

//2.main方法
    public static void main(String[] args) throws Exception {
        Class aClass = Class.forName("com.zx.neixing.Express");
        //传入类信息,得到i这个Bean类的封装对象
        BeanInfo beanInfo = Introspector.getBeanInfo(aClass);
        //获取BeanInfo对象每个属性的的get/set方法数组,数组中每一个数据存储一个属性的get和set方法
        PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
        //遍历数组,获取get/set方法
        for(PropertyDescriptor pd:pds){
            System.out.println("属性名:"+pd.getName());
            System.out.println("类型:"+ pd.getPropertyType());
            Method get = pd.getReadMethod();
            Method set = pd.getWriteMethod();
            System.out.println("该属性的get方法:"+get);
            if("class".equals(pd.getName())){
                System.out.println("class只有getclass()方法,没有set方法");
            }
            if("boolean".equals(pd.getPropertyType().getName())){
                System.out.println("boolean类型的get方法为:is属性名()而不是get属性名()");
            }
            System.out.println("该属性的set方法:"+set);
            System.out.println();
            System.out.println();
        }
    }

内省总结

内省是java提供的基于反射、用于操作bean类的API,提供了大量的操作方法。

比如上面的例子:相比于反射,使用内省做到了更快速的获取get/set方法

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值