编译期处理
语法糖:就是指java编译器把 .java源码编译为 .class字节码的过程中,自动生成和转化一些代码,主要是为了减轻程序员的负担
默认构造器
如以下代码:
public class Candy1{
}
编译后成class的代码:
public class Candy1{
public Cany1(){
super();
}
}
可以看到编译后的class代码中调用了无参构造方法,这个就是编译器给我们加上的
自动拆装箱
指java的基本类型和包装类型的转换,这个特性是jdk5开始加入的
public class Demo_17 {
public static void main(String[] args) {
Integer x=1;
int y=x;
}
}
这段代码在jdk5之前是无法编译通过的,必须改为一下样式:
public static void jdk5(){
Integer x=Integer.valueOf(1);
int y=x.intValue();
}
泛型集合取值
泛型也是在jdk5开始加入的特性,但java在编译泛型代码后会执行泛型擦除动作,即泛型信息在编译为字节码后就丢失了,实际的类型都当做了Object类型来处理
public class Demo_18 {
public static void main(String[] args) {
List<Integer> list=new ArrayList<>();
list.add(10);//实际调用的是list.add(Object e)
Integer x=list.get(0);//实际调用的是Object obj=list.get(int index)
}
}
通过反编译以及注解可以看出来:
擦除的是字节码上的泛型信息,还有一部分泛型信息会被保留下来,通过反编译可以看到:
可以看到泛型信息,但是有一些局限性,局部变量的泛型信息,虽然没有被擦除,但是我们没法用反射的方式拿到这些信息,只有在方法的参数和返回值上带有泛型信息,才可以通过反射的方式得到,如以下代码:
public class Demo_19 {
public static void main(String[] args) throws Exception {
Method test=Candy3.class.getMethod("test",List.class,Map.class);//得到方法对象
Type[] types = test.getGenericParameterTypes();//得到泛型参数的类型信息
for (Type type:types) {//遍历types数组
if(type instanceof ParameterizedType){//判断type是不是一个泛型
ParameterizedType parameterizedType= (ParameterizedType) type;//如果是,进行转化
System.out.println("原始类型-"+parameterizedType.getRawType());//getRawType()得到原始信息
Type[] arguments = parameterizedType.getActualTypeArguments();//得到泛型的参数
for (int i = 0; i < arguments.length; i++) {//遍历参数
System.out.printf("泛型参数与[%d] - %s\n",i,arguments[i]);
}
}
}
}
}
class Candy3{
public void test(List<String> list, Map<Integer,Object> map){
}
}
代码运行截图如下:
可变参数
可变参数也是jdk5开始加入的新特性:
例如:
public class Demo_20 {
public static void foo(String...args){
String[] arr=args;
System.out.println(arr);
}
public static void main(String[] args) {
foo("hello","world","aaaa");
}
}
可变参数String...args
其实是一个Sting[] args
,从代码中的复制语句中就可以看出来,同样的java编译器会在编译期间将上述代码转换为:
public class Demo_20 {
public static void foo(String[] args){
String[] arr=args;
System.out.println(arr);
}
public static void main(String[] args) {
foo("hello","world","aaaa");
}
}
注意:
如果调用了foo()
则等价代码为foo(new String())
,创建了一个空的数组,而不会传递null进去
foreach循环
还是jdk5引入的语法糖,数组的循环:
public class Candy5 {
public static void main(String[] args) {
int[] array={1,2,3,4,5};
for (int i:array) {
System.out.println(i);
}
}
}
会被编译器转化为:
int[] arr=new int[]{1,2,3,4,5};
for (int i = 0; i < array.length; i++) {
int e=array[i];
System.out.println(e);
}
而集合的循环:
List<Integer> list= Arrays.asList(1,2,3,4,5);
for (Integer i:list) {
System.out.println(i);
}
实际会被编译器转换为迭代器的调用
Iterator iterator = list.iterator();//迭代器
while (iterator.hasNext()){//判断有没有下一个元素
Integer e= (Integer) iterator.next();//获取下一个元素
System.out.println(e);
}
注意:
foreach
循环写法,能够配合数组,以及所有实现了Iterable
接口的集合类一起使用,其中Iterable
用来获取集合的迭代器(Iterator)