JDK 1.5 新特性

2023-10-26

文章目录

1、JDK 1.5 新特性

JDK1.5 一个重要主题就是通过新增一些特性来简化开发。

1.1、自动装箱和拆箱

1.1.1、什么是自动装箱和拆箱

  • 装箱

是把基本数据类型装箱成对象数据类型。

  • 拆箱

是把对象数据类型拆箱成基本数据类型。

1.1.2、自动装箱拆箱要点

  • 1.5 以前需要调用 valueOf() 方法手动装箱。使用 intValue() ,doubleValue() 等这类的方法手动拆箱。

  • 1.5 以后才有自动装箱和拆箱,自动装箱时,编译器调用 valueOf 将原始类型值转换成对象。自动拆箱时,编译器通过调用类似intValue(),doubleValue()等这类的方法将对象转换成原始类型值。

基本数据类型 对象数据类型
byte Byte
short Short
int Integer
long Long
float Float
double Double
boolean Boolean
char Charachter
  • Integer.valueOf(int i)基本数据类型转对象数据类型
  • Integer.parseInt(String s)字符串转基本数据类型
  • intValue 对象数据类型转基本数据类型

1.1.3、自动装箱拆箱样例

自动装箱主要发生在两种情况,一种是赋值时,另一种是在方法调用的时候。

(1)赋值时候

Integer y = 3 //自动装箱
int c = y //自动拆箱

(2)方法调用时候

public static Integer autoboxing(Integer iParam){
   return iParam;
}

autoboxing(3) //自动装箱
int result = show(3) //自动拆箱

1.1.4、自动装箱缺点

Integer sum = 0;
for(int i=1000; i<5000; i++){
   sum = sum + i;
}

首先 sum 进行自动拆箱操作,进行数值相加操作,最后发生自动装箱操作转换成 Integer 对象。其内部变化如下

sum = sum.intValue() + i;
Integer sum = new Integer(sum);

由于我们这里声明的 sum 为Integer类型,在上面的循环中会创建将近 4000 个无用的Integer对象,在这样庞大的循环中,会降低程序的性能并且加重了垃圾回收的工作量。因此在编程时,需要注意到这一点,正确地声明变量类型,避免因为自动装箱引起的性能问题。

1.1.5、重载与自动装箱

重载方法:方法名相同,参数类型不同。

private void add(Integer a){
   System.out.println("Integer");
}
private void add(int a){
   System.out.println("int");
}

测试方法

 DateDemo test = new DateDemo();
 test.add(1);
 test.add(Integer.valueOf(1));
 
结果
int
Integer

重载时传入int a 和 Integer a 是两个不同的方法,会根据传入基本类型或者包装类型来判断走哪个方法。

1.1.6、自动拆装箱的缓存机制

通过上面的讲述我们知道了从jdk1.5以后不再需要通过valueOf()的方式手动装箱,采用自动装箱的方式,其实底层用的还是valueOf()方法,只是现在不用要手动执行了,是通过编译器调用,执行时会自动生成一个静态数组作为缓存,例如Integer 默认对应的缓存数组范围在[-128,127],只要数据在这个范围内,就可以从缓存中拿到相应的对象。超出范围就新建对象,这个就是缓存机制。

源码

valueOf 方法

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

Integer 的内部类 IntegerCache

private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static final Integer cache[];

    static {
        // high value may be configured by property
        int h = 127;
        String integerCacheHighPropValue =
            VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
            try {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            } catch( NumberFormatException nfe) {
                // If the property cannot be parsed into an int, ignore it.
            }
        }
        high = h;

        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);

        // range [-128, 127] must be interned (JLS7 5.1.7)
        assert IntegerCache.high >= 127;
    }

    private IntegerCache() {}
}

IntegerCache是Integer的静态内部类,valueOf()调用的IntegerCache.cache就是一个数组对象,数组的大小取决于范围内的最大值和最小值,例如上面的Integer是[-128,127]。然后数组内的元素都会被赋一个Integer对象,缓存也就形成了。

存在数组缓存,也就意味着,如果取值在[-128,127],使用valueOf()或者自动装箱创建的Integer对象都是在数组中取出,因此对象指向的内存地址是完全一样的。而如果用new或者是超出这个范围都要重新创建对象。

其它类型缓存范围

Byte:(全部缓存)
Short:(-128 — 127缓存)
Integer:(-128 — 127缓存)
Long:(-128 — 127缓存)

Float:(没有缓存)
Double:(没有缓存)

Boolean:(全部缓存)
Character:(0 — 127缓存)

测试

Integer a = new Integer(1);
Integer b = new Integer(1);
System.out.println(a == b); //new创建的两个对象,即使值相同,指向的内存地址也是不同的,使用==进行比较,比较的是地址,返回结果为false
Integer c = 1;
Integer d = 1;
System.out.println(c == d); //自动装箱和缓存机制,两个对象实际上是相同的,返回结果为true
Integer e = 128;
Integer f = 128;
System.out.println(e == f); //超出缓存范围,执行时会new新对象,两个对象不同,返回结果为false

结果
false
true
false

1.2、for - each 循环

  • for ( 类型 变量名:集合或数组 ){}

循环样例

