枚举类
类的对象只有有限个,确定的。
称之为枚举类
例如: 星期:
星期一......星期天。 性别:男、女 季节:春、夏、秋、冬。 线程状态:创建、就绪、运行、阻塞、死亡。
自定义枚举类:
枚举类的属性
·枚举类对象的属性不应允许被改动 , 所以应该使用 private final 修饰。
·枚举类的使用 private final 修饰的属性应该在构造器中为其赋值。
·若枚举类显式的定义了带参数的构造器 , 则在列出枚举值时也必须对应的 传入参数
枚举类的使用
- 枚举类的理解:类的对象只有有限个,确定的。我们称此类为枚举类。
- 当需要定义一组常量时,强烈建议使用枚举类。
- 若枚举只有一个对象 , 则可以作为一种单例模式的实现方式。
如何定义枚举类
方式一:JDK1.5 之前需要自定义枚举类。
方式二:JDK1.5 新增的 enum 关键字用于定义枚举类。
使用 enum 关键字定义枚举类
- 使用 enum 定义的枚举类默认继承了 java.lang.Enum 类,因此不能再继 承其他类。
- 枚举类的构造器只能使用 private 权限修饰符。
- 枚举类的所有实例必须在枚举类中显式列出 (, 分隔 ; 结尾 )。
- 列出的实 例系统会自动添加 public static final 修饰。
- 必须在枚举类的第一行声明枚举类对象。
- JDK 1.5 中可以在 switch 表达式中使用 Enum 定义的枚举类的对象作为 表达式 , case 子句可以直接使用枚举值的名字 , 无需添加枚举类作为限定。
- 使用 enum 关键字定义枚举类。
说明:定义的枚举类默认继承于 java.lang.Enum 类
Enum 类中的常用方法
Enum 类的主要方法:
values() 方法:返回枚举类型的对象数组。
该方法可以很方便地遍历所 有的枚举值。
valueOf(String str):可以把一个字符串转为对应的枚举类对象。
要 求字符串必须是枚举类对象的“名字”。
如不是,会有运行时异常: IllegalArgumentException。 toString():返回当前枚举类对象常量的名称
反射
关注的点:一个方法区,一个栈,一个堆。
JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制
Class对象:在jvm中的一份份字节码,Class对象表示在jvm中的类或者接口,枚举是一种特殊的类,注解是一种特殊的接口,相同的类的对应的Class对象在堆中只有一个
当程序第一次使用某个类(例如:java.util.Date类)的时候,就会把该类的字节码(编译生成的字节码),加载进虚拟机,并创建一个Class对象(字节码对象),此时的Class对象可以表示java.util.Date类的字节码, 但是Class 类可以表示N个类的字节码对象,问题:要怎么区分Class类此时表示的是哪一个类的字节码呢?
Class 类没有公共的构造方法, Class 对象是在类加载的时候由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的, 因此不能显式地声明一个 Class 对象. 一个类被加载到内存并供我们使用需要经历如下三个阶段:
加载, 这是由类加载器 (ClassLoader) 执行的. 通过一个类的全限定名来获取其定义的二进制字节流(Class 字节码), 将这个字节流所代表的静态存储结构转化为方法去的运行时数据接口, 根据字节码在 java 堆中生成一个代表这个类的 java.lang.Class<?> 对象。
链接. 在链接阶段将验证 Class 文件中的字节流包含的信息是否符合当前虚拟机的要求, 为静态域分配存储空间并设置类变量的初始值(默认的零值), 并且如果必需的话, 将常量池中的符号引用转化为直接引用。
初始化. 到了此阶段, 才真正开始执行类中定义的 java 程序代码. 类静态变量初始化赋值,执行静态代码块, 如果该类有父类的话, 则优先对其父类进行初始化。
解决方案:Class类的设计者使用了泛型Class
使用 Class <?> static forName(String className)方法
forName()方法是Class类中的静态方法,Class.forName()会装入类并做类的初始化,返回Class对象。应该是在“链接”阶段,或者“初始化”阶段返回Class对象。
Class c = Class.forName("javacore.entity.Student");
class是Java中每个类都有的一个静态属性class,类名.class是使 JVM将使用类装载器将类装入内存(前提是类还没有装入内存),不做类的初始化工作,返回 Class 对象。应该就是在“加载”之后,“初始化”之前返回Class对象。
代码示例:
Class c = Student.class;
对象.getClass();
getClass()方法是Objdect类中的final native方法,引用名.getClass()会对类进行静态初始化、非静态初始化,返回引用运行时真正所指的对象(因为子对象的引用可能会赋给父对象的引用变量中)所属的类的 Class 对象。在“使用”阶段中引用真正所指的对象。
代码示例:
Student stu=new Student(); Class c = stu.getClass();
Field[] fields; 存储类的所有成员变量。
Constructors[] cons; 存储类的所有构造方法。
Method[] methods; 存储所有的成员方法。
注:所有的new 对象的操作,都是通过Class类创建出来的。
1. 当虚拟机启动,先初始化`main`方法所在的类 2. `new`一个类的对象 3. 调用类的静态成员(除了final常量)和静态方法 4. 使用`java.lang.reflect`包的方法对类进行反射调用 5. 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
举例:new一个String对象,通过反射机制,Class类中加载了String类的所有属性,因此把String类中的所有成员方法放在了Class类的Method[] 数组中。我们通过引用去调用时,此时就会看到该数组中的所有方法。如下图所示:
2.3 获取类的成员变量
1.Filed[] getFields(); 获取所有public修饰的成员变量.
2.Field getField(String name); 获取指定名称的public修饰的成员变量.
3.Field[] getDeclareFields();获取所有的成员变量,不考虑修饰符
4.Field getDeclaredField(String name);
获取成员变量的值:
使用Field类中的方法:Object get(Object obj);
设置成员变量的值:
使用Filed类中的方法: void set(Object obj,Object value)
代码示例:
获取public修饰的成员变量
//创建Class对象 Class c = Class.forName("day05.Animal"); //通过指定public修饰的成员名称获取到Field对象 Field f = c.getField("name"); Animal ani = new Animal(); //设置Animal类中name的值 f.set(ani,"小喵喵"); //获取值 Object value = f.get(ani); System.out.println(value);
获取全部的成员变量
Class c = Class.forName("day05.Animal"); Field f = c.getDeclaredField("sex"); Animal ani = new Animal(); //此时,不能获取到private修饰的成员属性值,需要设置暴力反射,忽略权限的检查. f.setAccessible(true); Object o = f.get(ani); System.out.println(o);
2.4 获取类的构造方法
1.Constructor<?>[] getConstrctors(); 获取所有public修饰的构造方法
2.Constructor<T> getConstructor(类<?>...paramterTypes); 获取public修饰的公共有参构造方法
3.Constructos<T>getDeclaredConstructor(类<?>...parameterTypes); 获取任何类型的公共有参构造方法
4.Constructors<?>[] getDeclaredConstructors(); 获取所有的构造方法
代码示例:
Class<Animal> ani = Animal.class; //获取有参的构造方法,创建对象 Constructor<Animal> cons = ani.getConstructor(String.class, char.class, int.class); //调用newInstance()方法,创建对象 Animal ani1= cons.newInstance("狗", '雌', 12); System.out.println(ani1); //获取无参的构造方法创建对象 Constructor<Animal> cons2 = ani.getConstructor(); //调用newInstance()方法,创建对象 Animal ani2 = cons2.newInstance(); //还可以使用Class类中的newInstance方法调用无参构造方法创建对象 Animal ani3 = ani.newInstance();
2.5 获取类的方法
1.Method[] getMethods();
2.Method getMethod(String name,类<?>...parameterTypes);
3.Method[] getDeclareMethods();
4.Method getDeclaredMethods(String name,类<?>...parameterTypes);
代码示例:
Class<Animal> ani = Animal.class; //获取指定名称的空参方法 Method m = ani.getMethod("sleep"); Animal animal=new Animal(); //执行方法 m.invoke(animal); //获取有参的方法 Method m2 = ani.getMethod("eat", String.class); m2.invoke(animal,"大米")
获取所有方法
Class<Animal> ani = Animal.class; //获取所有方法 Method[] methods = ani.getMethods(); for (Method method : methods) { //获取方法名称 System.out.println(method.getName()); } }
案例:
创建一个配置文件:
ClassName=day05.Animal//类的全路径 MethodName=eat//类的方法名
//加载配置文件的内容到一个集合中 Properties pro = new Properties(); InputStream in = Test.class.getClassLoader().getResourceAsStream("util.properties"); pro.load(in); //获取集合中的类名,和方法名 String className = pro.getProperty("ClassName"); String methodName = pro.getProperty("MethodName"); //通过反射创建对象 Class<?> c = Class.forName(className); Object obj = c.newInstance(); //调用方法 Method method = c.getMethod(methodName,String.class); method.invoke(obj,"饭");