JDK1.5 相关特性总结

JDK1.5 相关特性总结

自动装箱和拆箱:

装箱:把基本数据类型的数据装箱成对象类型
拆箱:把对象的类型变成基本数据类型

在jdk1.5及以后版本自动装箱和拆箱
int i=10;
Integer ii=i;
int k=ii
在jdk1.5以前的版本
int i=10;
Integer ii=new Integer(10);
int k=ii.intValue();

增强for循环:

for(类型 变量名 : 集合或数组){
}
可变参数:
-用…来定义
-本质就是一个数组
-可以传入任意个参数
-可变参数只能放在方法参数的最后一个位置

静态导入:

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

枚举:enum

当取值为几个(有限)固定的值.可以使用枚举类型
枚举是一个数据类型
普通的枚举:

1
2
3
 public  enum RequestMethod{
GET,POST,DELETE,PUT
}

1
2
3
4
5
6
7
8
9
 public enum Week{
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
}

总结:
枚举也可以有方法和属性和构造函数
但是构造方法必须是私有的.
枚举还可以实现接口,不能进行继承,枚举也可以包含抽象方法
所有的枚举的类型都默认继承自java.lang.Enum类

反射

(一)什么是反射:
java中提供一套类库,通过这样的类库
-运行时动态获取类中的信息
-运行时动态的调用构造函数创建对象
-运行时动态访问(调用)对象的方法和属性
这种运行期间动态获取类中的信息(属性,方法,包,注解等)
以及动态的调用对象的方法和属性的功能称之为java语言的反射机制
通俗的理解,在运行期间对类的内容进行操作

(二)Class类:
要想使用反射,就要获取到类中的所有信息(属性,方法,注解等…)
在java中有一个特殊的类 类型是Class,此类型的对象中存储是某个
类中的信息

比如:
Class clazz=Class.forName(“lut.Student”);
clazz是一个对象,对象中存储的都是数据,这些数据
都是Student中的属性和方法
属性:访问修饰符 类型 属性名
方法:访问修饰符 返回类型 方法名称(参数列表);

Class clazz=Class.forName("lut.User");
   clazz存储的是User类中的信息

Class类是java提供的,这个Class类可表达任意一个类的信息
-每个类加载后(方法区),系统都会为该类生成一个对应的Class
类型的对象,这个对象存储在堆区中,通过该Class对象可以
访问方法区中的类的信息
-一旦获取了某个类Class类型的对象之后,程序员可以写程序调用
Class对象中的api方法,获取Class对象中的类的信息
-所有的基本数据类型有Class对象
Class clazz=int.class;

(三)如何获取Class类型的对象
-对象.getClass();
比如:
User user=new User();
Class clazz=user.getClass();
-类名.class
比如:
有一个类叫做User类
Class clazz=User.class;
-Class.forName(“包名.类名”);
比如:
Class clazz=Class.forName(“lut.User”);

类的加载分为两个步骤:
-把类加载到方法区中,并创建类的Class类型的对象
-返回Class对象,用以上三种获取Class类型的对象

(四)通过Class类型对象获取如下:
-Field类:代表的成员变量,即属性
-Method类:代表的是方法
-Constructor类:代表的是构造方法
-Annotation类:代表的是注解

通过上面类的api方法获取数据
-可以获取Field类中的信息,获取类的属性 访问修饰符 属性的类型 属性名称
-可以获取Method类中的信息,获取方法 修饰符 返回类型 方法名(参数列表)
-可以获取Constructor中的信息,获取构造函数, 修饰符 类名(参数列表)
-可以获取Annotation中的信息,获取注解 注解名称 注解的属性

结论:
在运行期间,通过Class对象调用反射的api
可以反射实例化对象
可以反射访问属性,和反射调用方法
总之,编译期间能写的代码,用反射也能实现

(五)反射的api:
-反射的方式创建对象
1.用无参数构造创建对象
Class对象.newInstance();//常用的做法
2.用有参数构造创建对象
Class对象.getConstructor(new Class[]{若干参数的类类型})
.newInstance(构造函数的参数);
比如:
//有参数构造
public User(String name,String password){
}
Class clazz=User.class;
//传统的实例化对象
User user=new User(“zhangsan”,”zs”);
//反射的做法:
User user=clazz.getConstructor(new Class[]{String.class,String.class})
.newInstance(“张三”,”zs”);
-反射方式获取Field中的信息
1.获取当前类以及长辈类的public Field
Field[] fields=Class对象.getFields();
2.获取当前类中的所有的属性Field
Field[] fields=Class对象.getDeclaredFields()
3.获取当前类以及长辈类中指定的共有属性
Field field=Class对象.getField(String fieldName);
4.获取当前类中指定的属性
Field field=Class对象.getDeclaredField(String fieldName);
5.通过反射设定Field属性
Field对象.set(Object obj,Object value);
如果Field是私有的
必须先执行:Field对象.setAccessable(true);//设置属性可以访问
6.通过反射获取Field的值
Object value=Field对象.get(object);
如果Field是私有的
必须先执行:Field对象.setAccessable(true);//设置属性可以访问
-反射方式获取Method方法信息
1.获取当前类以及长辈类的public Method
Method[] methods=Class对象.getMethods();
2.获取当前类中的所有的属性Field
Method[] methods=Class对象.getDeclaredMethods()
3.获取当前类以及长辈类中指定的共有属性
Method method=Class对象.getMethod(String methodName,new Class[]{方法参数类型});
4.获取当前类中指定的属性
Method method=Class对象.getDeclaredMethod(String methodName,new Class[]{方法参数类型});
5.通过反射动态调用Method
Object returnValue =Method对象.invoke(Object obj,object…args);
解析:
就是通过obj这个对象,调用Method对象确定的方法,给这个方法传递的参数是args
Method对象对应的方法放回值returnValue;