int[] a = new int[] {2, 3, 1, 4};
for (int i : a) {
    System.out.println(i);
}

结果
2
3
1
4

1.3、可变参数

  • 用 … 定义。
  • 本质就是一个数组。
  • 可以传入任意个参数。
  • 可变参数只能放在方发参数的最后一个位置。

可变参数方法样例

public static double sum(int a, int b, double... ds) {
        double sum = 0;
        sum = a + b;
        if (ds != null) {
            for (int i = 0; i < ds.length; i++) {
                sum += ds[i];
            }
        }
        return sum;
    }

测试

System.out.println(sum(1, 2)); //3.0
System.out.println(sum(1, 2, 1)); //4.0
System.out.println(sum(1, 2, 1, 2)); //6.0
System.out.println(sum(1, 2, null)); //3.0
System.out.println(sum(1, 2, new double[] {1, 2, 3})); //9.0

1.4、 静态导入

  • 用 import static 包名.类名.静态属性或静态方法名
  • 可以提高开发的效率
  • 降低了可读,建议要慎用

定义一个静态类,包含静态变量和静态方法

public class Static_Import {
    // 静态变量
    public static int kk = 100;
    // 静态方法
    public static void method() {
        System.out.println("cc");
    }
}

测试静态导入

常规用法

// 常规用法,明确知道变量取自于某个类
System.out.println(Static_Import.kk); //100
Static_Import.method(); //cc

静态导入的用法

// 本类静态导入变量kk和方法method
import static com.yang.test.controller.Static_Import.kk;
import static com.yang.test.controller.Static_Import.method;

// 静态导入的用法(慎用)
System.out.println(kk); //100
method(); //cc

1.5、枚举(enum)

  • 当取值为有限固定的值,可以使用枚举类型,枚举是一个数据类型。

  • 枚举也可以有方法和属性和构造函数,但是构造方法必须是私有的。

  • 枚举还可以实现接口,不能进行继承,枚举也可以包含抽象方法。

  • 所有枚举的类型都默认继承 java.lang.Enum 类。

现在说下最简单的枚举类型。每个枚举值只有一个字符串,如一个星期的枚举类:

public enum Week {
    Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday;
}

但是在实际使用中,可能想给每个枚举值赋予更多的含义,例如,给每个星期中文说明和编号等。修改后的星期枚举类如下:

public enum Week {
    Monday("星期一", "1"), 
    Tuesday("星期二", "2"),
    Wednesday("星期三", "3"),
    Thursday("星期四", "4"), 
    Friday("星期五", "5"),
    Saturday("星期六", "6"), 
    Sunday("星期日", "7");

    private String name;
    private String number;

    Week(String name, String number) {
        this.name = name;
        this.number = number;
    }

    public String getName() {
        return name;
    }

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

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }
}

可以在枚举类中增加了 name/number 两个属性,并重新编写了构造方法。实现了要求。

测试得到结果

System.out.println(Week.Monday); //Monday
System.out.println(Week.Monday.getName()); //星期一
System.out.println(Week.Monday.getNumber()); //1

还有就是可以在枚举类中增加自定义抽象方法,再次修改星期枚举类如下:

public enum Week {
    Monday("星期一", "1") {
        @Override
        void helloYang() {
            System.out.println("hello Monday");
        }
    },
    Tuesday("星期二", "2") {
        @Override
        void helloYang() {
            System.out.println("hello Monday");
        }
    },
    Wednesday("星期三", "3") {
        @Override
        void helloYang() {
            System.out.println("hello Monday");
        }
    },
    Thursday("星期四", "4") {
        @Override
        void helloYang() {
            System.out.println("hello Monday");
        }
    },
    Friday("星期五", "5") {
        @Override
        void helloYang() {
            System.out.println("hello Monday");
        }
    },
    Saturday("星期六", "6") {
        @Override
        void helloYang() {
            System.out.println("hello Monday");
        }
    },
    Sunday("星期日", "7") {
        @Override
        void helloYang() {
            System.out.println("hello Monday");
        }
    };

    private String name;
    private String number;

    Week(String name, String number) {
        this.name = name;
        this.number = number;
    }

    public String getName() {
        return name;
    }

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

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    abstract void helloYang();
}

测试得到结果

Week.Monday.helloYang(); //hello Monday

1.6、反射

运行期间动态获取类中的信息(属性,方法,包的信息,注解等)以及动态调用对象的方法和属性的功能,称之为java语言的反射机制,通俗的理解,就是在运行期间对类的内容进行操作。

Person 类

public class Person {

    private String test1;
    public String test2;

    public  Person(){}
    
    public void eatPerson() {
    }
    private void badPerson() {
    }
}

User类 继承 Person类

public class User extends Person{

    private String userName;
    private String userPassword;
    public String sex;
    protected String email;
    String phone;

    private User() {}
    public User(String userName, String userPassword) {
        super();
        this.userName = userName;
        this.userPassword = userPassword;
    }

    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getUserPassword() {
        return userPassword;
    }
    public void setUserPassword(String userPassword) {
        this.userPassword = userPassword;
    }
    protected void eat() {

    }
    private void bad() {
    }
    void hello() {
    }
}

传统的做法,不使用反射在编译期间确定调用的关系

