Java笔记23——Java枚举、注解与反射

Java学习——Java枚举、注解与反射


一、枚举

在C和Objective-C等其他语言中,枚举用来管理一 组相关常量的集合,使用枚举可以提高程序的可读 性,使代码更清晰且更易于维护。 在Java 5之前没有提供枚举类型,可以通过声明静态常量(final static变量)替代枚举常量。
例:

public static final int LOW = 1;
public static final int MEDIUM = 1;
public static final int HIGH = 1;

1. 枚举的使用

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

例:

public enum Level {
    LOW(30), MEDIUM(15), HIGH(7), URGENT(1);
    private int levelValue;

    private Level(int levelValue) {
        this.levelValue = levelValue;
    }

    public int getLevelValue() {
        return levelValue;
    }
}

2. 枚举类的主要方法

在这里插入图片描述

3. 实现接口的枚举类

所有的枚举都继承自java.lang.Enum类。由于Java 不支持多继承,所以枚举对象不能再继承其他类。 每个枚举对象,都可以实现自己的抽象方法
例:

public enum Level implements LevelShow {
    LOW(1){
        @Override
        public void show() {
            System.out.println("低等级 ");
        }
    },MEDIUM(5) {
        @Override
        public void show() {
            System.out.println("中等级 ");
        }
    },HIGH(10) {
        @Override
        public void show() {
            System.out.println("高等级 ");
        }
    };

    private int levelValue;

    private Level(int levelValue) {
        this.levelValue = levelValue;
    }

    private Level() { }

    public int getLevelValue() {
        return levelValue;
    }
}


interface LevelShow{
    void show();
}

测试类:

public class Demo1 {
    public static void main(String[] args) {

        System.out.println(Level.HIGH.getLevelValue());
        Level.MEDIUM.show();
    }

    public static void judge(Level l){
        switch (l){
            case HIGH:
                System.out.println("高等级!");
                break;
            case MEDIUM:
            case LOW:
                System.out.println("中低等级!");
                break;
        }
    }
}

输出结果:
在这里插入图片描述

注意事项:
一旦定义了枚举,最好不要妄图修改里面的值,除非修改是必要的。

  • 枚举类默认继承的是java.lang.Enum类而不是Object类
  • 枚举类不能有子类,因为其枚举类默认被final修饰
  • 只能有private构造方法
  • switch中使用枚举时,直接使用常量名,不用携带类名
  • 不能定义name属性,因为自带name属性
  • 不要为枚举类中的属性提供set方法,不符合枚举最初设计初衷。

二、注解(标注)

Java 5之后可以在源代码中嵌入一些补充信息,这种补充信息称为注解(Annotation),例如在方法 覆盖中使用过的@Override注解,注解都是@符号开头的。

注解并不能改变程序运行的结果,不会影响程序运行的性能。有些注解可以在编译时给用户提示或警告,有的注解可以在运行时读写字节码文件信息。

Java 语言中的类、方法、变量、参数和包等都可以被标注。和注释不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标注。

  • 主要用于:

编译格式检查
反射中解析
生成帮助文档
跟踪代码依赖

1. 内置注解与元注解

  • @Override : 重写

     定义在java.lang.Override
    
  • @Deprecated:废弃

     定义在java.lang.Deprecated
    
  • @SafeVarargs

     Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
    
  • @FunctionalInterface: 函数式接口

     Java 8 开始支持,标识一个匿名函数或函数式接口。
    
  • @Repeatable:标识某注解可以在同一个声明上使用多次

     Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
    
  • @SuppressWarnings:抑制编译时的警告信息。

     定义在java.lang.SuppressWarnings
     三种使用方式:
      @SuppressWarnings("unchecked") [^ 抑制单类型的警告] 
      @SuppressWarnings("unchecked","rawtypes") [^ 抑制多类型的警告] 
      @SuppressWarnings("all") [^ 抑制所有类型的警告]
    

在这里插入图片描述

2.元注解

用途:作用在其他注解的注解

