java笔记-枚举、反射、注解、序列化、泛型

枚举

枚举的用法

枚举的简单用法
    /**
    * 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
    }

枚举与单例

参考:https://blog.csdn.net/javazejian/article/details/71333103

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;
        }
    }

枚举的序列化如何实现、枚举的线程安全性问题

参考:https://www.cnblogs.com/MyStringIsNotNull/p/8018243.html

类型信息和反射

1、RTTI的概念以及Class对象作用

**RTTI(Run-Time Type Identification):**运行时类型识别,C++中概念,Java中主要由Class对象实现。

Class对象解释:

  1. Class也是一个普通类,与class关键字不一样
  2. 每个通过class标识的类,无论创建多少个实例对象在jvm中都只存在一个Class对象
  3. Class只有私有化构造函数,所有只能由jvm创建和加载
  4. 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;
}

总结

  1. 当第一次使用类的静态变量时会加载类,若有父类(且未加载)时会先加载父类,后加载子类
  2. 使用new关键字实例化对象时会加载类,若有父类(且未加载)时会先加载父类,后加载子类
  3. 使用反射技术动态获取类的Class对象时会加载类,若有父类(且未加载)时会先加载父类,后加载加载子类
  4. 接口同理,但不会直接加载父类,只有在使用到父类时才会加载所使用的父类
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;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值