User user = new User();
user.setUserName("杨");
user.setUserPassword("1212");
System.out.println(user.getUserName() + " " + user.getUserPassword());
}

接下来我来介绍如何使用反射完成上述的对User类属性的操作,在这之前先了解下Class类型对象和反射的api。

1.6.1、Class类型对象

要想使用反射,就要获取到类中的所有信息(属性,方法,注解等),在java中有一个特殊的类,类型是Class,此类型的对象中存储的是某个类中的信息。在运行期间,通过Class对象调用反射的api可以反射实例化对象,可以反射访问属性,和反射调用方法,总之,编译期间能写的代码,用反射也能实现。

3种方式获取Class类型的对象

  • 对象.getClass();

      User user = new User();
      Class clazz = user.getClass();
    
  • 类名.class

      Class clazz = User.class
    
  • Class.forName(“包名.类名”)

      Class.clazz = Class.forName("cn.tedu.User");
    

测试

User user = new User();
// 对象.getClass()
Class clazz1 = user.getClass();
// 类名.class
Class clazz2 = User.class;
// Class.forName("包名.类名")
Class clazz3 = Class.forName("com.yang.test.controller.User");
// 输出hashcode值一样,证明此Class对象只有一个
System.out.println(clazz1.hashCode());
System.out.println(clazz2.hashCode());
System.out.println(clazz3.hashCode());

1.6.2、反射的API

1.6.2.1、获取对象
  • 用无参数构造创建对象

Class对象.newInstance()

  • 用有参数构造创建对象

Class对象.getConstructor(new Class[]{若干参数的类类型}).newInstance(构造函数的参数);

 // 无参构造创建对象
 Class clazz = User.class;
 Object obj = clazz.newInstance();
 
 // 有参构造创建对象
 Object obj1 = clazz.getConstructor(String.class, String.class).newInstance("yang", "yy");
 if (obj1 instanceof User) {
     User user = (User)obj1;
     System.out.println(user.getUserName() + "  " + user.getUserPassword());
 }
1.6.2.2、通过反射获取属性信息(Field)
1.6.2.2.1、通过反射获取本类的所有的属性(getDeclaredFields)
Class clazz = User.class;
Field[] fields = clazz.getDeclaredFields();
for (Field f : fields) {
    System.out.print(f.getModifiers() + "  ");
    System.out.print(f.getType().getName() + " ");
    System.out.println(f.getName());
}

结果
2  java.lang.String userName
2  java.lang.String userPassword
1  java.lang.String sex
4  java.lang.String email
0  java.lang.String phone
1.6.2.2.2、通过反射获取本类以及长辈类所有的公有属性(getFields)
Class clazz = User.class;
// Declared 不存在只能公有
Field[] fields = clazz.getFields();
for (Field f : fields) {
    System.out.print(f.getModifiers() + "  ");
    System.out.print(f.getType().getName() + " ");
    System.out.println(f.getName());
}

结果
1  java.lang.String sex
1  java.lang.String test2
1.6.2.2.3、通过反射获取本类的指定的属性(getDeclaredField)
Class clazz = User.class;
Field field = clazz.getDeclaredField("userName");
System.out.print(field.getModifiers() + " ");
System.out.print(field.getType().getName() + " ");
System.out.print(field.getName());

结果
2 java.lang.String userName
1.6.2.2.4、通过反射获取本类以及长辈类指定的公有属性(getField)
Class clazz = User.class;
Field field = clazz.getField("test2");
System.out.print(field.getModifiers() + " ");
System.out.print(field.getType().getName() + " ");
System.out.print(field.getName());

结果
1 java.lang.String test2
1.6.2.2.5、通过反射设定和获取Field属性
  • Field对象.set(Object obj,Object value)。
  • Object value = Field对象.get(object)。

如果Field是私有的,必须先执行,Field对象.setAccessable(true),设置属性可以访问。

// 通过反射获取指定属性
Class clazz = User.class;
Field field = clazz.getDeclaredField("userName");
// 要给field赋值,先要创建对象
Object obj = clazz.newInstance();
// 设置field具备可访问性
field.setAccessible(true);
// 赋值
field.set(obj, "yang");
// 要取出field中的数据
String value = field.get(obj).toString();
System.out.println(value);

结果
yang
1.6.2.3、通过反射获取方法信息(Method)
1.6.2.3.1、通过反射获取本类的所有的方法(getDeclaredMethods)
Class clazz = User.class;
Method[] methods = clazz.getDeclaredMethods();
for (Method m : methods) {
    System.out.print(m.getModifiers() + "  ");
    System.out.print(m.getReturnType() + " ");
    System.out.print(m.getName() + "(");
    Class[] claxxs = m.getParameterTypes();
    if (claxxs != null && claxxs.length > 0) {
        for (int i = 0; i < claxxs.length - 1; i++) {
            System.out.print(claxxs[i].getName() + ",");
        }
        System.out.print(claxxs[claxxs.length - 1]);
        System.out.println(")");
    } else {
        System.out.println(")");
    }
}

