一、枚举
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、枚举常用方法
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 个 Annotation 对象,都会有唯一的 RetentionPolicy 属性;至于 ElementType 属性,则有 1~n 个。
- ElementType(注解的用途类型)
package java.lang.annotation;
public enum ElementType{
TYPE, /*类、接口(包括注释类型)或枚举声明*/
FIFLD, /*字段声明(包括枚举常量)*/
METHOD, /*方法声明*/
PARAMETER, /*参数声明*/
CONSTRUCTOR, /*构造方法声明*/
LOCAL_VARIABLE, /*局部变量声明*/
ANNOTION_TYPE, /*注解类型声明*/
PACKAGE /*包声明*/
}
- 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