枚举
枚举的用法
枚举的简单用法
/**
* Java中每个枚举都继承自java.lang.Enum类,当定义一个枚举类时,每个枚举类型成员都可以看做时Enum类的实例,这些成员默认都被
* public static final 修饰,当使用枚举类型成员是,直接使用枚举名称调用成员即可
* 所有枚举类都可以调用Enum类的方法
* values():已数组形式返回枚举类型所有成员
* valueOf():将普通字符串类型转换为枚举实例
* compareTo():比较两个枚举成员在定义时的顺序
* ordinal():获取枚举成员的索引位置
*
*/
public enum Color {
RED,BLUE,GREEN,BLACK,GRAY,YELLOW
}
@Test
public void test01(){
Color[] values = Color.values();
for (int i = 0;i < values.length;i++){
System.out.println(values[i]);//RED,BLUE,GREEN,BLACK,GRAY,YELLOW
}
Color red = Color.valueOf("RED");
int i = red.compareTo(Color.BLACK);
System.out.println(i);//-3
System.out.println(Color.BLACK.ordinal());//3
}
枚举中添加方法
public enum WeekDay {
//定义枚举成员,必须先定义,且最后必须以";"结尾,若要添加全局变量,需要重写构造函数,并将构造函数用private修饰
Mon("Monday"),Tue("Tuesday"),Wed("Wednesday"),Thu("Thursday"),Fri("Friday"),Sat("Saturday"),Sun("Sunday");
private final String day;
/**
* 构造函数只能用private|default修饰
* @param day
*/
private WeekDay(String day){
this.day = day;
}
public String getDay() {
return day;
}
}
@Test
public void test02(){
WeekDay[] values = WeekDay.values();
for (int i = 0;i < values.length;i++){
System.out.print(values[i]+",");//Mon,Tue,Wed,Thu,Fri,Sat,Sun,
}
System.out.println();
System.out.println(WeekDay.Fri.getDay());//Friday
}
枚举中添加抽象方法
public enum Sex {
male{
public String getInfo(){
return "This is a male";
}
},female{
public String getInfo(){
return "This is a female";
}
};
public abstract String getInfo();
}
@Test
public void test03(){
System.out.println(Sex.male.getInfo());//This is a male
System.out.println(Sex.female.getInfo());//This is a female
}
枚举与单例
参考:
Java 枚举如何比较、switch 对枚举的支持
枚举可以直接使用""进行比较 ,因为在Enum类里面,已经重写了equals方法,而方法里面比较就是直接使用,所有==与equals等价
public final boolean equals(Object other) {
return this==other;
}
//switch 对枚举的支持
@Test
public void test04(){
Sex male = Sex.male;
switch (male){
case male:
System.out.println("this is a male");
break;
case female:
System.out.println("this is a female");
break;
}
}
枚举的序列化如何实现、枚举的线程安全性问题
参考:
类型信息和反射
1、RTTI的概念以及Class对象作用
**RTTI(Run-Time Type Identification):**运行时类型识别,C++中概念,Java中主要由Class对象实现。
Class对象解释:
- Class也是一个普通类,与class关键字不一样
- 每个通过class标识的类,无论创建多少个实例对象在jvm中都只存在一个Class对象
- Class只有私有化构造函数,所有只能由jvm创建和加载
- Class类的对象作用是运行时提供或获得某个对象的类型信息
2、Class对象的加载及其获取方式
类的生命周期:加载–>验证–>准备–>解析(可能会发生在初始化后)–>初始化–>使用–>卸载;
其中(验证,准备,解析)三个阶段统称为连接
类加载时机
package com.learn.javaClass;
/**
* 类加载时机演示
*/
public class LoadingDemo {
public static void main(String[] args) throws Exception{
//调用父类的静态变量
//System.out.println(SupClass.name);
// out SupClass init
// SupClass name
//调用父类的常量静态变量
//System.out.println(SupClass.age);
// out SupClass init
// 13
//调用子类静态变量
//System.out.println(SubClass.weight);
/*
* out
* SupClass init
* SubClass init
* 178
*/
//使用new实例化父类
//new SupClass();//out:SupClass init
//使用new 实例化子类
//new SubClass();//SupClass init SubClass init
//使用反射
//Class.forName("com.learn.javaClass.SupClass");//out:SupClass init
Class.forName("com.learn.javaClass.SubClass");
//SupClass init
//SubClass init
}
}
class SupClass{
static {
System.out.println("SupClass init");
}
public static String name = "SupClass name";
public final static Integer age = 13;
}
class SubClass extends SupClass{
static {
System.out.println("SubClass init");
}
public static Integer weight = 178;
}
总结
- 当第一次使用类的静态变量时会加载类,若有父类(且未加载)时会先加载父类,后加载子类
- 使用new关键字实例化对象时会加载类,若有父类(且未加载)时会先加载父类,后加载子类
- 使用反射技术动态获取类的Class对象时会加载类,若有父类(且未加载)时会先加载父类,后加载加载子类
- 接口同理,但不会直接加载父类,只有在使用到父类时才会加载所使用的父类
2.1 类的加载过程
加载过程:1.通过类的全限定名称来获取类的二进制流
2.将二进制的静态存储结构存入到方法区的运行时数据接口
3.在内存中生成一个java.lang.Class对象,作为方法区这个类的各种数据访问的入口
校验过程:1.文件格式验证(可以正确的把流存储到方法区中)
2.元数据验证 (保证不符合java语言规范的元数据信息)
3.字节码验证 (确保验证类的方法在运行的时候不会对jvm产生危害)
4.符号引用验证
javassist使用方法
概述
用来 处理 Java 字节码的类库。它可以在一个已经编译好的类中添加新的方法,或者是修改已有的方法,并且不需要对字节码方面有深入的了解。同时也可以去生成一个新的类对象,通过完全手动的方式
使用方法
需要先引入jar包
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.25.0-GA</version>
</dependency>
package com.learn.javaClass.learnJavassist;
import javassist.*;
import java.io.IOException;
/**
* javassist 中 最重要的几个类
* ClassPool:一个基于HashMap实现的CtClass对象容器,其中键是类名称,值是表示该类的CtClass对象。
* 默认的ClassPool使用与底层JVM相同的类路径,因此在某些情况下,可能需要向ClassPool添加类路径或类字节
* getDefault():静态方法,返回默认的ClassPool,单列模式获取
* appendClassPath,insertClassPath:将一个ClassPath加到类搜索路径的末尾位置货插入到起始位置,通常通过该方法写入额外
* 的类搜索路径,以解决多个类加载器环境中找不到类的尴尬;
* toClass():将修改后的CtClass加载至当前线程的上下文类加载器中,CtClass的toClass方法是通过调用本方法实现。
* 需要注意的是一旦调用该方法,则无法继续修改已经被加载的class;
* get(),getCtClass(): 根据类路径名获取该类的CtClass对象,用于后续的编辑。
* CtClass:表示一个类,这些CtClass对象可以从ClassPool获得
* CtMethods:表示类中的方法
* CtFields:表示类中的字段
*/
public class CreatePerson {
/**
* 动态生成一个类
*/
public static void dynGenerateClass() throws CannotCompileException, NotFoundException, IOException {
//获取ClassPool
ClassPool pool = ClassPool.getDefault();
//创建一个Person类
CtClass ctClass = pool.makeClass("com.learn.javaClass.learnJavassist.Person");
//新增字段
CtField nameField = new CtField(pool.get("java.lang.String"), "name", ctClass);
//设置访问权限为private
nameField.setModifiers(Modifier.PRIVATE);
//初始化值
ctClass.addField(nameField,CtField.Initializer.constant("xiaoming"));
//生成getter、setter方法
ctClass.addMethod(CtNewMethod.setter("setName",nameField));
ctClass.addMethod(CtNewMethod.getter("getName",nameField));
//添加无参构造函数
CtConstructor ctConstructor = new CtConstructor(new CtClass[]{}, ctClass);
ctClass.addConstructor(ctConstructor);
//添加有参构造函数
CtConstructor ctConstructor1 = new CtConstructor(new CtClass[]{pool.get("java.lang.String")}, ctClass);
ctConstructor1.setBody("{name=\"xiaohong\"}");
ctClass.addConstructor(ctConstructor1);
//创建一个名为printName方法,无参数,无返回值,输出name值
CtMethod ctMethod = new CtMethod(CtClass.voidType, "printName", new CtClass[]{}, ctClass);
ctMethod.setModifiers(Modifier.PUBLIC);
ctMethod.setBody("{System.out.println(name);}");
ctClass.addMethod(ctMethod);
ctClass.writeFile("E:\\learn_source\\java\\source\\class_fx_zj");
}
public static void main(String[] args) throws IOException, CannotCompileException, NotFoundException {
dynGenerateClass();
}
}
序列化与反序列化
概念
序列化是指把对象转换为字节序列的过程,而反序列化是指把字节序列恢复为对象的过程
作用
在传递和保存对象的时候,可以保证对象的完整性和可传递性。核心作用就是对象状态的保存与重建。以便在网络上传输或者保存在本地文件中。
序列化与单例模式
package com.learn.javaSerilized;
import java.io.Serializable;
/**
* 序列化
*/
public class SingletonClass implements Serializable {
//私有化构造函数,防止在外部实例化
private SingletonClass(){}
/**
* 声明实例名,使用volatile保存可见性
*/
private volatile static SingletonClass singletonClass;
//获取实例方法,使用双重校验
public static SingletonClass getInstance(){
if(singletonClass == null){
synchronized (SingletonClass.class){
if(singletonClass == null){
singletonClass = new SingletonClass();
}
}
}
return singletonClass;
}
}
package com.learn.javaSerilized;
import java.io.Serializable;
/**
* 防止序列化对单列的破坏
*/
public class SingletonClass02 implements Serializable {
//私有化构造函数,防止在外部实例化
private SingletonClass02(){}
/**
* 声明实例名,使用volatile保存可见性
*/
private volatile static SingletonClass02 singletonClass;
//获取实例方法,使用双重校验
public static SingletonClass02 getInstance(){
if(singletonClass == null){
synchronized (SingletonClass02.class){
if(singletonClass == null){
singletonClass = new SingletonClass02();
}
}
}
return singletonClass;
}
private Object readResolve(){
return singletonClass;
}
}
package com.learn.javaSerilized;
import java.io.*;
public class Demo {
public static void main(String[] args) throws Exception {
//序列化对象
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("templateFile"));
out.writeObject(SingletonClass.getInstance());
//从文件中读取对象
ObjectInputStream in = new ObjectInputStream(new FileInputStream("templateFile"));
SingletonClass singletonClass = (SingletonClass) in.readObject();
System.out.println(singletonClass == SingletonClass.getInstance());//false
//序列化SingletonClass02对象,添加了readResolve方法
out = new ObjectOutputStream(new FileOutputStream("templateFile02"));
out.writeObject(SingletonClass02.getInstance());
in = new ObjectInputStream(new FileInputStream("templateFile02"));
SingletonClass02 singletonClass02 = (SingletonClass02) in.readObject();
System.out.println(singletonClass02 == SingletonClass02.getInstance());//true
}
}
总结:
对象的序列化过程通过ObjectOutputStream和ObjectInputputStream来实现的,而ObjectInputStream的
readObject
的调用栈为readObject--->readObject0--->readOrdinaryObject--->checkResolve
注解
package com.learn.javaAnnnotation;
import java.lang.annotation.*;
/**
* 定义注解使用@interface关键字
* 元注解;
* @Retention : 标识注解所作用的阶段
* SOURCE:该类型的注解信息只会保留在源码里,源码经过编译后
* ,注解信息会被丢弃,不会保留在编译好的class文件里
* CLASS(默认值):注解在class文件中可用,但会被VM丢弃(该类型的注解信息会
* 保留在源码里和class文件里,在执行的时候,不会加载到虚拟机中),
* RUNTIME:注解信息将在运行期(JVM)也保留,因此可以通过反射机制读取注解的信息(
* 源码、class文件和执行的时候都有注解的信息)
*
* @Target : 用来约束注解可用的地方,
* TYPE:标明该注解可以用于类、接口(包括注解类型)或enum声明
* FIELD:标明该注解可以用于字段(域)声明,包括enum实例
* METHOD:标明该注解可以用于方法声明
* PARAMETER:标明该注解可以用于参数声明
* CONSTRUCTOR:标明该注解可用于构造函数
* LOCAL_VARIABLE:标明该注解可用于局部变量声明
* ANNOTATION_TYPE:标明注解可以用于注解声明
* PACKAGE:标明该注解可用于包声明
* TYPE_PARAMETER:标明该注解可用于类型参数,1.8+
* TYPE_USE:类型使用声明,1.8+
*
* @Documented : 标识此注解会生成到javadoc中
*
* @Repeatable : 标识次注解可重复使用
*
* @Inherited : 用此注解标识的类的子类可以继承父类的该注解
*/
@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.ANNOTATION_TYPE})
public @interface Demo {
/**
* 定义注解成员变量
*
* 可用数据类型
* 八种基本数据类型(byte,char,short,int,float,double,long,boolean)
* String
* Class,
* enum,
* Annotation,
* 及以上类型的数组
*
*/
String value() default "";
String name();
}
练习
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBTable {
String name() default "";
}
/**
* 约束注解
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Constraints {
/*标识该字段是否为主键*/
boolean primaryKey() default false;
/*标识该字段是允许为空*/
boolean allowNull() default false;
/*标识字段值是否唯一*/
boolean unique() default false;
}
/**
* int类型标识注解
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLInteger {
/*该字段对应数据库列名*/
String name() default "";
/*内嵌注解,标识此字段是否需要约束*/
Constraints constraints() default @Constraints;
}
/**
* String类型标识注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SQLString {
/*标识字段名*/
String name() default "";
/*标识该字段的长度*/
int length() default 0;
Constraints constraints() default @Constraints;
}
/**
* 运行时注解处理器
*/
public class TbCreator {
public static String createTableSql(String className) throws Exception{
//加载使用注解的类对象
Class<?> aClass = Class.forName(className);
//获取当前类上的DBTable注解
DBTable dbTable = aClass.getAnnotation(DBTable.class);
//判断当前类是否存在注解
if(dbTable == null){
System.out.println(className+"类上没有注解");
return null;
}
String tableName = dbTable.name();
if("".equals(tableName)){
tableName = aClass.getSimpleName().toUpperCase();
}
LinkedList<String> columnRefs = new LinkedList<>();
// 遍历所有字段
for (Field field : aClass.getFields()) {
String columnName = null;
//获取字段上的注解
SQLInteger intAnnotation = field.getAnnotation(SQLInteger.class);
SQLString stringAnnotation = field.getAnnotation(SQLString.class);
if(intAnnotation == null && stringAnnotation == null){
continue;
}else if(intAnnotation != null){
String fieldName = intAnnotation.name();
if("".equals(fieldName)){
columnName += field.getName().toUpperCase();
}else{
columnName += fieldName;
}
columnRefs.add(columnName + " INT " + getConstraints(intAnnotation.constraints()));
}else if(stringAnnotation != null){
String fieldName = stringAnnotation.name();
if("".equals(fieldName)){
columnName += field.getName().toUpperCase();
}else{
columnName += fieldName;
}
columnRefs.add(columnName + " varchar("+stringAnnotation.length()+") " + getConstraints(intAnnotation.constraints()));
}
StringBuilder sb = new StringBuilder();
sb.append("create table "+tableName+"(");
for (String ref : columnRefs) {
sb.append(ref);
}
return sb.substring(0,sb.length()-1).concat(")");
}
return null;
}
private static String getConstraints(Constraints constraints){
String constrain = "";
if(constraints.primaryKey()){
constrain += " PRIMARY KEY";
}
if(constraints.allowNull()){
constrain += " NOT NULL ";
}
if(constraints.unique()){
constrain += " unique ";
}
return constrain;
}
}