1.默认构造器
public class Candy1{
}
编译成class的代码:
public class Candy1{
//这个无参构造器是编译器帮我们加上的
public Candy1(){
super();//调用父类Object无参构造方法
}
}
2.自动拆装箱
public class Candy2{
public static void main(String[] args) {//现在编译时会自动转换成下面的
Integer x= 1;
int y= x;
}
}
在jdk5前必须要手动转换
public class Candy2{
public static void main(String[] args) {
Integer x= Integer.valueOf(1);
int y= x.intValue;
}
}
3.泛型集合取值
jdk5开始加入的特性,编译泛型代码后会执行泛型擦除的动作,即泛型信息在编译为字节码之后就丢失了,实际的类型都当做Object类型来处理
public class Candy3 {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(10);//实际调用list.add(Object 0)
Integer x = list.get(0);//实际调用的是Object object = list.get(int index);
}
}
所以在取值时,编译器真正生成的字节码中,还要额外做一个类型转换的操作:
Integer x = (Integer)list.get(0);
如果前面x变量类型修改为int基本类型那么最终生成的字节码是:
int x = ((Integer)list.get(0)).intValue();
泛型反射,获取信息
4.可变参数
jdk5加入
public class Candy4 {
public static void foo(String... args){
String[] array = args;
System.out.println(array);
}
public static void main(String[] args) {
foo("hello","world");
}
}
编译器在编译期间将上述代码变换为:
public class Candy4 {
public static void foo(String[] args){
String[] array = args;
System.out.println(array);
}
public static void main(String[] args) {
foo(new String[]{"hello","world"});
}
}
5.foreach循环
6.switch字符串
jdk7开始可以配合字符串和枚举类使用,相当于拆成两个switch,为什么用hashcode呢?当比较的字符串数量较多时,用equals速度很慢,显然是不合理的,会尽可能减少比较次数,一般hashCode是惟一的,比逐一比较字符串快,但为什么还要再比较一次字符串呢?因为这样可以防止hashCode冲突
public class Candy6{
public static void choose(String str){
switch (str){
case "hello":
System.out.println("h");
break;
case "world":
System.out.println("w");
break;
default:
break;
}
}
}
上面代码会被编译器转换为
class Candy6{
public static void choose(String str){
byte x = -1;
switch (str.hashCode()){
case 99162322://"hello"的哈希码
if(str.equals("hello")){
x = 0;
}
break;
case 113318802://"world"的哈希码
if(str.equals("world")){
x = 1;
}
break;
default:
break;
}
switch(x){
case 0:
System.out.println("h");
break;
case 1:
System.out.println("w");
break;
default:
break;
}
}
}
7.switch 枚举
enum Sex{
MALE,FEMALE
}
public class Candy7 {
public static void foo(Sex sex){
switch (sex){
case MALE:
System.out.println("男");
break;
case FEMALE:
System.out.println("女");
break;
default:
break;
}
}
}
转换后代码
public class Candy7 {
/**
* 定义一个合成类(仅jvm使用,我们看不见)
* 用来映射枚举的ordinal与数组元素的关系
* 枚举的ordinal表示枚举对象的序号,从0开始
* 即MALE的ordinal()=0,FEMALE的ord()=1
*/
static class $MAP{
//数组大小即为枚举元素的个数,里面存储case用来对比的数字
static int[] map = new int[2];
static {
map[Sex.MALE.ordinal()] = 1;
map[Sex.FEMALE.ordinal()] = 2;
}
}
public static void foo(Sex sex){
int x = $MAP.map[sex.ordinal()];
switch (x){
case 1:
System.out.println("男");
break;
case 2:
System.out.println("女");
break;
default:
break;
}
}
}
8.枚举类
enum Sex{//本质Class,实例个数有限,这里仅有两个
MALE,FEMALE
}
转换后代码
public final class Sex extends Enum<Sex>{
public static final Sex MALE;
public static final Sex FEMALE;
public static final Sex Sex[] $VALUES;
static {
MALE = new Sex("MALE",0);
FEMALE = new Sex("FEMALE",1);
$VALUE$ = new Sex[]{MALE,FEMALE};
}
private Sex(String name, int ordinal){
super(name, ordinal);
}
public static Sex[] values(){
return $VALUES.clone();
}
public static Sex valueOf(String name){
return Enum.valueOf(Sex.class,name);
}
}
9.try-with-resources
jdk7 开始新增了对需要关闭的资源处理的特殊语法
前提:资源对象需要实现AutoCloseable接口,例如InputStream、OutputStream、Connection、Statement、ResultSet等接口都实现类AutoCloseable,使用try-with-resources可以不用写finally语句块,编译时会自动将finally部分加上,并且考虑的十分到位
try(Inputstream is = new FileInputStream("d://test.txt")){
} catch(Exception e){
}
甚至考虑到了关闭资源时的异常,t.addSuppressed(e2)压制异常,防止异常信息的丢失。(想想try-with-resources生成的finally中如果抛出了异常,也会打印出来)
10.方法重写时的重写方法
方法重写时对返回值分两种情况:(如下)
- 父子类的返回值完全一致
- 子类的返回值可以是父类返回值的子类
class A{
public Number tt(){
return 1;
}
}
class B extends A{
@Override
public Integer tt(){
return 2;
}
}
对于子类,java编译器会做如下处理:
class B extends A{
public Integer tt(){
return 2;
}
//合成方法真正重写了父类 public Number tt()方法,仅对对虚拟机可见,
//与原来的public Integer tt()没有命名冲突
public synthetic bridge Number tt(){
return tt();
}
//可以用反射代码验证
for(Method m : B.class.getDeclaredMethods()){
System.out.println(m);
}
//输出:
//public java.lang.Integer test.candy.B.m()
//public java.lang.Number test.candy.B.m()
}
11.匿名内部类
public class Candy11 {
public static void main(String[] args) {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("ok");
}
};
}
}
转换后代码:
//产生额外的类
final class Candy11$1 implements Runnable{
Candy11$1(){
}
public void run(){
System.out.println("ok");
}
}
public class Candy11 {
public static void main(String[] args) {
Runnable runnable = new Candy11$1();
}
}
引用局部变量的匿名内部类,源代码:局部变量必须final修饰
public class Candy11 {
public static void test(final int x) {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("ok"+x);
}
};
}
}
转换后代码:
final class Candy11$1 implements Runnable{
int val$x;//用来存x
Candy11$1(int x){//把x传过来
this.val$x = x;
}
public void run(){
System.out.println("ok"+this.val$x);
}
}
public class Candy11 {
public static void main(String[] args) {
Runnable runnable = new Candy11$1();
}
}
为什么要求局部变量是final的?x不可修改,这样可以保持和匿名内部类中的x一致
学习整理于解密jvm.