枚举、反射与内省

一、枚举

1、枚举概述

用于定义有限数量的一组同类常量,例如:
   错误级别: 低级、中级、高级
   一年四季:春、夏、秋、冬
   商品类别:美妆、手机、数码、护肤、家电...
在枚举类型中定义的常量(如春、高级等)是该枚举类型的实例

2、jdk1.5之前如何定义一个等级类型?

/*
* 1.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;
    }
}

3、枚举定义

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

使用枚举类型定义一个等级类型

public enum DemoLevel02 {

    //指定等级对应的水平值
    LOW(1),MEDIUM(50),HIGH(100);

    //水平值
    private int levelValue;

    //将构造方法私有化,表示除此类之外其他方法无法调用 -- 保证等级仅由程序员设定
    private DemoLevel02(int levelValue){
        this.levelValue = levelValue;
    }

    public int getLevelValue() {
        return levelValue;
    }

    //一般不为枚举对象设置set方法,除非修是必要的,否则不修改枚举的值
    public void setLevelValue(int levelValue) {
        this.levelValue = levelValue;
    }
}

4、枚举常用方法

Enum部分方法描述

5、枚举接口 – 实现接口的枚举类

所有的枚举都继承自java.lang.Enum类
由于Java不支持多继承,所以枚举对象不能再继承其他类

每个枚举对象都可以实现自己的抽象方法(接口中定义的抽象方法)

Level.java

/*
* 接口
* */
interface LShow{
    void show();
}
/*
 * 实现接口的枚举类
 * */
public enum Level03 implements LShow{

    LOW{
        @Override
        public void show(){
            System.out.println("低等级");
        }
    },MEDIUM{
        @Override
        public void show(){
            System.out.println("中等级");
        }
    },HIGH{
        @Override
        public void show(){
            System.out.println("高等级");
        }
    };

DemoLevel03.java

public class DemoLevel03 {
    public static void main(String[] args) {
        System.out.println(Level03.LOW.compareTo( Level03.HIGH ));      //-2
        System.out.println(Level03.HIGH.compareTo( Level03.LOW ));      //2
        System.out.println(Level03.LOW.compareTo( Level03.MEDIUM ));    //-1
        System.out.println(Level03.MEDIUM.compareTo( Level03.LOW ));    //1

        System.out.println("equals方法:" + Level03.LOW.equals( Level03.HIGH));    //false
        System.out.println("equals方法:" + Level03.LOW.equals( Level.LOW ));      //false
        System.out.println("getDeclaringClass方法:" + Level03.LOW);               //LOW
        System.out.println("name方法:" + Level03.LOW.name());                     //LOW
        System.out.println("toString方法:" + Level03.LOW.toString());             //LOW
        System.out.println("ordinal方法:" + Level03.LOW.ordinal());               //0

        Level03 high = Enum.valueOf( Level03.class, "HIGH" );
        System.out.println(high);           //HIGH
        System.out.println(high.name());    //HIGH

        Level03.LOW.show();
        Level03.HIGH.show();

    }

}

6、枚举注意事项

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

二、注解

1、注解概述

1、Java注解(Annotion)又称Java标注,是jdk5.0引入的一种注释机制

2、区别于注释:
 
 - Java标注可以通过反射机制获取标注内容
 - 在编译器生成class文件时,标注可以被嵌入字节码中
 - JVM虚拟机可以保留标注内容,在运行时获取标注内容
 - 支持自定义Java标注
 - 注释--给人看   注解--给机器看

3、主要作用 