结果
1  class java.lang.String getUserName()
1  void setUserName(class java.lang.String)
0  void hello()
1  class java.lang.String getUserPassword()
4  void eat()
2  void bad()
1  void setUserPassword(class java.lang.String)
1.6.2.3.2、通过反射获取本类以及长辈类所有的公有方法(getMethods)
Class clazz = User.class;
Method[] methods = clazz.getMethods();
for (Method m : methods) {
    System.out.print(m.getModifiers() + "  ");
    System.out.print(m.getReturnType() + " ");
    System.out.print(m.getName() + "(");
    Class[] claxxs = m.getParameterTypes();
    if (claxxs != null && claxxs.length > 0) {
        for (int i = 0; i < claxxs.length - 1; i++) {
            System.out.print(claxxs[i].getName() + ",");
        }
        System.out.print(claxxs[claxxs.length - 1]);
        System.out.println(")");
    } else {
        System.out.println(")");
    }
}

结果 (包含了Object类的公有方法)
1  class java.lang.String getUserName()
1  void setUserName(class java.lang.String)
1  class java.lang.String getUserPassword()
1  void setUserPassword(class java.lang.String)
1  void eatPerson()
17  void wait()
17  void wait(long,int)
273  void wait(long)
1  boolean equals(class java.lang.Object)
1  class java.lang.String toString()
257  int hashCode()
273  class java.lang.Class getClass()
273  void notify()
273  void notifyAll()
1.6.2.3.3、通过反射获取本类的指定的方法(getDeclaredMethod)
Class clazz = User.class;
Method method = clazz.getDeclaredMethod("setUserName", String.class);
System.out.print(method.getModifiers() + " ");
System.out.print(method.getReturnType() + " ");
System.out.print(method.getName() + "(");

Class[] claxxs = method.getParameterTypes();
if (claxxs != null && claxxs.length > 0) {
    for (int i = 0; i < claxxs.length - 1; i++) {
        System.out.print(claxxs[i].getName() + ",");
    }
    System.out.print(claxxs[claxxs.length - 1]);
    System.out.println(")");
} else {
    System.out.println(")");
}

结果
1 void setUserName(class java.lang.String)
1.6.2.3.4、通过反射获取本类以及长辈类指定的公有方法(getMethod)
Class clazz = User.class;
Method method = clazz.getMethod("eatPerson", null);
System.out.print(method.getModifiers() + " ");
System.out.print(method.getReturnType() + " ");
System.out.print(method.getName() + "(");

Class[] claxxs = method.getParameterTypes();
if (claxxs != null && claxxs.length > 0) {
    for (int i = 0; i < claxxs.length - 1; i++) {
        System.out.print(claxxs[i].getName() + ",");
    }
    System.out.print(claxxs[claxxs.length - 1]);
    System.out.println(")");
} else {
    System.out.println(")");
}

结果
1 void eatPerson()
1.6.2.3.5、通过反射动态调用Method
  • Object returnValue = Method对象.invoke(Object,object…args)。
Class clazz = User.class;
Object obj = clazz.newInstance();

Method method = clazz.getDeclaredMethod("setUserName", String.class);
method.setAccessible(true);	
method.invoke(obj, "cc");
Method method1 = clazz.getDeclaredMethod("getUserName");
method1.setAccessible(true);	
System.out.println(method1.invoke(obj, null));

结果
cc
1.6.2.4、通过反射获取构造函数信息(Constructor)
1.6.2.4.1、通过反射获取本类所有的构造方法(getDeclaredConstructors)
Class clazz = User.class;
Constructor[] constructors = clazz.getDeclaredConstructors();
for (Constructor c : constructors) {

    System.out.print(c.getModifiers() + "  ");
    System.out.print(c.getName() + "(");
    Class[] claxxs = c.getParameterTypes();

    if (claxxs != null && claxxs.length > 0) {
        for (int i = 0; i < claxxs.length - 1; i++) {
            System.out.print(claxxs[i].getName() + ",");
        }
        System.out.print(claxxs[claxxs.length - 1]);
    }
    System.out.println(")");
}

结果
2  com.yang.test.controller.User()
1  com.yang.test.controller.User(java.lang.String,class java.lang.String)
1.6.2.4.2、通过反射获取本类公有的构造方法(getDeclaredConstructor)
Class clazz = User.class;
Constructor constructor = clazz.getDeclaredConstructor(String.class, String.class);

System.out.print(constructor.getModifiers() + "  ");
System.out.print(constructor.getName() + "(");
Class[] claxxs = constructor.getParameterTypes();
if (claxxs != null && claxxs.length > 0) {
    for (int i = 0; i < claxxs.length - 1; i++) {
        System.out.print(claxxs[i].getName() + ",");
    }
    System.out.print(claxxs[claxxs.length - 1]);
    System.out.println(")");
}
}

结果
1  com.yang.test.controller.User(java.lang.String,class java.lang.String)
1.6.2.4.3、通过反射获取本类指定的构造方法(getDeclaredConstructor)
Class clazz = User.class;
Constructor constructor = clazz.getDeclaredConstructor(String.class, String.class);

System.out.print(constructor.getModifiers() + "  ");
System.out.print(constructor.getName() + "(");
Class[] claxxs = constructor.getParameterTypes();
if (claxxs != null && claxxs.length > 0) {
    for (int i = 0; i < claxxs.length - 1; i++) {
        System.out.print(claxxs[i].getName() + ",");
    }
    System.out.print(claxxs[claxxs.length - 1]);
    System.out.println(")");
}
结果
1  com.yang.test.controller.User(java.lang.String,class java.lang.String)
1.6.2.4.4、通过反射获取本类指定的公有构造方法(getConstructor)
Class clazz = User.class;
Constructor constructor = clazz.getConstructor(String.class, String.class);