元注解是为其他注解进行说明的注解,当自定义一 个新的注解类型时,其中可以使用元注解。

元注解包括:@Documented、@Target、 @Retention、@Inherited、@Repeatable和@Native。

  1. @Documented

如果在一个自定义注解中引用@Documented注 解,那么该注解可以修饰代码元素(类、接 口、成员变量和成员方法等),javadoc等工具 可以提取这些注解信息。

  1. @Target

@Target注解用来指定一个新注解的适用目标。 @Target注解有一个成员(value)用来设置适用 目标,value java.lang.annotation.ElementType 枚举类型的数组,ElementType描述Java程序元素类型,它有10个枚举常量,如下图所示:

在这里插入图片描述

  1. @Retention

@Retention注解用来指定一个自定义注解的有效范围,@Retention注解有一个成员(value)用来设置保留策略,value是 java.lang.annotation.RetentionPolicy枚举类型, RetentionPolicy描述注解保留策略,它有3个枚举常量,如下图所示:
在这里插入图片描述

  1. @Inherited

@Inherited注解用来指定一个自定义注解可以被继承。假定一个类A被该新注解修饰,那么这个A 类的子类会继承该新注解。

注意:
(1). 子类会继承父类使用的注解中被@Inherited修饰的注解
(2). 接口继承关系中,子接口不会继承父接口中的任何注解,不管父接口中使用的注解有没有 被@Inherited修饰
(3). 类实现接口时不会继承任何接口中定义的注解

  1. @Repeatable

@Repeatable注解是Java 8新增加的,它允许在相同的程序元素中重复注释,可重复的注释必须使用@Repeatable进行注释。

  1. @Native

@Native注解一个成员变量,指示这个变量可以被本地代码引用。常常被代码生成工具使用。

我们在自定义注解示范若何使用常用的几个元注解。

3.自定义注解

注解架构:
在这里插入图片描述

  • (1). Annotation与RetentionPolicy 与ElementType 。

每 1 个 Annotation 对象,都会有唯一的 RetentionPolicy 属性;至于 ElementType 属性,则有 1~n个。

  • (2). ElementType(注解的用途类型)

“每 1 个 Annotation” 都与 “1~n 个 ElementType” 关联。当 Annotation 与某个 ElementType 关联时,就意味着:Annotation有了某种用途。例如,若一个 Annotation 对象是 METHOD 类型,则该Annotation 只能用来修饰方法。

public enum ElementType {
    TYPE,   /* 类、接口(包括注释类型)或枚举声明 */ 
    FIELD,  /* 字段声明(包括枚举常量) */ 
    METHOD,     /* 方法声明 */ 
    PARAMETER,  /* 参数声明 */ 
    CONSTRUCTOR,    /* 构造方法声明 */ 
    LOCAL_VARIABLE,     /* 局部变量声明 */ 
    ANNOTATION_TYPE,    /* 注释类型声明 */ 
    PACKAGE     /* 包声明 */
}
  • (03) RetentionPolicy(注解作用域策略)

“每 1 个 Annotation” 都与 “1 个 RetentionPolicy” 关联。

	a) 若 Annotation 的类型为 SOURCE,则意味着:Annotation仅存在于编译器处理期间,编译器处理完之后,该 Annotation 就没用了。 例如," @Override" 标志就是一个Annotation。当它修饰一个方法的时候,就意味着该方法覆盖父类的方法;并且在编译期间会进行语法检查!编译器处理完后,"@Override"就没有任何作用了。

	b) 若 Annotation 的类型为 CLASS,则意味着:编译器将 Annotation 存储于类对应的 .class 文件中,它是 Annotation 的默认行为。
	
	c) 若 Annotation 的类型为 RUNTIME,则意味着:编译器将 Annotation 存储于 class 文件中,并且可由JVM读入。

public enum RetentionPolicy {