 1. 编译格式检查 如@Override检查是否重写父类的方法
 2. 反射中解析
 3. 生成帮助文档
 4. 跟踪代码依赖...

4、学习重点:

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

2、内置注解(系统已有的)

Java 定义了一套注解,共有 7 个,3 个在 java.lang 中,剩下 4 个在 java.lang.annotation 中。

1. 作用在代码的注解
@Override - 检查该方法是否是重写方法(只能是方法)
如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
@Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
@SuppressWarnings - 指示编译器去忽略注解中声明的警告。
  三种用法:
  1.@SuppressWarnings("unchecked") [抑制单类型的警告]
  2.@SuppressWarnings("unchecked","rawtypes") [抑制多类型的警告]
  3.@SuppressWarnings("all") [抑制所有类型的警告]
  其他参数见下图

在这里插入图片描述

2. 作用在其他注解的注解(元注解)
@Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
@Documented - 标记这些注解是否包含在用户文档中。
@Target - 标记这个注解应该是哪种 Java 成员。
@Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)
  1.子类会继承父类使用的注解中被@Inherited修饰的注解
  2.接口继承关系中,子接不会继承父类接口中的任何注解,不管父类接口中使用的注解有没有被@Inherited修饰
  3.类实现接口时不会继承任何接口中定义的注解
3. 从 Java 7 开始,额外添加了 3 个注解
@SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
@FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
@Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。

3、自定义注解

1. 注解架构

在这里插入图片描述

  1. 每 1 个 Annotation 对象,都会有唯一的 RetentionPolicy 属性;至于 ElementType 属性,则有 1~n 个。
  2. ElementType(注解的用途类型)
package java.lang.annotation;
public enum ElementType{
   TYPE,				/*类、接口(包括注释类型)或枚举声明*/
   FIFLD,				/*字段声明(包括枚举常量)*/
   METHOD,				/*方法声明*/
   PARAMETER,			/*参数声明*/
   CONSTRUCTOR,			/*构造方法声明*/
   LOCAL_VARIABLE,		/*局部变量声明*/
   ANNOTION_TYPE,		/*注解类型声明*/
   PACKAGE				/*包声明*/
 }
  1. RetentionPolicy(注解作用域策略)
package java.lang.annotation;
public enum RetentionPolicy{
	SOURCE,      /*Annotation信息仅存在于编译器处理期间,编译器处理完之后就没有该Annotation信息了*/
	CLASS,      /*编译器将Annotation存储于类对应的.class文件中。默认行为*/
    RUNTIME      /*编译器将Annotation存储于class文件中,并且可由JVM读入*/
}
2. 定义格式
@interface 自定义注解名{}
类似类、方法的定义,注解的定义将关键字改成@
3. 注意事项
1. 定义的注解,自动继承了java.lang,annotation.Annotation接口
2. 注解中的每一个方法,实际是声明的注解配置参数
 	·方法的名称 -- 配置参数的名称
 	·方法的返回值类型 -- 配置参数的类型(只能是基本类型/Class/String/enum)
3. 可以通过default来声明参数的默认值
  如 String value default "易拉罐"
4. 如果只有一个参数成员,一般参数名为value
5. 注解元素必须要有值,我们定义注解元素时,经常使用空字符串、0作为默认值。
4. 实例展示
@Target(ElementType.TYPE)
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MyAnnotion {
    String value() default 0;
}

在这里插入图片描述

三、反射

1、反射概述

JAVA反射机制是在运行状态中,获取任意一个类的结构,创建对象,得到方法,执行方法,属性!
这种在运行状态动态获取信息以及动态调用对象方法的功能被称为java语言的反射机制。

2、类加载器与资源目录

通过类加载器加载资源文件
默认是加载src路径下的文件,但是当项目存放在resource root 目录下,就加载resource root下的文件。
在这里插入图片描述

3、Class与加载方式

要想了解一个类,必须先要获取到该类的字节码文件对象。
在Java中,每一个字节码文件,被夹在到内存后,都存在一个对应的Class类型的对象

得到Class的几种方式

1.已知类的名称,且类已经存在
包名.类名.class 得到一个类的类对象
2.如果拥有类的对象
Class 对象.getClass() 得到一个类的 类对象
3.已知类的名称
Class.forName(包名+类名) 得到一个类的 类对象

4、反射中的构造方法

(1)获取Constructor–通过class对象 获取一个类的构造方法
1. 通过指定的参数类型,获取指定的单个构造方法
getConstructor(参数类型的class对象数组)
	例如: Person(String name, int age)
	Constructor c = p.getClass().getConstructor(String.class, int.class);