System.out.print(constructor.getModifiers() + "  ");
System.out.print(constructor.getName() + "(");
Class[] claxxs = constructor.getParameterTypes();
if (claxxs != null && claxxs.length > 0) {
    for (int i = 0; i < claxxs.length - 1; i++) {
        System.out.print(claxxs[i].getName() + ",");
    }
    System.out.print(claxxs[claxxs.length - 1]);
    System.out.println(")");
}

结果
1  com.yang.test.controller.User(java.lang.String,class java.lang.String)
1.6.2.5、通过反射获取注解信息(Annotation)

自行查看 API 文档

1.6.3、反射的应用场景

  • 用反射实现 jdbc 的通用查询和通用更新。
  • 单元测试,就是用反射实现的。
  • 常见的框架,spring框架,springmvc框架都是用反射实现的。
  • EL表达式。

1.6.4、反射的优点和缺点

优点

  • 大幅度提高开发效率,框架就是反射实现的,框架可以大大提高开发效率。

缺点

  • 反射执行效率比非反射的方式执行效率低,反射可以暴露类中的所有细节,突破了封装。

1.7、内省

本质就是反射,通过反射的方式访问javabean的技术。

User类

public class User {
    private String userName;
    private String passWord;

    public User(String userName, String passWord) {
        super();
        this.userName = userName;
        this.passWord = passWord;
    }

    public User() {}

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassWord() {
        return passWord;
    }

    public void setPassWord(String passWord) {
        this.passWord = passWord;
    }
}

1.7.1、JDK内省类库(Introspector类和PropertyDescriptor类)

Introspector可以按照JavaBean的规范将一个类封装成BeanInfo对象。通过调getPropertyDescriptors()方法会返回一个包含所有属性的PropertyDescriptor对象数组,通过PropertyDescriptor可以操作类的属性。

PropertyDescriptor可以获取某一个具体的属性。主要的方法有:

  • getPropertyType(),获得属性的Class对象。
  • getReadMethod(),获得用于读取属性值的方法,例如getXXX()方法。
  • getWriteMethod(),获得用于写入属性值的方法,例如setXXX()方法。
  • hashCode(),获取对象的哈希值。
  • setReadMethod(Method readMethod),设置用于读取属性值的方法。
  • setWriteMethod(Method writeMethod),设置用于写入属性值的方法。

样例代码

User user = new User();
// 获取BeanInfo对象
BeanInfo bi = Introspector.getBeanInfo(user.getClass());
// 用BeanInfo的api来获取所有属性的描述器
PropertyDescriptor[] pds = bi.getPropertyDescriptors();
// 遍历所有的属性描述器,每一个描述器代表的是反射中的Field
for (PropertyDescriptor pd : pds) {
    Method writeMethod = pd.getWriteMethod();
    if (writeMethod != null) {
        writeMethod.invoke(user, "yang");
    }
    Method readMethod = pd.getReadMethod();
    if (readMethod != null) {
        Object returnValue = readMethod.invoke(user);
        System.out.println(returnValue);
    }
}

结果
yang

1.7.2、apache提供的Common-beanutils工具

此工具类中有若干工具类

  • MethodUtil工具类
  • ConstructorUtil工具类
  • PropertyUtils工具类

样例代码

	/**
	  * 演示PropertyUtils工具类
	  */
 	@Test
    public void test01() throws Exception {
        User user = new User();
        // 用MethodUtil工具类的api方法
        // 先查找方法后调用
        Method method = MethodUtils.getAccessibleMethod(User.class, "setUserName", String.class);
        if (method != null) {
            // 说明找到了方法
            method.invoke(user, "杨");
        }
        // 查找到方法就直接调用
        Object value = MethodUtils.invokeMethod(user, "getUserName", null);
        System.out.println(value);
    }

    /**
     * 演示ConstructorUtil工具类
     */
    @Test
    public void test02() throws Exception {
        // 用ConstructorUtils工具类
        User user1 = (User)ConstructorUtils.invokeConstructor(User.class, new String[] {"yang", "jun"});
        System.out.println(user1.getUserName() + " " + user1.getPassWord());
    }

    /**
     * 演示PropertyUtils工具类
     */
    @Test
    public void test03() throws Exception {
        User user1 = new User("yang", "jie");
        User user2 = new User();

        PropertyUtils.copyProperties(user2, user1);
        System.out.println(user2.getUserName() + " " + user2.getPassWord());

    }

1.8、注解

注解应用场景很广泛,将来是一个趋势,他可以提高开发效率,但是执行效率堪忧,因为其底层解析注解是用反射解析的,用注解可以替换xml配置和属性文件。

1.8.1、如何定义注解

(1)设定注解MyAnnotation应用在什么位置上 @Target(value={ElementType.METHOD})。

ElementType 类型 描述
METHOD 方法
FIELD 属性
CONSTRUCTOR 构造
TYPE 类/接口
PARAMETER 方法的参数上