    SOURCE,     /* Annotation信息仅存在于编译器处理期间,编译器处理完之后就没有该 Annotation信息了 */ 
    CLASS,      /* 编译器将Annotation存储于类对应的.class文件中。默认行为 */ 
    RUNTIME     /* 编译器将Annotation存储于class文件中,并且可由JVM读入 */
}

定义格式:

@interface 自定义注解名{}

注意事项:

  1. 定义的注解,自动继承了java.lang,annotation.Annotation接口

  2. 注解中的每一个方法,实际是声明的注解配置参数

     方法的名称就是 配置参数的名称
     方法的返回值类型,就是配置参数的类型。只能是:基本类型/Class/String/enum
    
  3. 可以通过default来声明参数的默认值

  4. 如果只有一个参数成员,一般参数名为value

  5. 注解元素必须要有值,我们定义注解元素时,经常使用空字符串、0作为默认值。

举例:

import java.lang.annotation.*;

@MyAnnotation(value = "1",name = {"王二","李四"})   //默认指定为注解里面的value参数,否则需要指定参数名(多参数情况下均需要指定)
public class Demo2 {
    @MyAnnotation(value = "2")
    public static void main(String[] args) {
        //这里是错误用法,该注解没有指定可以在属性上使用
        @MyAnnotation(value = "2")  //错误用法
        int a = 0;
    }
}

/**
 * 自定义注解,以下四个为元注解
 */
//注解是否包含在文档中
@Documented
//用途类型
@Target({ElementType.TYPE,ElementType.METHOD})  //参数表示,此注解类与方法可用
//保存策略
@Retention(RetentionPolicy.RUNTIME)
//可以继承
@Inherited
@interface MyAnnotation{    //自定义注解默认继承jajva.lang.Annotation接口
    /**
     * 注解中的每一个方法,实际是声明注解配置参数
     *      方法的名称: 配置参数的名称
     *      方法的返回值类型,就是配置参数的返回值类型。且只能是:基本类型/class/String/enum
     * 可以通过default来声明参数的默认值
     * 如果只有一个参数成员,一般参数名为value
     * 注解元素必须要有值
     * 其中定义的参数通过反射调用
     *
     * @return
     */
    String value();
    int age() default 1;    //指定默认值,则调用个注解可以选择不传此参数
    String[] name() default "麻子";
}

三、反射

反射(Reflection)是程序的自我分析能力,通过反射可以确定类有哪些方法、有哪些构造方法以及有 哪些成员变量。Java语言提供了反射机制,通过反射机制能够动态读取一个类的信息;能够在运行时动态加载类,而不是在编译期。反射可以应用于框架开发,它能够从配置文件中读取配置信息动态加载类、创建对象,以及调用方法和成员变量。

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

注意:反射机制,在一般的Java应用开发上很少使用,一般来说需要开发框架需要用上。

1. Java反射机制API

Java反射机制API主要是 java.lang.Class类和 java.lang.reflect包。

(1). java.lang.Class类

java.lang.Class类是实现反射的关键所在,Class类的 一个实例表示Java的一种数据类型,包括类、接 口、枚举、注解(Annotation)、数组、基本数据 类型和void,void是“无类型”,主要用于方法返回值类型声明,表示不需要返回值。Class没有公有的构造方法,Class实例是由JVM在类加载时自动创建 的。

(2). java.lang.reflect包

java.lang.reflect包提供了反射中用到类.
主要的类:

  • Constructor类:提供类的构造方法信息。
  • Field类:提供类或接口中成员变量信息。
  • Method类:提供类或接口成员方法信息。
  • Array类:提供了动态创建和访问Java数组的方法。
  • Modifier类:提供类和成员访问修饰符信息。
(3). 类加载机制

类加载器
Java类加载器(Java Classloader)是Java运行时环境(Java Runtime Environment)的一部分, 负责动态加载Java类到Java虚拟机的内存空间中。