2. 获取构造方法数组
getConstructors();
3. 获取所有权限的单个构造方法
getDeclaredConstructor(参数类型的class对象数组)
4. 获取所有权限的构造方法数组
getDeclaredConstructors();
(2) Constructor创建对象
 newInstance(Object...para)
 [参数:是一个Object类型可变参数,传递的参数顺序必须匹配构造方法中形式参数列表的顺序]
 setAccessible(boolean flag)
 flag = true 表示忽略访问权限检查
(3)练习

Person.java

package demoAnnotion_01;

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

    public Person() {
    }
    //私有的构造方法
    private Person(String name){
        this.name = name;
        this.age = 100;
    }


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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", 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;
    }
}

package demoAnnotion_01;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class DemoPerson {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //1.获取Person类的类对象 -- 把Person加载到内存
        Class<Person> pClass = (Class<Person>)Class.forName( "demoAnnotion_01.Person" );

        //方法一 :获取类对象的无参构造方法
        Constructor<Person> c01 = pClass.getConstructor();
        //通过获取到的无参构造方法创建一个Person对象
        Person p01 = c01.newInstance();
        System.out.println(p01);        //Person{name='null', age=0}

        //方法二:获取类对象的全参构造方法
        Constructor<Person>  c02 = pClass.getConstructor(String.class, int.class);
        //通过获取到的全参构造方法创建一个Person对象
        Person p02 = c02.newInstance("易拉罐",21);
        System.out.println(p02);            //Person{name='易拉罐', age=21}

        //方法三:获取类对象的所有权限(包括public修饰)的构造方法
        Constructor<Person> c03 = pClass.getDeclaredConstructor( String.class );
        /**
         * 如果没有设置修改权限为true,会报错
         *  Class demoAnnotion_01.DemoPerson can not access a member of class demoAnnotion_01.Person with modifiers "private"
         */
        c03.setAccessible( true );
        Person p03 = c03.newInstance( "Eva's family" );
        System.out.println(p03);          //Person{name='Eva's family', age=100}
    }
}

5、反射中的方法

(1)通过class对象 获取一个类的方法
1.根据参数列表的类型和方法名,得到一个方法(public修饰的)
getMethod(StringmethodName,class..clss)
2.得到一个类的所有方法(public修饰的)
getMethods();
3.根据参数列表的类型和方法名,得到一个方法(除继承以外所有的:包含私有,共有,保护,默认)
getDeclaredMethod(StringmethodName,class..clss)
4.得到一个类的所有方法(除继承以外所有的:包含私有,共有,保护,默认)
getDeclaredMethods();
(2)Method执行方法
invoke(Object o, Object...para):
		参数1. 要调用方法的对象
		参数2:要传递的参数列表
getName() 获取参数的方法名称
setAccessiable(boolean flag) 
(3)练习
        //1.获取Person类的类对象 -- 把Person加载到内存
        Class<Person> pClass = (Class<Person>)Class.forName( "demoAnnotion_01.Person" );

        //2. 获取类对象的构造方法,并创建类对象

        //方法一 :获取类对象的无参构造方法
        Constructor<Person> c01 = pClass.getConstructor();
        //通过获取到的无参构造方法创建一个Person对象
        Person p01 = c01.newInstance();
        System.out.println(p01);        //Person{name='null', age=0}

        //3. 获取类的方法

        Method setName = pClass.getMethod( "setName", String.class );
        Method setAge = pClass.getMethod( "setAge", int.class );
        

        //Method执行方法
        setName.invoke( p01, "zqd" );
        setAge.invoke( p01, 23 );
        System.out.println(p01);       //Person{name='zqd', age=23}

6、反射中的属性

(1)通过class对象 获取一个类的属性
1.根据属性的名称,获取一个属性对象(public修饰)
getField(String filedName)
2.获取一个类的所有属性(public修饰的)
getFields();
3.根据属性的名称,获取一个属性对象(所有属性)
getDeclaredField(String filedName)
4.获取所有属性
getDeclaredFileds();
(2)Field属性的对象类型
1.get(Object o )
	参数:要获取属性的对象
	获取指定对象的此属性值
2.set(Object o,Object value);
	参数1. 要设置属性值的 对象
	参数2. 要设置的属性值