(2)指定注解MyAnnotation的保留策略 @Retention(RetentionPolicy.RUNTIME)。

RetentionPolicy 类型
SOURCE
CLASS
RUNTIME

SOURCE
源代码级别,Source修饰的注解是给编译器看的,编译器把源代码编译完毕后,在class文件中就没有注解。

CLASSS
Class修饰的注解给类加载器看的,在类加载的时候可以做一系列的引导操作,在编译器编译完毕后注解存在,在类加载加载之后就要丢弃注解。

RUNTIME
运行时级别,给JVM看的,在程序运行的过程中做相关的操作可以在jvm中借助反射api解析注解。

(3)设定MyAnnotation注解中的属性。

  • 注解定义属性和接口定义的方法类似,缺省默认public(public 类型 属性名称())。

  • 定义属性如果没有使用default指定默认值,则在使用注解,必须给属性赋值,如果带有默认值,可以在使用注解的时候给属性赋值为新值或者使用默认值。

  • 注解中的属性类型必须遵守是八种基本数据类型,枚举类型,Class类型,String类型以及前面类型的一维数组。

  • 在给数组赋值的时候,如果数组只有一个值就不用写{}。

  • 有一个极特殊的属性value,如果只为该属性赋值,value=值,但是如果注解中只有value这一个属性,那么value可以省略。例如下面的 @MyAnnotation(“xxx”),本质是 @MyAnnotation(value = “xxx”)。

1.8.2、自定义和使用注解样例

自定义注解

@Target(value = {ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {

    // 注解的value属性
    public String value();

    // 注解的默认属性,且有默认值abc
    public String name() default "abc";

    // 注解的colors属性,且是一个数组,默认值是red和blue
    public String[] colors() default {"red", "blue"};

}

使用注解

@MyAnnotation(value = "xx", name = "yy", colors = {"pink", "green", "gay"})
public class AnnotationClass {
    @MyAnnotation(value = "aa", name = "bb")
    public void method1() {
        System.out.println("method1");
    }

    @MyAnnotation("xxx")
    public void method2() {
        System.out.println("method2");
    }

    @MyAnnotation("yyy")
    public void method3() {
        System.out.println("method3");
    }
}

1.8.3、写反射代码解析注解

用反射的代码来确定是否有注解和注解属性值 ,根据是否有注解以及注解的值做相应的功能。

以下代码是来解析上述类上的注解:

Class clazz = AnnotationClass.class;

// 查找并判断类上是否有指定的注解
boolean flag = clazz.isAnnotationPresent(MyAnnotation.class);

if (flag) {
    // 说明类上有指定的@MyAnnotation注解
    // 可以获取注解对象
    MyAnnotation myAnnotation = (MyAnnotation)clazz.getAnnotation(MyAnnotation.class);
    // 通过注解的对象,获取注解中的属性的数据
    String value = myAnnotation.value();
    String name = myAnnotation.name();
    String[] colors = myAnnotation.colors();
    System.out.println(value);
    System.out.println(name);
    System.out.println(Arrays.toString(colors));

    // 根据注解属性的定义,来决定doSomething
    if ("xx".equals(value) && "yy".equals(name)) {
        System.out.println("doSomething 代表一段业务逻辑代码");
    }
}

结果
xx
yy
[pink, green, gay]
doSomething 代表一段业务逻辑代码

以下代码是来解析上述方法上的注解:

Class clazz = AnnotationClass.class;
Object obj = clazz.newInstance();
 	// 获取类中的所有的方法,要查看方法上是否有指定的注解
    Method[] methods = clazz.getDeclaredMethods();

    for (Method method : methods) {
        // 判断方法上是否有指定的注解
        boolean flag = method.isAnnotationPresent(MyAnnotation.class);

        if (flag) {

            // 说明方法上有指定的注解
            // 获取到方法上的注解的对象
            MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);

            String value = myAnnotation.value();
            String name = myAnnotation.name();
            String[] colors = myAnnotation.colors();

            if ("xxx".equals(value) || "bb".equals(name)) {
                method.invoke(obj);
            }

        }

    }
    
结果
method2
method1

1.9、泛型(一种参数化的类型)

Java 泛型是 JDK 1.5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。

泛型的本质是参数化类型,即给类型指定一个参数,然后在使用时再指定此参数具体的值,那样这个类型就可以在使用时决定了。这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

1.9.1、为什么使用泛型

(1)保证了类型的安全性。

在没有泛型之前,从集合中读取到的每一个对象都必须进行类型转换,如果不小心插入了错误的类型对象,在运行时的转换处理就会出错。

比如:没有泛型的情况下使用集合:

ArrayList list = new ArrayList();
list0.add("abc");
list0.add(10);

比如:没有泛型的情况下使用集合:

ArrayList<String> list = new ArrayList<String>();
list.add("abc");
list.add(10);// 编译报错

相当于告诉编译器每个集合接收的对象类型是什么,编译器在编译期就会做类型检查,告知是否插入了错误类型的对象,使得程序更加安全,增强了程序的健壮性。

(2) 消除强制转换

泛型的一个附带好处是,消除源代码中的许多强制类型转换,这使得代码更加可读,并且减少了出错机会。

没有泛型的代码段需要强制转换

ArrayList list = new ArrayList();
list.add("a");
String s = (String)list.get(0);

当使用泛型时,代码不需要强制转换

ArrayList<String> list = new ArrayList<String>();
list.add("a");
String s = list.get(0);

(3)多种数据类型执行相同的代码使用泛型可以复用代码。

1.9.2、泛型的分类

1.9.2.1、泛型类(类上的泛型)

定义格式:
在这里插入图片描述

public class 类名 <泛型类型,...> {
}
  • 泛型类型必须是引用类型(非基本数据类型)
  • 定义泛型类,参数可以有多个,多个参数使用逗号分隔。
  • 参数类型也是有规范的,通常类型参数我们都使用大写的单个字母表示。

E: Element (在集合中使用,因为集合中存放的是元素)
T:Type(Java 类)
K: Key(键)
V: Value(值)
N: Number(数值类型)
?: 表示不确定的java类型

定义泛型类

public class User<T> {

    public T name;

    public User(T name) {
        this.name = name;
    }
}

测试泛型类

User<String> user = new User<>("yang");
System.out.println(user.name);

User<Integer> number = new User<>(123);
System.out.println(number.name);

结果
yang
123
1.9.2.2、泛型接口(接口上的泛型)

定义格式:
在这里插入图片描述

public <泛型类型> 返回类型 方法名(泛型类型 变量名) {
}

方法声明中定义的形参只能在该方法里使用,而接口、类声明中定义的类型形参则可以在整个接口、类中使用。当调用fun()方法时,根据传入的实际对象,编译器就会判断出类型形参T所代表的实际类型。

定义泛型接口

public interface GenericInterface<T> {
    void show(T value);}
}

