目录
1. JDK,JRE,JVM 的关系
JDK=JRE+开发工具集(例如Javac编译工具)
JRE=JVM+JavaSE标准类库
2. Java定义的数据类型
按数据类型分:a)基本数据类型:整型(byte、short、int、long)、浮点型(float、double)、
字符型(char)、布尔型(boolean)
b)引用数据类型:类、接口、字符串、数组
3. 基本数据类型所占据内存空间大小
3.1 整型
a) byte(1字节=8bit)、b)short(2字节)、c) int(4字节)、d)long(8字节)
3.2 浮点型
a) float(4字节)、b) double(8字节)
3.3 字符型
a)char(1字符=2字节)
3.4 练习:判断输出
- sout("*\t*"); // * *
- sout('*'+'\t'+'*'); //数字
- sout('*'+"\t"+'*'); // * *
- sout('*'+'\t'+"*"); //数字*
- sout('*'+('\t'+"*")); // * *
4. 自动类型提升
定义:容量大的数据类型与容量小的数据类型变量做运算时,自动提升为容量大的数据类型。
byte、short、char-->int-->long-->float-->double
4.1 练习:判断能否通过编译
short s =5;
s = s - 2; //no
byte b = 3;
b = b + 4; //no
b = (byte) (b+4); //yes
char c = 'a';
int i = 5;
float d = .314F;
double result = c+i+d; //yes
byte b = 5;
short s = 3;
short t = s + b; //no-->自动类型转换为int
5.强制类型转换
强制类型转换可能会导致精度缺失。
5.1 ++、--、+=、-+、*=、/=、%=
结论:以上运算均不改变数据类型
int i = 1;
i *= 0.1; //i = (int)i * 0.1; -->0 强制类型转换会损失精度
i ++; //i = 1
int m = 2;
int n = 3;
n *= m++; //n = n * (m++) n = 6, m = 3
int n = 10;
n += (n++) + (++n); //n = n + (n++) + (++n) 10+10+12 = 32
note:
a)如果变量是基本数据类型:此时,赋值的是变量所保存的数据值。
b)如果变量是引用数据类型:此时,赋值的是变量所保存的数据的地址。
6.进制
1)二进制:以0b或0B开头(零b)
2) 十进制:默认
3) 八进制:以0开头
4)十六进制:以0x或0X开头
结论:所有进制在java运行后自动变为十进制
7. &与&&、|与||的区别
a) &与&&
相同点1:不管用这两个哪个,运算结果都是相同的
2:当符号左边是true时,两者都会执行符号右边的运算
不同点:当符号左边是false时,&继续执行符号右边的运算,&&不再执行符号右面的运算
b) |与||
相同点1:两者运算的结果是相同的
相同点2:如果符号左边是false时,两者都会执行符号右边的运算
不同点:如果符号左边时true时,|继续执行符号右边的运算,而||不再执行符号右边的运算
8. 位运算
8.1 最高效计算 2 *8
2 << 3;
8 << 1;
8.2 交换两变量的值
//方式一:定义临时变量
int num1 = 10;
int num2 = 20;
int c = num1;
num1 = num2;
num2 = c;
/*
方式二:好处:不用定义临时变量
弊端:①、相加操作可能超出存储范围
②、有局限性:值能适用于数值类型,如果是char,String那么不适用
*/
int num1 = 10;
int num2 = 20;
num1 = num1 +num2;
num2 = num1 - num2;
num1 = num1 - num2;
// 方式三:使用位运算符
int num1 = 10;
int num2 = 20;
num1 = num1 ^ num2;
num2 = num1 ^ num2;
num1 = num1 ^ num2;
9. 三元运算符
定义: (条件表达式) ? 表达式1 : 表达式2;
note:表达式1与表达式2要求是一致的(可以统一为一个类型),因为返回值类型只能是一个
10. if-else
如果if-else 结构中的执行语句只有一行,那么"{}"可以省略(不建议)
11. switch-case
switch中只能是:byte、short、int、枚举(5.0新增)、String(7.0新增)
note:一旦匹配成功,则进入响应的case中,调用其执行语句,当调用完执行语句后,则仍向下执行其他case中的执行语句(不进行条件判断),直到遇到break或者此switch-case结构末尾为止结束。
int number=23;
switch(number) {
default:
System.out.println("other");
case 0:
System.out.println("zero");
break;
case 1:
System.out.println("one");
break;
case 2:
System.out.println("two");
break;
case 3:
System.out.println("three");
break;
}//没有匹配,跳到最上面执行default,之后无break,执行zero
效率:三元运算 > switch-case > if-else
12. break 与 continue 的区别
break和continue关键字的使用
适用范围 循环中使用的作用(不同点) 相同点
break: switch-case 结束当前循环 关键字的后面不能声明执行语句
循环结构
continue: 循环结构 结束当次循环 关键字的后面不能声明执行语句
13. 求取100以内所有的质数
label:for(int i = 2;i <= 100;i++) {//遍历100000以内的自然数
//优化二:对本身是质数的自然数是有效的。
for(int j = 2;j <= Math.sqrt(i);j++) {//j:被i去除
if(i%j == 0) {//i被j除尽
continue label;
}
}
//能执行到此步骤的,都是质数
System.out.println(i);
}
14. 数组
14.1 数组的基本概念
1. 数组是有序排列的,数组元素既可以是基本数据类型,也可以是引用数据类型
2. 数组的大小一旦确定,就不可以被更改
3. 数组元素的默认初始化值:整型(0),浮点型(0),char(0(ASC码值)),布尔型(false),引用数据类型(null)
4.多维数组的创建方式
int[] arr[] = new int[][] {{1,2,3},{4,5},{6,7,8}};
int[][] arr1 = new int [2][3];//内层元素初始化值:与以为数组初始化值相同
//外层元素的初始化值:地址值
int[][] arr2 = new int [2][];//内层元素初始化值:不能调用,否则报错
//外层元素初始化值:null
14.2 数组练习
//int[] x,y[]; 在给x,y变量赋值以后,以下选项允许通过编译的是:
a) x[0] = y; no
b) y[0] = x; yes
c) y[0][0] = x; no
d) x[0][0] = y; no
e) y[0][0] = x[0]; yes
f) x = y; no //虽然都存的是地址值,但是类型不同,无法传递
//杨辉三角
int[][] arr = new int[10][];
for (int i = 0; i <= 9; i++) {
arr[i] = new int[i + 1];
for (int j = 0; j <= i; j++) {
if (j == 0 || i == j) {
arr[i][j] = 1;
} else {
arr[i][j] = arr[i - 1][j] + arr[i - 1][j - 1];
}
}
}
/*
创建一个长度为6的int型数组,要求数组元素的值都在1-12之间,且是随机赋值。同时,要求
元素的值各不相同。
*/
int[] arr = new int[6];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) (Math.random() * 12 + 1);//[0,1)->[0,12)->[1,13)
for (int j = 0; j < i; j++) {
if(arr[i] == arr[j]){
arr[i] = (int) (Math.random() * 12 + 1);
j --;
}
}
}
String[] arr = new String[]{"JJ", "DD", "MM", "BB", "GG", "AA"};
//数组的复制
String[] arr1 = new String[arr.length];
for (int i = 0; i < arr1.length; i++) {
arr1[i] = arr[i];
}
//数组元素的反转
String[] arr2 = new String[arr.length];
for (int i = 0, j = arr.length - 1; i < arr.length; i++, j--) {
arr2[i] = arr[j];
}
//数组元素的查找(线性查找)
int[] arr3 = new int[]{-98, -34, 2, 34, 54, 66, 79, 105, 210, 333};
int findNumber = 66;
boolean flag = false;
for (int i = 0; i < arr3.length; i++) {
if (arr3[i] == findNumber) {
System.out.println("找到!索引为:" + i);
flag = true;
break;
}
}
if (!flag) {
System.out.println("没有");
}
//二分查找(所查数组必须有序)
int startIndex = 0;
int endIndex = arr3.length - 1;
int middleIndex;
boolean flag1 = false;
while (startIndex < endIndex) {
middleIndex = (startIndex + endIndex) / 2;
if (findNumber > arr3[endIndex] || findNumber < arr3[startIndex]) {
break;
} else if (findNumber == arr3[middleIndex]) {
System.out.println("找到了,索引为:" + middleIndex);
flag1 = true;
break;
} else if (findNumber > arr3[middleIndex]) {
startIndex = middleIndex;
} else {
endIndex = middleIndex;
}
}
if (!flag1) {
System.out.println("没找到!");
}
//1.冒泡排序
int[] arr = new int[]{43, 32, 76, -98, 0, 64, 33, -21, 32, 99, -5, 458, -78, 66, 76};
for (int i = 0; i < arr.length - 1; i++) {//总共比较多少轮
for (int j = 0 ; j <= arr.length - 2; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
15. 面向对象
1.Java类及类的成员:属性、方法、构造器;代码块、内部类
2.面向对象的大特征:封装性、继承性、多态性、(抽象性)
3.其它关键字:this、super、static、final、abstract、interface、package、import等
16. 方法的重载(overload)
1.定义:在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。
"两同一不同":同一个类,相同方法名
参数列表不同:参数个数不同,参数类型不同
2.判 断:
与void show(int a,char b,double c){}构成重载的有:
a) void show(int x,char y,double z){} // no
b) int show(int a,double c,char b){} // yes
c) void show(int a,double c,char b){} // yes
d) boolean show(int c,char b){} // yes
e) void show(double c){} // yes
f) double show(int x,char y,double z){} // no
g) void shows(){double c} // no
3.可变个数形参的方法(JDK5.0新增)
1)可变个数形参的格式:数据类型... 变量名
2)当调用可变个数形参的方法时,传入的参数个数可以是0个,1个,2个 ...
3)可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载
4)可变个数形参的方法与本类中方法名相同,形参类型也相同的数组之间不构成重载,两者不能共存
5)可变个数形参在方法的形参中,必须声明在末尾!
6)可变个数形参在方法的形参中,最多只能声明一个可变形参。
17. 方法形参的传递机制:值传递
值传递机制:
1)如果参数是基本数据类型,此时,实参赋给形参的是实参真实存储的数据值
2)如果变量是引用数据类型:此时,赋值的是变量所保存的数据的地址
public class Change {
public static void main(String[] args) {
int m=10;
int n=20;
System.out.println("m="+m+" n="+n);//m=10,n=20
//交换两个变量值的操作
Change change = new Change();
change.swap(m, n);
System.out.println("m="+m+" n="+n); //m=10,n=20
}
public void swap(int m,int n) {
int temp=m;
m=n;
n=temp;
}
}
3)内存的结构:栈(局部变量)、堆(new出来的结构:对象(非static的成员变量)、数组、接口)
4)变量:成员变量 vs 局部变量(方法内、方法形参、构造器内、构造器形参、代码块内)
18.递归
定义:一个方法体内调用它自身,方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制。递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环。
/*
输入一个数据n,计算斐波那契数列(Fibonacci)的第n个值
1 1 2 3 5 8 13 21 34 55
规律:一个数等于前两个数之和
要求:计算斐波那契数列(Fibonacci)的第n个值,并将整个数列打印出来
*/
public static int getSum(int number) {
if (number == 1 || number == 2) {
return 1;
} else {
return getSum(number - 1) + getSum(number - 2);
}
}//调用该方法即可
19. 面向对象的特征一:封装与隐藏
1.封装性的体现:1)我们将类的属性私有化
2)不对外暴露的私有的方法
3)单例模式
2. 4种权限可以用来修饰类及类的内部结构:属性,方法,构造器,内部类。
但是类只可以使用缺省、public
3. a)private:只能在类内部使用
b)缺省:能在类内部和同一个包内使用
c)protected:能在类内部,同一个包和不同包的子类使用
d)public:能在类内部,同一个包,不同包的子类和同一个工程中使用
20. 构造器
1.如果没有显式的定义类的构造器的话,则系统默认提供一个空参的构造器
2.一旦我们显式的定义了类的构造器之后,系统就不再提供默认的空参构造器了。
3.一个类中,至少要有一个构造器。
4.总结:属性赋值的先后顺序
① 默认初始化
② 显式初始化
③ 构造器中初始化
④ 通过"对象.方法"或"对象.属性"的方式赋值
以上操作的先后顺序:①-->②-->③-->④
5.构造器的权限默认与类的权限相同
6.JavaBean是一种Java语言写成的可重用组件。
所谓javaBean,是指符合如下标准的Java类:
1)类是公共的
2)有一个无参的公共的构造器
3)有属性,且有对应的get、set方法
7.this关键字的使用
定义:this理解为:当前对象或当前正在创建的对象
① 我们在类的构造器中,可以显式的使用"this(形参列表)"的方式,调用本类中指定的其他构造器
② 构造器中不能使用"this(形参列表)"的方式调用自己;
③ 如果一个类中有n个构造器,那么最多有n-1个构造器中使用了使用"this(形参列表)"的方式
④ 规定:使用"this(形参列表)"的方式,必须声明在当前构造器的首行。
⑤ 构造器内部,最多只能声明一个"this(形参列表)"的方式,用来调用其他的构造器。
20.面向对象的特征之二:继承性
1.继承性的好处
①减少了代码的冗余,提高代码的复用性
②便于功能的扩展
③为之后多态性的使用提供了前提(接口)
2.体现:一旦子类A继承了父类B以后,子类A中就获取了父类B中声明的所有的属性和方法
(如果子父类有相同属性,那么就看左侧到底是父类的类型还是子类的类型,进而决定 调用的到底是谁的属性,因为堆空间中都存在子父类的属性(不进行属性的覆盖!))
3.Java中关于继承性的规定:
1)一个类可以被多个子类继承
2)Java中类的单继承性:一个类只能有一个父类
21.方法的重写
定义:子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作
规定:① 子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
② 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符。(子类都覆盖了父类,所以至少应该和父类权限一样大,甚至更大)
>特殊情况,子类不能重写父类中声明为private权限的方法
③ 返回值类型:
>父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void
>父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或者A类的子类
>父类被重写的方法的返回值类型是基本数据类型(比如double),则子类重写的方法的返回值类型(必须也是double)必须是相同的基本数据类型
④ 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类
例1:如果现在父类的一个方法定义成private访问权限,在子类中将此方法声明为default访问权限,那么这样还叫重写吗?no
例2:public void add(int a , int...arr);
public void add(int a, int[] arr); //注意这两者是方法的重写!
22. 子类对象实例化的全过程
1.从结果上来看:(继承性)
子类继承父类之后,就获得父类中声明的属性或方法
创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。
2.从过程上来看:
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,直到调用了Java.lang.Object类中空参的构造器为止。正因为加载过所有父类的结构,所以才可以看到内存中有父类中的结构,子类对象才可以考虑调用。
23. super关键字
super理解为:父类的
1) 我们可以在子类的方法或构造器中,通过使用"super.属性"或"super.方法"的方式,显式的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略super.
2) 特殊情况,当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须 显式的使用 super.属性 的方式,表名调用的是父类中声明的属性。
3) 特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的使用 super.属性 的方式,表名调用的是父类中被重写的方法。
4)我们可以在子类的构造器中显式的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器
5)"super(形参列表)"的使用,必须声明在子类构造器的首行
6)我们在类的构造器当中,针对于"this(形参列表)"或者"super(形参列表)"只能二选一,不能同时出现
7)在构造器的首行,没有显式的声明"this(形参列表)"或者"super(形参列表)",则默认调用的是父类中空参的构造器:super();
8)在类的多个构造器当中,至少有一个类的构造器使用了"super(形参列表)"的方式 ,调用父类中的构造器
9)如果子父类的关系复杂,很多子父类同时拥有相同的属性和方法,运用super关键字也仅仅是调用了直接父类中的属性或者方法。
24.面向对象特征之三:多态性
定义:父类的引用指向子类的对象(多态是运行时行为)
1.多态性前提:① 继承性
② 重写
2.多态的使用:有了多态性以后,我们在编译期,只能调用在父类中声明的方法,但在运行期,
我们实际执行的是子类重写父类的方法(虚拟方法调用)(编译看左面,运行看右 面)
3.对象的多态性,只适用于方法,不适用于属性(编译和运行都看左面)
/*
* 1.若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的
同名方法,系统将不可能把父类里的方法转移到子类中。
2.对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的
实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量
*
*/
public class Practice_03 {
public static void main(String[] args) {
Sub s = new Sub();
System.out.println(s.count);// 20
s.display();// 20
Base b = s;// Base b=new Sub(); 多态
System.out.println(b == s);// == 比较的是地址值
// 因为是引用数据类型,赋的是地址,一定是true
System.out.println(b.count);// 10
b.display();// 20
}
}
class Base {
int count = 10;
public void display() {
System.out.println(this.count);
}
}
class Sub extends Base {
int count = 20;// 属性方面不体现多态性
public void display() {
System.out.println(this.count);
}
}
public class InterviewTest1 {//p291
public InterviewTest1() {
}
public static void main(String[] args) {
Base1 base = new Sub1();
base.add(1, 2, 3);//b
Sub1 s = (Sub1)base;
s.add(1, 2, 3);//sub_2
}
}
class Base1 {
Base1() {
}
public void add(int a, int... arr) {
System.out.println("base1");
}
}
class Sub1 extends Base1 {
Sub1() {
}
public void add(int a, int[] arr) {
System.out.println("sub_1");
}//此方法是对父类方法的重写
public void add(int a, int b, int c) {
System.out.println("sub_2");
}
}
25.== 和 equals()的区别
一、== :运算符
1.可以使用在基本数据类型变量和引用数据类型变量中
2.如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等(不一定类型要相同)
int a =1;
double b =1.0;
System.out.println(a==b);//true,自动类型提升
如果比较的是引用数据类型变量,比较两个对象的地址值是否相同,即两个引用是否指向同一个对象实体
补充:==符号使用时,必须保证符号左右两端的变量类型一致(或者可以互相转化)。
二、equals()方法的使用:
1.是一个方法,而非运算符
2.只适用于引用数据类型
3.Object类中定义的equals方法()和==的作用是相同的,比较两个对象的地址值是否相同,即两个引用是否指向同一个对象实体
4.Object类中equals()的定义:
public boolean equals(Object obj) {
return (this == obj);
}
5.像String、Date、File、包装类 等都重写了Object类中的equals()方法,重写以后,比较的不是两个引用的地址是否相同,而是比较两个对象的"实体内容"是否相同。
6.通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的"实体内容"是否相同。 那么我们就需要对Object类中的equals()方法进行重写。
26.toString()
1.当我们输出一个对象的引用时,实际上就是调用当前对象对此对象的toString()方法
2.Object类中toString()的定义:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
3.像String\Date\File\包装类 都重写了Object中的toString()方法。使得在调用对象的toString()时,返回“实体内容信息”
27.包装类
27.1 转换关系
//1.基本数据类型、String-->包装类:调用包装类的构造器
int num1 = 10;
Integer num2 = new Integer(num1);
Integer num3 = new Integer("123");//int,float,double,boolean都可以转换
//2.包装类-->基本数据类型:调用基本数据类型的xxxValue()
//JDK5.0新增自动装箱,自动拆箱
int num4 = num2.intValue();
//3.基本数据类型-->String
//方式一
String str1 = num1 + "";
//方式二:调用String重载的valueOf()
String str2 = String.valueOf(num1);//"10"
//4.String-->基本数据类型:调用包装类型的parseXxx()
int num5 = Integer.parseInt("123");//123
27.8 练习
Object o1 = true ? new Integer(1) : new Double(2.0);//1.0 自动类型提升
/*
Integer内部定义了IntegerCache结构,IntegerCache中定义了
Integer[],保存了从-128-127范围内的整数。如果我们使用自动装箱
的方式,给Integer赋值在-128-127范围内时,可以直接使用数组中的元素,
不用new了。目的:提高效率
*/
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j);//两个引用类型,地址不同false
Integer m = 1;
Integer n = 1;
System.out.println(m == n);//true
Integer x = 128;
Integer y = 128;
System.out.println(x == y);//false
28.static 关键字
① 静态变量随着类的加载而加载,可以通过“类.静态变量”的方式进行调用
② 静态变量的加载要早于对象的创建
③ 由于类只会加载一次,则静态变量在内存中也只会存在一份,存在方法区的静态域中。
④在静态的方法内,不可以使用this关键字、super关键字(生命周期不同)
栈:局部变量
堆:new出来的结构:对象、数组
方法区:类的加载信息、静态域、常量池
例题:
static 修饰的属性,相较于实例变量,有哪些特别之处(>=3点)
1.实例变量在对象创建后才加载,而静态属性随着类的加载而加载
2.静态属性一旦更改,所有对象中的静态属性都会改变
3.静态属性可以通过 “类.属性”的方法来调用
29.单例模式
1.定义:所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例。
//单例模式:饿汉式
class Singleton {
//1.私有化的构造器
private Singleton() {
}
//2.内部创建类的对象,要求此属性也是静态的
private static Singleton instance = new Singleton();
//3.提供公共的静态方法,返回类的对象
public static Singleton getInstance() {
return instance;
}
}
//懒汉式
//懒汉式:线程安全
class Singleton{
//私有的构造器
private Singleton(){}
private static Singleton instance = null;
private static Singleton getInstance(){
if(instance == null){
synchronized(Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
30.代码块
1.代码块的作用:用来初始化类、对象(只有静态代码块与非静态代码块之分)
2.静态代码块
>内部可以有输出语句
>随着类的加载而执行,而且只执行一次
>作用:初始化类的信息
>如果一个类中定义了多个静态代码块,则按照声明的顺序执行
>静态代码块的执行要优先于非静态代码块的执行
>静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构
3.非静态代码块
>内部可以有输出语句
>随着对象的创建而执行
>每创建一个对象,就执行一次非静态代码块
>作用:可以在创建对象时,对对象的属性等进行初始化
>如果一个类中定义了多个非静态代码块,则按照声明的顺序执行
>非静态代码块内部可以调用静态的属性、静态的方法或非静态的属性、非静态的方法
4.对属性可以赋值的位置:
①默认初始化
②显式初始化
③构造器中初始化
④通过“对象.属性”或“对象.方法”的方式进行赋值
⑤代码块中赋值
执行顺序:①-->②/⑤-->③-->④(②⑤谁先执行,谁后执行要看他们的位置,谁在前面谁就先执行)
5.执行顺序练习
class Root {
static {
System.out.println("Root的静态初始化块");
}
{
System.out.println("Root的普通初始化块");
}
public Root() {
System.out.println("Root的无参数的构造器");
}
}
class Mid extends Root {
static {
System.out.println("Mid的静态初始化块");
}
{
System.out.println("Mid的普通初始化块");
}
public Mid() {
System.out.println("Mid的无参数的构造器");
}
public Mid(String msg) {
// 通过this调用同一类中重载的构造器
this();
System.out.println("Mid的带参数构造器,其参数值:" + msg);
}
}
class Leaf extends Mid {
static {
System.out.println("Leaf的静态初始化块");
}
{
System.out.println("Leaf的普通初始化块");
}
public Leaf() {
// 通过super调用父类中有一个字符串参数的构造器
super("gcc");
System.out.println("Leaf的构造器");
}
}
public class Order {
public static void main(String[] args) {
new Leaf();
System.out.println();
new Leaf();
}
}
/*
输出顺序为:
Root的静态初始化块
Mid的静态初始化块
Leaf的静态初始化块
Root的普通初始化块
Root的无参数的构造器
Mid的普通初始化块
Mid的无参数的构造器
Mid的带参数构造器,其参数值:gcc
Leaf的普通初始化块
Leaf的构造器
Root的普通初始化块
Root的无参数的构造器
Mid的普通初始化块
Mid的无参数的构造器
Mid的带参数构造器,其参数值:gcc
Leaf的普通初始化块
Leaf的构造器
*/
class Father {
static {
System.out.println("11111111111");
}
{
System.out.println("22222222222");
}
public Father() {
System.out.println("33333333333");
}
}
public class Son extends Father {
static {
System.out.println("44444444444");
}
{
System.out.println("55555555555");
}
public Son_10() {
System.out.println("66666666666");
}
public static void main(String[] args) { // 由父及子 静态先行
System.out.println("77777777777");
System.out.println("************************");
new Son ();
System.out.println("************************");
new Son ();
System.out.println("************************");
new Father();
}
}
/*
输出结果:
11111111111
44444444444
77777777777
************************
22222222222
33333333333
55555555555
66666666666
************************
22222222222
33333333333
55555555555
66666666666
************************
22222222222
33333333333
*/
6.总结:由父及子,静态先行
31. final关键字
1.定义:final用来修饰一个类:此类不能被其他类所继承.
用来修饰方法:表名此方法不可以被重写
用来修饰变量:此时的“变量”就称为是一个常量
2.练习
public class Something {
public static void main(String[] args) {
Other o = new Other();
new Something().addOne(o);
}
public void addOne(final Other o) {
// o = new Other(); 报错
o.i++;// o不可改变但是其中的属性可以改变
}
}
class Other {
public int i;
}