3. getName() 获取属性的名称
4. setAccessible(boolean flag)

7、获取注解信息

ORM框架 -- Object Relationship Mapping 对象关系映射
 类 -- 数据库
(1)获取类/属性/方法的全部注解对象`
Annotation[] annotations01 = Class/Field/Method.getAnnotations();
for(Annotationannotation:annotations01){
	System.out.println(annotation);
}
(2) 根据类型获取类/属性/方法的注解对象
注解类型对象名 = (注解类型) c.getAnnotation(注解类型.class);

四、内省

1、简介

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

一个定义在包中的类:
		拥有无参构造器
		所有属性提供get/set方法
		所有属性私有
		实现了序列化接口
这种类称为bean类

Java提供了一套java.beans包的api,对于反射的操作,进行了封装!

在这里插入图片描述

2、Introspector

获取Bean类信息方法:
	BeanInfo getBeanInfo(Class cls)
 	通过传入的类信息,得到这个Bean类的封装对象

3、BeanInfo

常用方法:
	MethodDescriptor[] getPropertyDescriptors():获取bean类的ger/set方法  数组

4、MethodDescriptor

常用方法:
	1.Method getReadMehtod(); // 获取一个get方法
	2.Method getWriteMethod(); //获取一个set方法
有可能返回null  记得加判断!

5、案例展示

Express.java

package demoBean;

import java.io.Serializable;
/*
* Bean类 -- Express
*       拥有无参构造器
*       所有属性私有, 所有属性提供get/set方法
*       实现了序列化接口 -- implements Serializable
* */
public class Express implements Serializable {
    private String number;
    private String name;
    private String phoneNumber;
    private String address;
    private boolean flag;

    public Express() {
    }

    @Override
    public String toString() {
        return "Express{" +
                "number='" + number + '\'' +
                ", name='" + name + '\'' +
                ", phoneNumber='" + phoneNumber + '\'' +
                ", address='" + address + '\'' +
                ", flag=" + flag +
                '}';
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    public String getName() {
        return name;
    }

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

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

Demo01.java

package demoBean;

import java.beans.*;

public class Demo01 {
    public static void main(String[] args) throws IntrospectionException {
        //获取Express类
        Class c = Express.class;
        //获取Express这个Bean类的封装对象
        BeanInfo beanInfo = Introspector.getBeanInfo( c );
        //获取属性
        PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
        //循环遍历
        for (PropertyDescriptor pd:pds){
            System.out.println("属性" + pd.getName() + "的get方法:" + pd.getReadMethod());
            System.out.println("属性" + pd.getName() + "的set方法:" + pd.getWriteMethod());

            System.out.println("属性对应的类型:" + pd.getPropertyType());

        }
        /*
        * 注意:如果属性是Boolean类型,则get方法是is+属性名
        * 在定义Bean类时,必须严格遵守定义规则
        * 属性class的get方法:public final native java.lang.Class java.lang.Object.getClass()
        * 属性class的set方法:null
        * */
    }
}

输出结果:

属性address的get方法:public java.lang.String demoBean.Express.getAddress()
属性address的set方法:public void demoBean.Express.setAddress(java.lang.String)
属性对应的类型:class java.lang.String
属性class的get方法:public final native java.lang.Class java.lang.Object.getClass()
属性class的set方法:null
属性对应的类型:class java.lang.Class
属性flag的get方法:public boolean demoBean.Express.isFlag()
属性flag的set方法:public void demoBean.Express.setFlag(boolean)
属性对应的类型:boolean
属性name的get方法:public java.lang.String demoBean.Express.getName()
属性name的set方法:public void demoBean.Express.setName(java.lang.String)
属性对应的类型:class java.lang.String
属性number的get方法:public java.lang.String demoBean.Express.getNumber()
属性number的set方法:public void demoBean.Express.setNumber(java.lang.String)
属性对应的类型:class java.lang.String
属性phoneNumber的get方法:public java.lang.String demoBean.Express.getPhoneNumber()
属性phoneNumber的set方法:public void demoBean.Express.setPhoneNumber(java.lang.String)
属性对应的类型:class java.lang.String

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值