java默认有三种类加载器,BootstrapClassLoader、ExtensionClassLoader、App ClassLoader。

  • BootstrapClassLoader(引导启动类加载器):
    嵌在JVM内核中的加载器,该加载器是用C++语言写的,主要负载加载JAVA_HOME/lib下的类库,引导启动类加载器无法被应用程序直接使用。
  • ExtensionClassLoader(扩展类加载器):
    ExtensionClassLoader是用JAVA编写,且它的父类加载器是Bootstrap。
    是由sun.misc.Launcher$ExtClassLoader实现的,主要加载JAVA_HOME/lib/ext目录中的类库。
    它的父加载器是BootstrapClassLoader。
  • App ClassLoader(应用类加载器): AppClassLoader是应用程序类加载器,负责加载应用程序classpath目录下的所有jar和class文件。它的父加载器为Ext ClassLoader。
    在这里插入图片描述
    类通常是按需加载,即第一次使用该类时才加载。由于有了类加载器,Java运行时系统不需要知道文件与文件系统(因为Java虚拟机运行的是.class字节码文件)。学习类加载器时,掌握Java的委派概念很重要。

双亲委派模型:如果一个类加载器收到了一个类加载请求,它不会自己去尝试加载这个类,而是把这个请求转交给父类加载器去完成。每一个层次的类加载器都是如此。因此所有的类加载请求都应该传递到最顶层的 启动类加载器中,只有到父类加载器反馈自己无法完成这个加载请求(在它的搜索范围没有找到这个类)时,子类加载器才会尝试自己去加载。委派的好处就是避免有些类被重复加载

(4). 通过反射获取注解信息
  1. 获取Class
  2. 获取Constructor
  3. 获取Method
  4. 获取Field
  5. 获取注解信息

得到Class的几种方式

  1. 如果在编写代码时, 指导类的名称, 且类已经存在, 可以通过 包名.类名.class 得到一个类的 类对象
  2. 如果拥有类的对象, 可以通过 Class 对象.getClass() 得到一个类的 类对象
  3. 如果在编写代码时, 知道类的名称 , 可以通过 Class.forName(包名+类名): 得到一个类的 类对象

注意:上述的三种方式, 在调用时, 如果类在内存中不存在, 则会加载到内存 ! 如果类已经在内存中存在, 不 会重复加载, 而是重复利用 ! (一个class文件 在内存中不会存在两个类对象 )

特殊的类对象

  • 基本数据类型的类对象: 基本数据类型.clss
  • 包装类.type
  • 基本数据类型包装类对象: 包装类.class

获取Constructor

  1. 通过指定的参数类型, 获取指定的单个构造方法 getConstructor(参数类型的class对象数组) 例如:构造方法如下: Person(String name,int age) 得到这个构造方法的代码如下:
Constructor c = p.getClass().getConstructor(String.class,int.class);
  1. 获取构造方法数组 getConstructors();
  2. 获取所有权限的单个构造方法 getDeclaredConstructor(参数类型的class对象数组)
  3. 获取所有权限的构造方法数组 getDeclaredConstructors();

Constructor 创建对象
常用方法:
newInstance(Object... para) 调用这个构造方法, 把对应的对象创建出来 参数: 是一个Object类型可变参数, 传递的参数顺序 必须匹配构造方法中形式参数列表的顺序!
setAccessible(boolean flag) 如果flag为true 则表示忽略访问权限检查 !(可以访问任何权限的方法)

获取Method

通过class对象获取一个类的方法

  1. getMethod(String methodName , class… clss) 根据参数列表的类型和方法名, 得到一个方法(public修饰的)
  2. getMethods(); 得到一个类的所有方法 (public修饰的)
  3. getDeclaredMethod(String methodName , class… clss) 根据参数列表的类型和方法名, 得到一个方法(除继承以外所有的:包含私有, 共有, 保护, 默认)
  4. getDeclaredMethods(); 得到一个类的所有方法 (除继承以外所有的:包含私有, 共有, 保护, 默认)

Method 执行方法

  1. invoke(Object o,Object… para) : 调用方法 , 参数
  2. 要调用方法的对象 参数
  3. 要传递的参数列表 getName() 获取方法的方法名称 setAccessible(boolean flag) 如果flag为true 则表示忽略访问权限检查 ! (可以访问任何权限的方法)

