基本类型的取值范围
public void t1(){
//使用10进制 8进制 16进制来赋值数据
int int10 = 12; //10进制
int int8 = 012; //8进制
int int16 = 0xff;//16进制
//数据上溢和下溢
Integer a = Integer.MAX_VALUE+1;
Integer b = Integer.MIN_VALUE-1;
//类型转换优先关系顺序: 小类型数据与大类型数据运算结果是大类型数据
// byte,short,char >int > long > float > double
}
基本类型与包装类型
- 对于成员变量,为基本类型时 java会为它们赋予默认值,为包装类型或者引用类型时 java也会为它们赋予默认值null。
- 对于局部变量,我们需要手动为该变量初始化,否则后面引用到该变量时会编译报错。
基本类型与包装类型的取舍:我推荐尽量使用包装类型,比如int类型默认初始化值是0,但是有时候我们对于某个字段的要求是无就是无,0代表不了无,因此使用null来表示更适合语义,又比如我们使用int来接受数据库某个字段的值时 假设这个字段对应的记录在数据库中为null,但是我们却以int去接收 ,以mybatis为例 会返回attempted to return null from a method with a primitive return type (int).错误,当数据库返回null,但是我们以int ,boolean ,long等基本类型去接收时就会出现错误,因为基本类型无法赋值为null,
但是基本类型也不是一无是处,基本类型有基本类型的特点,由于基本类型直接存储值,并且放置于栈中,因此比包装类型更加高效。
赋值和方法调用中的别名问题
基本类型及其包装类以及String都是通过复制一个值来来传递
引用类型,数组通过复制地址的值来传递。(以下例子结果在方法调用中表现的也一样)
public class Test1 {
public static void main(String[] args) {
int a = 1;
int b =a ;
b = 2;
System.out.println(a);//输出1
System.out.println(b);//输出2
Integer c = 1;
Integer d = c;
d = 2;
System.out.println(c);//输出1
System.out.println(d);//输出2
String s1 = "1";
String s2 = s1;
s2="2";
System.out.println(s1);//输出1
System.out.println(s2);//输出2
Person p1 = new Person();
Person p2 = p1 ;
p2.a=200;
System.out.println(p1.a);//输出200
System.out.println(p2.a);//输出200
System.out.println("---------------");
Integer[] arr1 = {1,2,3};
Integer[] arr2 = arr1;
arr2[0] = 11;
System.out.println(arr1[0]);//输出11
System.out.println(arr2[0]);//输出11
int[] arr3 = {1,2,3};
int[] arr4 = arr3;
arr4[0] = 11;
System.out.println(arr3[0]);//输出11
System.out.println(arr4[0]);//输出11
}
}
@Data
class Person{
Integer a = 100;
}
类初始化顺序
主要注意,1类初始化<clinit>,2 实例初始化<init>,3 方法重写
- main方法所在的类进行初始化,执行<clinit>类初始化方法(如果有父类,那就执行父类<clinit>)
- <clinit>实例初始化方法,<clinit>由静态代码块和静态成员变量赋值代码组成(静态成员与静态代码块谁先声明那就先执行谁,这两个都是只执行一次)
- 实例初始化<init>,<init>()方法可能重载有多个,有几个构造器就有几个版本的<init>方法。
<init>方法由非静态成员变量和非静态代码块,以及构造器组成。
执行所在类的<init>方法,先执行super()方法,再执行(成员变量的初始化 ,普通代码块:这两个谁先声明就先执行谁)-->构造函数方法体。 - 方法重写:子类如果重写了父类的方法,通过子类对象调用的一定是子类重写过的代码,非静态方法默认的调用对象是this,this对象在构造器或者说<init>方法中就是正在创建的对象
public class Son extends Father{
private static int j = method();
static{
System.out.print("(6)");
}
{
System.out.print("(8)");
}
private int i = test();
Son(){
System.out.print("(7)");
}
public int test(){
System.out.print("(9)");
return 1;
}
public static int method(){
System.out.print("(10)");
return 1;
}
public static void main(String[] args) {
Son s1 = new Son();
System.out.println();
Son s2 = new Son();
}
}
public class Father{
private int i = this.test();
static{
System.out.print("(1)");
}
private static int j = method();
Father(){
System.out.print("(2)");
}
{
System.out.print("(3)");
}
public int test(){
System.out.print("(4)");
return 1;
}
public static int method(){
System.out.print("(5)");
return 1;
}
}
//输出结果
//(1)(5)(10)(6)(9)(3)(2)(9)(8)(7)
//(9)(3)(2)(9)(8)(7)
// main方法 所在的类进行初始化 先初始化Father类在初始化Son类
//父类的<clinit>方法 JVM定义的方法,全名 class init,clinit方法只执行一次
//先初始化Father类,静态代码块比静态成员先声明,所以先执行静态代码块
//输出 1
//然后执行静态成员初始化
//输出5
//父类的<clinit>方法
//再初始化Son类,静态成员比静态代码块先申明,所以先执行静态成员初始化
//输出10
//然后 静态代码块
//输出6
//执行子类的<init>方法,JVM中定义的,new几次对象<init>就执行几次
//然后执行构造函数中第一行super();它比成员变量先执行,
//先执行Father的super();由于父类是Object,所以无任何输出
//然后进行Father的初始化,成员变量初始化优先于构造函数(与声明位置无关)
//执行的Father的test()方法,非静态方法前面其实有一个默认的对象this,this在构造器(或<init>)它表示的是正在创建的对象,因为这里是在创建Son对象,所以test()执行的是子类重写的代码
//输出9
//执行父类的非静态代码块
//输出3
//执行父类的构造函数体
//输出2
//执行子类的成员变量初始化 ,执行test()
//输出9
//执行子类的非静态代码块
//输出8
//执行子类的构造函数体
//输出7
//**因为创建了两个Son对象,因此实例化方法<init>执行两次**
//输出:9 3 2 9 8 7
网友评论