-反射获取Constructor构造函数
-具体查看api文档
-反射获取注解Annotation
-具体查看api文档

(六)在哪些地方使用反射,反射的应用场景
-用反射实现jdbc的通用查询和通用更新
-用反射解析注解
-单元测设,就是用反射实现的
-常见的框架,spring框架,apringmvc框架等,都是用反射实现的
-EL表达式
等…

反射的优点:
大幅度提高开发效率,框架就是反射实现的,框架可以大大提高开发效率
反射的缺点:
反射执行效率比非反射的方式执行效率低
反射可以暴露类中的所有细节,突破了封装.

内省:

就是自查的意思,本质就是反射,利用反射自省类中的属性和方法和其他

自省的方式有两种
方式一:
jdk(jre)中自带的一套自省的类库,类库包含的是api方法
侧重:属性和属性的值,属性所对应的getter和setter方法
方式二:
apache基金会提供一套公有的自省类库CommonsBeanUtils.jar
他能够处理属性和属性的值和对应的getter和setter
还可以处理普通的方法

总结:
1.能用Commons-Beanutils就用此工具类
2.其次用java的原生的内省Introspector工具类
3.最后用java的原生的反射api Class Field Method Constructor Annotation
原生反射api是最灵活的
要开发快选择1方式
要灵活选择3方式

注解:

  • 注解应用场景很广泛,将来是一个趋势
  • 他可以提高开发效率,但是执行效率堪忧,因为其底层解析注解是用反射解析的
  • 用注解可以替换xml配置和属性文件
  • 注解是一个标识,注解所代表的功能一定要用反射来实现
  • 这些反射的代码,用于解析(寻找注解,获取注解的属性值)
  • 然后根据设定的属性值,来决定是否执行一些业务功能

(一)使用注解有三个步骤:
1.用户定义注解 创建注解
2.把注解应用到对应的目标上 把注解放什么地方(包,类,属性,方法,方法参数等)
3.用反射的代码来确定是否有注解和注解属性值,
根据是否有注解以及注解的值做相应的功能

(二)如何定义注解:
程序员创建注解

1
2
3
4
5
6
7
8
9
10
11
12
//设定MyAnnotation注应用在什么位置上
@Target(value={ElementType.METHOD})
//指定注解的保留策略,source级别 class级别 runtime级别
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
//注解的value属性
public String value();
//注解的name属性,且有默认值abc
public String name() default "abc";
//注解的colors属性,且是一个数组,默认值为red和blue
public String[] colors() default {"red","blue"};
}

(三)元注解/源注解
@Target和 @Retention和@Override等…都是jdk自带的注解
1.@Target(value={ElementType.Method,…})
指定要修饰在什么样的目标上(包,注解,类/接口,方法,属性,参数等…)
value取值是一个枚举类型的数据
ElementType:
ANNOTATION 注解
METHOD 方法
FIELD 属性
CONSTRUCTOR 构造
TYPE 类/接口
PARAMETER 方法的参数上
等…
2.@Retention(RetentionPolicy.RUNTIME)
注解的保留策略

 .java--.class-->jvm执行.class

SOURCE:源代码级别,Source修饰的注解是给编译看的
       编译器把源代码编译完毕后,在class文件中就没有注解
CLASSS:类级别,Class修饰的注解给类加载器看的
       在类加载的时候可以做一系列的引导操作,
       在编译器编译完毕后注解存在,在类加载加载之后
       就要丢弃注解
RUNTIME:运行时级别,给JVM看的,在程序运行的过程中做相关的操作
       可以在jvm中借助反射api解析注解

(四)注解中的属性详解:
a.注解定义属性和接口定义的方法类似,缺省默认public
public 类型 属性名称();
b.定义属性是,如果没有使用default指定默认值
则在使用注解,必须给属性赋值
如果带有默认值,是在使用注解的时候给属性赋值为新值
也可以使用默认值
c.注解中的属性类型必须遵守如下要求
可以是八种基本数据类型,枚举类型,Class类型,String类型,
以及上面类型的一维数组
d.在给数组赋值的时候,如果数组只有一个值就不用写{}
e.有一个极特殊的属性value
如果只为该属性赋值,value=值
但是如果注解中只有value这一个属性,那么value可以省略