获取Field
通过class对象获取一个类的属性

  1. getDeclaredField(String filedName) 根据属性的名称, 获取一个属性对象 (所有属性)
  2. getDeclaredFields() 获取所有属性
  3. getField(String filedName) 根据属性的名称, 获取一个属性对象 (public属性)
  4. getFields() 获取所有属性 (public)

Field 属性的对象类型
常用方法:

  1. get(Object o ); 参数: 要获取属性的对象 获取指定对象的此属性值

  2. set(Object o , Object value);

    参数1. 要设置属性值的 对象
    参数2. 要设置的值,设置指定对象的属性的值 
    
  3. getName() 获取属性的名称

  4. setAccessible(boolean flag) 如果flag为true 则表示忽略访问权限检查 !(可以访问任何权限的属性)

获取注解信息
获取类/属性/方法的全部注解对象

Annotation[] annotations01 = Class / Field / Method.getAnnotations(); 
for(Annotation annotation :annotations01){
    System.out.println(annotation);
}

根据类型获取类/属性/方法的注解对象

注解类型 对象名 = (注解类型) c.getAnnotation(注解类型.class);

代码示例:
使用到的几个实体类:
Person.java

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

	//无参构造方法
    public Person() {
    }
	//设置的私有一参构造方法
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    private Person(String name) {
        this.name = name;
        this.age = 18;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    private void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", phone='" + phone + '\'' +
                '}';
    }
}

Book.java

import java.util.Objects;

/**
 * 图书信息实体类
 */
@TableAnnotation("test_Book")
public class Book {
    @ColumnAnnotation(columnName = "id",type = "int",length = "11")
    private int id;
    @ColumnAnnotation(columnName = "name",type = "String",length = "50")
    private String name;
    @ColumnAnnotation(columnName = "info",type = "String",length = "1000")
    private String info;

    public Book() {
    }

    public Book(int id, String name, String info) {
        this.id = id;
        this.name = name;
        this.info = info;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Book book = (Book) o;
        return id == book.id &&
                Objects.equals(name, book.name) &&
                Objects.equals(info, book.info);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, info);
    }

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", info='" + info + '\'' +
                '}';
    }
}

1. 获取Class

/**
 * 将类加载到内存中的方式
 */
public class Demo3 {
    public static void main(String[] args) throws ClassNotFoundException {
        //第一种方法,通过包名.类名.class 加载类
        Class<Person> c1 = Person.class;
        System.out.println(c1);

        //第二种方式,通过类的对象获取类的信息
        Person p1 = new Person();
        Class<Person> c2 = (Class<Person>) p1.getClass();
        System.out.println(c2);
        System.out.println(c1 == c2);   //结果为true,说明俩个对象的内存地址相同

        //第三种方式,通过类的名称(字符串)加载
        Class<Person> c3 = (Class<Person>) Class.forName("com.kkb.task10309.Person");
        System.out.println(c3);
        System.out.println(c1==c2 && c2==c3);   //与上面等同
    }
}

2. 获取Constructor

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

/**
 * 获取Constructor
 */
public class Demo4 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    //使用的是第三种类的加载方式(类名的字符串)
        Class<Person> pClass = (Class<Person>) Class.forName("com.kkb.task10309.Person");
        //找到无参构造方法
        Constructor<Person> c1 = pClass.getConstructor();
        //使用无参构造方法创造对象
        Person p1 = c1.newInstance();
        System.out.println(p1);

        //找到含括String name ,int age的构造方法
        Constructor<Person> constructor1 = pClass.getConstructor(String.class, int.class);
        Constructor<Person> constructor2 = pClass.getConstructor(new Class[]{String.class, int.class}); //与上面获取构造是等同的
        //使用构造方法,创造对象
        Person p2 = constructor1.newInstance("dasd",12);
        System.out.println(p2);

        //使用反射机制打破Java传统封装
        Constructor<Person> dC1 = pClass.getDeclaredConstructor(String.class);
        dC1.setAccessible(true);    //通过getDeclaredConstructor方法,并将此设置为true,用以获取所有权限
        Person person = dC1.newInstance("王五");
        System.out.println(person);

    }
}