定义泛型接口实现类

public class StringShowImpl implements GenericInterface<String> {
    @Override
    public void show(String value) {
        System.out.println(value);
    }
}
public class NumberShowImpl implements GenericInterface<Integer> {
    @Override
    public void show(Integer value) {
        System.out.println(value);
    }
}

测试泛型接口

GenericInterface<String> genericInterface = new StringShowImpl();
GenericInterface<Integer> genericInterface1 = new NumberShowImpl();
GenericInterface<String> genericInterface = new NumberShowImpl(); //编译报错
GenericInterface<Integer> genericInterface1 = new StringShowImpl(); //编译报错

使用泛型的时候,前后定义的泛型类型必须保持一致
1.9.2.3、泛型方法(方法上的泛型)

定义格式:
在这里插入图片描述

修饰符 <代表泛型的变量> 返回值类型 方法名(参数){ }
  • 修饰符与返回值中间<代表泛型的变量>非常重要,可以理解为声明此方法为泛型方法。
  • 只有声明了<代表泛型的变量>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
  • 与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E等形式的参数常用于表示泛型。

定义泛型方法

public <T> T genercMethod(T t){
    System.out.println(t.getClass());
    System.out.println(t);
    return t;
}

测试泛型方法

User<String> user = new User<>("yang");
user.method("yang");

User<Integer> number = new User<>(123);
user.method(123);

结果
class java.lang.String
yang
class java.lang.Integer
123

1.9.3、泛型的通配符

  • 无边界的通配符,就是<?>,比如List<?>。
    无边界的通配符的主要作用就是让泛型能够接受未知类型的数据。

  • 固定上边界的通配符,采用<? extends E>的形式,比如< T extends Animal >。
    使用固定上边界的通配符的泛型,就能够接受指定类及其子类类型的数据(Animal类和其子类)。

  • 固定下边界的通配符,采用<? super E>的形式,比如< T super Animal >。
    使用固定下边界的通配符的泛型, 就能够接受指定类及其父类类型的数据(Animal类和其长辈类)。

你可以为一个泛型指定上边界或下边界, 但是不能同时指定上下边界。

如果不指定泛型默认的上边界是 Object 。

1.9.4、泛型擦除

Java 中的泛型基本上都是在编译器这个层次来实现的。在生成的 Java 字节代码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉。如在代码中定义的 List< Object > 和 List< String >等类型,在编译之后都会变成 List。 这个过程就称为类型擦除。

测试泛型擦除

ArrayList<String> list1 = new ArrayList<String>();
list1.add("abc");
ArrayList<Integer> list2 = new ArrayList<Integer>();
list2.add(123);
// 泛型类型String和Integer都被擦除掉了,只剩下原始类型
System.out.println(list1.getClass() == list2.getClass());

结果
true
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JDK 1.5 新特性 的相关文章