(五)把注解应用到目标
在对应的目标上写上注解

1
2
3
4
5
6
7
8
9
10
11
12
  	@MyAnnotation(value="xx",name="yy",colors={"pink","green","gay"})
public class Demo1 {

private String str;
@MyAnnotation(value="aa",name="bb")
public void method1(String name){

}
public void method2(){

}
}

(六)写反射代码来解析注解:
a.解析类上的注解
b.解析方法上的注解

泛型:

(一) 一种参数化的类型
//非泛型的写法,不是参数化得类型
ArrayList list0=new ArrayList();
list0.add(“abc”);
list0.add(10);
//泛型的标准的写法,标准参数化的类型, 参数化类型指的是可以任意类型
ArrayList list1=new ArrayList();
list1.add(“abc”);
list1.add(“aaa”);
//泛型的写法,但不推荐
ArrayList list2=new ArrayList();
list2.add(“abc”);
list2.add(“aaa”);
list2.add(10);//语法是可以通过的
//泛型的写法,但不推荐
ArrayList list3=new ArrayList();
list3.add(“abc”);
list3.add(“aaa”);
//list3.add(10);//语法是错误的

//下列的写法是错误的,编译报错,泛型要求两端的类型必须一致
//ArrayList<Object> list4=new ArrayList<String>();

(二)自定义泛型

  • 类上的泛型,泛型类

    1、泛型类一定要先定义,后使用,在类的名字后面定义泛型的类型
    2、泛型的类型一定要是引用数据类型,不能是基本数据类型
    3、 定义在类上,在类的内部使用,在整个类范围内类型是一致的
    4、类上的泛型的具体类型需要在创建类的对象的时候指定泛型具体类
    5、如果在使用类上泛型时不指定泛型的具体类型,默认的具体类型为泛型的上边界

  • 方法上的泛型,泛型方法
    泛型要先定义后使用,在返回类型之前定义,通常用大写字母
    来定义一个泛型方法,可以定义多个,且定义在方法上,在方法的内部
    使用,方法上泛型,在方法调用的时候自动推断出具体的类型,
    无论何时,尽量使用泛型方法,因为泛型方法作用域小,只局限
    于方法内部
    T :类型type
    E :元素element
    K :关键字key
    V :值 value

    比如:

    1
    2
    3
     public <T,E> T save(T t,E e){
    return t;
    }

    <T,E>就是标记,标记此方法是一个泛型方法放在修饰符和返回
    类型之间,可以放置多个符号,说明有多个参数化类型

(三)泛型的上边界:

  • 泛型所取得类型是上边界或上边界类型的子孙类型
  • 如果不指定泛型默认的上边界是Object

(四)泛型擦除:

泛型编译期间起作用,真正执行期间,泛型已经被擦除了,在编译阶段,编译器会将所有使用泛型的地方替换为泛型的上边界,并在必要的时候增加上必须的类型转换和类型检查,所以在执行期间没有泛型的概念了。泛型在编译期间就被擦除掉了.此过程叫做泛型的擦除

(五)集合与泛型
在 <<Easy Coding>> 一书中是这样解释的:
1、List、List、List<?>的区别
2、<? extends T> 和 <? super T> 的使用场景

  • <? extends T>
    • 是Get First,适用于消费集合元素为主的场景
    • 可以赋值给任何T及T子类的集合,上界为T
    • 取出来的数据带有泛型限制,向上强制转型为 T ,null 可以表示任何类型,除 null 外,任何元素都不得添加进 <? extends T> 集合内
  • <? super T>
    • 是Put First ,适用于生产集合元素为主的场景
    • 可以赋值给任何T及T父类的集合,下界为T
    • 只能往里放数据,而在取数据时不知道该数据具体属于谁,相当于泛型丢失
      extends 的场景是 put 功能受限,而 supper 的场景是 get 功能受限。

* 书中所举例子,以加菲猫、猫、动物为例,说明extends 和 super 的详细语法差异,源代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//用动物的猫科与加菲猫的继承关系说明 extends 与 super 在集合中的意义
public class AnimalCatCarfield{
public static void main (String[] args){
//第1段:声明三个依次继承的类的集合:Object>动物>猫>加菲猫
List<Animal> animal = new ArrayList<>(Animal);
List<Cat> cat = new ArrayList<Cat>();
List<Garfield> garfield = new ArrayList<Garfield>();

animal.add(new Animal());
cat.add(new Cat());
garfield.add(new Garfield());

//第2段:测试赋值操作
//下行编译出错。只能赋值 Cat 或 Cat 子类集合
}
}
# Java

评论