在这里插入图片描述

3. 获取Method

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

/**
 * 反射中的方法
 */
public class Demo5 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //加载类
        Class aClass = Class.forName("com.kkb.task10309.Person");
        //获取类的构造方法
        Constructor constructor = aClass.getConstructor();
        //创建对象
        Object o = constructor.newInstance();
        //获取类中的方法
        Method setName = aClass.getMethod("setName", String.class);
        //执行方法,参数1.哪个对象要执行这个方法
        //参数2.调用方法是传递的参数 0-n
        setName.invoke(o,"王五");
        System.out.println(o);

        //调用私有方法
        Method setAge = aClass.getDeclaredMethod("setAge", int.class);
        setAge.setAccessible(true);     //表示忽略权限检查,以此获取私有方法
        setAge.invoke(o,18);
        System.out.println(o);
    }
}

在这里插入图片描述

4. 获取Field

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

/**
 * 反射中的属性
 */
public class Demo6 {
    public static void main(String[] args) throws Exception {
        Class<Person> aClass = (Class<Person>) Class.forName("com.kkb.task10309.Person");
        Constructor<Person> constructor = aClass.getConstructor();
        Person p = constructor.newInstance();

        //获取共有属性
        Field phone = aClass.getField("phone");
        phone.set(p,"16996996");    //通过直接调用Field类型的set方法设置属性值
        System.out.println(p);
        //获取私有属性,与之前 忽略权限一般操作
        Field name = aClass.getDeclaredField("name");
        name.setAccessible(true);   //忽略权限检查
        name.set(p,"傻子");
        System.out.println(p);
    }
}

在这里插入图片描述

5. 获取注解信息

自定义的两个注解类:

TableAnnotation.java

import java.lang.annotation.*;

@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableAnnotation {
    /**
     * 用于标注类对应的表格名称
     */
    String value();
}

ColumnAnnotation .java

import java.lang.annotation.*;

@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ColumnAnnotation {
    String columnName();    //描述列名
    String type();          //描述类型
    String length();        //描述长度
}

测试类Demo7:

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;

/**
 * 获取注解信息
 */
public class Demo7 {
    public static void main(String[] args) throws ClassNotFoundException {
        Class aClass = Class.forName("com.kkb.task10309.Book");
        Annotation[] annotation = aClass.getAnnotations();  //获取类的注解
        for (Annotation a : annotation) {
            System.out.println(a);
        }
        //获取类的注解
        TableAnnotation ta = (TableAnnotation) aClass.getAnnotation(TableAnnotation.class); //获取TableAnnotation注解的对象
        String value = ta.value();  //获取内容
        System.out.println("表名:" + value);

        //获取属性上的注解
        Field[] fields = aClass.getDeclaredFields();
        for (Field field : fields) {
            ColumnAnnotation ca = field.getAnnotation(ColumnAnnotation.class);
            System.out.println(field.getName() + "属性对应数据库中的字段:" + ca.columnName() + " ,数据类型:" + ca.type() + " ,数据长度:" + ca.length());
        }
    }
}

在这里插入图片描述

四、内省

基于反射 , java所提供的一套应用到JavaBean的API
一个定义在包中的类 ,

  1. 拥有无参构造器
  2. 所有属性私有
  3. 所有属性提供get/set方法
  4. 实现了序列化接口

这种类, 我们称其为 bean类 . Java提供了一套java.beans包的api , 对于反射的操作, 进行了封装 !

1. 操作方法

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

BeanInfo

常用的方法:

 MethodDescriptor[] getPropertyDescriptors()

获取bean类的 get/set方法 数组

MethodDescriptor