随机推荐

  • Matlab中数值类型(二)

    目录 单精度运算 创建双精度数据 转换为单精度 创建单精度零和一 算术运算和线性代数运算 用于处理单精度或双精度的一个程序 整数 整数类 创建整数数据 整数类的算术运算 整数类的最大值和最小值 整数运算 加载整数信号数据 对数据绘图 处理数
  • Python常用模块总结

    Python常用模块总结 os模块 os remove 删除文件 os unlink 删除文件 os rename 重命名文件 os listdir 列出指定目录下所有文件 os chdir 改变当前工作目录 os getcwd 获取当前文
  • 制作跟文件系统时出现的一些错误

    在make busybox的时候出现如下错误 arm none linux gnueabi libc usr include linux netfilter h 44 error field in has incomplete type a
  • 微信小程序——点击列表里某元素,实现跳转+显示点击元素的内容

    最近刚刚学微信小程序没多久 碰到了很多问题 这也是我碰到的一个问题 思路 跳转 组件绑定一个点击事件 在事件里面实现跳转 显示点击元素的内容 获取点击元素在列表里面的id 但是我发现我获取到的id是空的 啥也没有 呜呜呜 可怜的小白 都不知
  • Ubuntu下安装man中文手册(中英文共有)

    安装步骤 1 先安装所需要的依赖包 automake 工具 sudo apt get install autoconf automake libtool git 工具 sudo apt get install git 2 下载中文man安装
  • 自学Mysql-存储过程--判断和循环语句

    case 用case解决传入的值是否是数字 CREATE PROCEDURE test case num int out flag varchar 20 BEGIN case num 类似于swtich when num gt 0 then
  • android recycleview 没有填满屏幕

    最近使用recyclerview 每次绘制的item 虽然写的是填充父控件 但是每次效果都是包裹内容 没有填满手机屏幕 后来才意识到是填充子view的时候出现了问题 没有填满屏幕的时候 你可以试着在item view的主布局设置一个back
  • GBase 8c亮相国内首款金融数据库性能测试工具开源发布会

    2 月 17 日 由信通院主办的国内首款金融数据库性能测试工具开源发布会在线上召开 会上 定位于国家高端专业智库 产业创新发展平台的信通院宣布开源该测试工具 并详细阐述了开源此工具的背景 初心 历程以及愿景 南大通用受邀参加此次发布会 GB
  • 用for语句算15的阶乘

    x 1 for i in range 1 16 i从1依次取到15 x x i print x
  • webrtcvad 安装失败

    倒腾了两个小时终于解决了这个问题 所有办法都试了 只有这个管用 下载安装VC 不要下载那种在线安装的 我试了很多次 都是安装包丢失或损坏 直接复制下面链接去下载 百度网盘链接 https pan baidu com s 1IaqkukMzb
  • 我的个人博客

    经过几天鼓捣 我的个人博客终于建成了 为了提高网站安全性我把http协议升级成了https的 带有传输加密的协议能保证传输的安全而且可以防止篡改网站的网页 网站的访问速度也不能慢 为此我有花费了一些精力配置了CDN 现在通过https ww
  • es数据量过大,内存撑不住,关闭部分历史数据索引,以及备份删除索引

    关闭索引命令 curl XPOST http ip 9200 索引名称 close 恢复索引命令 curl XPOST http ip 9200 202205 open 遇到问题索引权限问题关闭失败 type cluster block e
  • 12.话题消息的定义与实现

    学习视频 https www bilibili com video BV1zt411G7Vn p 12 目标 消息的自定义 发布及订阅个人信息 一 自定义话题信息 1 定义msg文件 mkdir catkin ws src learning
  • OkHttp:基本使用详解

    简介 OkHttp是一个高效的HTTP客户端 它有以下默认特性 支持HTTP 2 允许所有同一个主机地址的请求共享同一个socket连接 连接池减少请求延时 透明的GZIP压缩减少响应数据的大小 缓存响应内容 避免一些完全重复的请求 当网络
  • TypeError: test_recharge() missing 1 required positional argument: ‘item‘

    提示test recharge 缺少一个必须的位置参数 item 说明是item有问题 debug后 发现item没有出现用例中的数据 再看是发现没有写 ddt 写上去就好了
  • CorelDraw X4 unable to load resource dll_七夕小子_新浪博客

    CorelDraw X4以前安装过 现在重新安装后 运行时 弹出几个对话框 Unable to load resource dll CRLUTLINTL dll Unable to load resourse DLL CrlCmnMappe
  • java中让控制台输出彩色字符的方法-Jansi

    在网上有很多类似的文章 教你如何在控制台输出彩色字符 其中比较好的方法是用别人的做好的包 Jansi 但是在网上很多的文章没有给出完整的操作过程 只是给出了方法 在这里将会有完整的过程 1 下载jnsi包 http maven outofm
  • 【报告分享】ChatGPT:AI模型框架研究.pdf(附下载链接)

    省时查报告 专业 及时 全面的行研报告库 省时查方案 专业 及时 全面的营销策划方案库 免费下载 2023年2月份热门报告合集 限时免费 ChatGPT4体验 无需翻墙直接用 ChatGPT团队背景研究报告 ChatGPT的发展历程 原理
  • Selenium基础用法

    目录 一 概念和自己的理解 二 安装 三 浏览器驱动 四 正真的基础上场 1 先要打开浏览器 打不开 我们后面也就做不了 万事开头先有前提 2 获取元素的方法 3 操作元素 4 浏览器操作 5 鼠标操作 6 键盘操作 7 下拉框操作 8 页
  • JDK 1.5 新特性

    文章目录 1 JDK 1 5 新特性 1 1 自动装箱和拆箱 1 1 1 什么是自动装箱和拆箱 1 1 2 自动装箱拆箱要点 1 1 3 自动装箱拆箱样例 1 1 4 自动装箱缺点 1 1 5 重载与自动装箱 1 1 6 自动拆装箱的缓存机