常用方法:

  1. Method getReadMethod() 获取一个get方法
  2. Method getWriteMethod() 获取一个set方法 ,有可能返回null 注意 ,需要判断 !

示例:

Express.java , bean类

import java.io.Serializable;

/**
 *  快递Express bean类
 */
public class Express implements Serializable {  //实现序列化接口
    //所有的属性私有化
    private String number;
    private String name;
    private String phoneNumber;
    private String adress;
    private boolean flag;

    //无参构造器
    public Express() {
    }

    public Express(String number, String name, String phoneNumber, String adress) {
        this.number = number;
        this.name = name;
        this.phoneNumber = phoneNumber;
        this.adress = adress;
    }

    //所有实行提供set/get方法
    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 getAdress() {
        return adress;
    }

    public void setAdress(String adress) {
        this.adress = adress;
    }

    public boolean isFlag() {
        return flag;
    }

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

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

测试类:Demo8.java

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;

public class Demo8 {
    public static void main(String[] args) throws IntrospectionException {
        Class c = Express.class;
        //获取bean类信息
        BeanInfo beanInfo = Introspector.getBeanInfo(c);
        //获取bean类的 get/set方法 返回数组
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        //遍历获得的数组,取出并打印
        for (PropertyDescriptor pd : propertyDescriptors) {
            Method get = pd.getReadMethod();
            Method set = pd.getWriteMethod();
            System.out.println("名称: " + pd.getName());
            System.out.println("类型: " + pd.getPropertyType());
            System.out.println("get方法:" + get);
            System.out.println("set方法:" + set);
        }
    }
}

在这里插入图片描述

总结

枚举,反射,注解,冲冲冲( ̄︶ ̄)↗ !!!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
文件上传是Web开发中常见的功能之一,Java中也提供了多种方式来实现文件上传。其中,一种常用的方式是通过Apache的commons-fileupload组件来实现文件上传。 以下是实现文件上传的步骤: 1.在pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.3</version> </dependency> ``` 2.在前端页面中添加文件上传表单: ```html <form method="post" enctype="multipart/form-data" action="upload"> <input type="file" name="file"> <input type="submit" value="Upload"> </form> ``` 3.在后台Java代码中处理上传文件: ```java // 创建一个DiskFileItemFactory对象,用于解析上传的文件 DiskFileItemFactory factory = new DiskFileItemFactory(); // 设置缓冲区大小,如果上传的文件大于缓冲区大小,则先将文件保存到临时文件中,再进行处理 factory.setSizeThreshold(1024 * 1024); // 创建一个ServletFileUpload对象,用于解析上传的文件 ServletFileUpload upload = new ServletFileUpload(factory); // 设置上传文件的大小限制,这里设置为10MB upload.setFileSizeMax(10 * 1024 * 1024); // 解析上传的文件,得到一个FileItem的List集合 List<FileItem> items = upload.parseRequest(request); // 遍历FileItem的List集合,处理上传的文件 for (FileItem item : items) { // 判断当前FileItem是否为上传的文件 if (!item.isFormField()) { // 获取上传文件的文件名 String fileName = item.getName(); // 创建一个File对象,用于保存上传的文件 File file = new File("D:/uploads/" + fileName); // 将上传的文件保存到指定的目录中 item.write(file); } } ``` 以上代码中,首先创建了一个DiskFileItemFactory对象,用于解析上传的文件。然后设置了缓冲区大小和上传文件的大小限制。接着创建一个ServletFileUpload对象,用于解析上传的文件。最后遍历FileItem的List集合,判断当前FileItem是否为上传的文件,如果是,则获取文件名,创建一个File对象,将上传的文件保存到指定的目录中。 4.文件上传完成后,可以给用户一个提示信息,例如: ```java response.getWriter().write("File uploaded successfully!"); ``` 以上就是使用Apache的commons-fileupload组件实现文件上传的步骤。需要注意的是,文件上传可能会带来安全隐患,因此在处理上传的文件时,需要进行严格的校验和过滤。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值