java基础
记录自己快速熟悉Java基础的历程
一、Java概述
1.1 什么是Java
Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程 。
1.2 Java三大版本
- Java SE(J2SE,Java 2 Platform Standard Edition,标准版)
- Java EE(J2EE,Java 2 Platform Enterprise Edition,企业版)
- Java ME(J2ME,Java 2 Platform Micro Edition,微型版)
1.3 JVM、JRE和JDK的关系
- JVM
Java Virtual Machine是Java虚拟机,Java程序需要运行在虚拟机上,不同的平台有自己的虚拟机,因此Java语言可以实现跨平台。 - JRE
Java Runtime Environment包括Java虚拟机和Java程序所需的核心类库等。核心类库主要是java.lang包:包含了运行Java程序必不可少的系统类,如基本数据类型、基本数学函数、字符串处理、线程、异常处理类等,系统缺省加载这个包只运行java程序的话,只需要安装JRE
- JDK
Java Development Kit是提供给Java开发人员使用的,其中包含了Java的开发工具,也包括了JRE。所以安装了JDK,就无需再单独安装JRE了。其中的开发工具:编译工具(javac.exe),打包工具(jar.exe)等Java开发环境,就安装JDK,包含JRE等
1.4 Java语言有哪些特点
- 简单易学(Java的语法与C/C++很接近)
- 面向对象(封装,继承,多态)
- 跨平台(.java文件->javac->.class->java通过JVM运行)
- 解释型语言
- Java有自动内存管理机制,不需要程序员手动释放无用内存
1.5 什么是字节码?字节码的好处是什么?
- 字节码:
Java源代码经过虚拟机编译器编译后产生的文件(即扩展为.class的文件),它不面向任何特定的处理器,只面向虚拟机。 - 采用字节码的好处:
Java语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以Java程序运行时比较高效,而且,由于字节码并不专对一种特定的机器,因此,Java程序无须重新编译便可在多种不同的计算机上运行。
1.6 Java程序执行流程
二、Java基础
2.1 输入输出、数据类型
2.1.1 Hello world
1 | //Hello.java |
- 源文件名与类名相同
- 每个类只能有一个main(类的入口方法)
- 使用
//
进行注释
2.1.2 基本数据类型
1 | //Variable.java |
- 数据类型的转换关系
- byte->short->int->long->float->double(精度链/范围链)
- 上面的链反向需强制类型转换
- 自动类型转换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42//Change_byte.java
public class Change_byte {
public static void main(String[] args) {
int aInt = 20;
long bLong = 50L;
double cDouble = 4.8;
//低优先级类型数据 + 高优先级类型数据 ——> 结果会自动转换为高优先级数据
long sum = aInt + bLong;
//long -> int 需要强制类型转换
int d = (int) bLong;
//double -> int 需要强制类型转换
int e = (int) cDouble;
System.out.println("自动类型转换 int—>long: " + sum);
System.out.println("强制类型转换 long—>int: " + d);
System.out.println("强制类型转换 double—>int: " + e);
System.out.println();
//int 和 byte 转换
byte fByte = (byte) aInt; //高转低,强转
int gInt = fByte; //低转高,自动
System.out.println("高转低-强转,int->byte: " + fByte);
System.out.println("低转高-自动,byte->int: " + gInt);
System.out.println();
//int 和 char 转换
char hChar = 'a';
int iInt = hChar;
char j = (char) iInt;
System.out.println("低转高-自动,char->int: " + iInt);
System.out.println("高转低-强转,int->char: " + j);
System.out.println();
//byte 和 char 互转
byte m = (byte) hChar;
char n = (char) m;
System.out.println("char->byte,强转: " + m);
System.out.println("byte->char,强转: " + n);
}
}
2.1.3 引用数据类型
引用数据类型的内存分布,可以类比C/C++中的指针
- 数组
一维数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26//Bubble_sort.java
public class Bubble_sort {
public static void main(String[] args) {
int n=20;
int arr[]=new int[n];
Random numList = new Random();
for (int i = 0; i <n; i++) {
arr[i]= numList.nextInt(100);
}
int temp=0;
for (int i = 0; i< arr.length-1; i++){
for (int j=0;j<arr.length-1-i;j++){
if (arr[j]>arr[j+1]){
temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
System.out.println("第"+i+"轮");
for (int k=0;k<arr.length;k++){
System.out.print(arr[k]+"\t");
}
System.out.println();
}
}
}二维数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50//MiGong.java 迷宫
public class MiGong {
public static void main(String[] args) {
int arr[][] = new int[10][10];
for (int i=0;i<10;i++){
arr[0][i]=1;
arr[arr.length-1][i]=1;
}
for (int i=0;i<10;i++){
arr[i][0]=1;
arr[i][arr.length-1]=1;
}
arr[3][1]=1;
arr[2][2]=1;
Tools tools=new Tools();
tools.FindWay(arr,2,1);
for (int i=0;i<arr.length;i++){
for (int j=0;j<arr[i].length;j++){
System.out.print(arr[i][j]+"\t");
}
System.out.println();
}
}
}
class Tools{
public boolean FindWay(int arr[][],int start_i,int start_j){
if (arr[arr.length-2][arr.length-2]==2){
return true;
}else {
if (arr[start_i][start_j]==0){
arr[start_i][start_j]=2;
if (FindWay(arr,start_i+1,start_j))
return true;
else if (FindWay(arr,start_i,start_j+1))
return true;
else if (FindWay(arr,start_i-1,start_j))
return true;
else if (FindWay(arr,start_i,start_j-1))
return true;
else{
arr[start_i][start_j]=3;
return false; }
}else {
return false;
}
}
}
} - 类
- 接口
后面讲
2.1.4 输入
- 导入包
import java.util.Scanner;
1
2
3
4
5
6
7
8
9
10
11import java.util.Scanner;
//Print.java
public class Print {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
//接收一个int类型的值并进行输出
System.out.println(scanner.nextInt());
System.out.println(scanner.nextDouble());
System.out.println(scanner.nextBoolean());
}
}
2.2 运算符
2.2.1 算术运算符
1 | //arithmetic.java |
2.2.2 关系运算符
1 | //relationship.java |
2.2.3 位运算符
1 | //Bitwise_operators.java |
2.2.4 二进制
- 二进制的高位为符号位(0表示整数,1表示负数)
- 正数的原码、补码、反码都一样(三码合一)
- 负数的反码=原码符号位不变,其他位取反
- 负数的补码=反码+1
- 负数的反码=补码-1
- 0的反码和补码都是0
- 计算机在运行时,都是以补码形式运算
- 结果看原码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26import java.util.Scanner; //导入类
public class Input {
public static void main(String[] args) {
int a=2,b=3;
//a的原码、补码、反码:00000000 00000000 00000000 00000010
//b的原码、补码、反码:00000000 00000000 00000000 00000011
//-----------------------------------------------------
// 00000000 00000000 00000000 00000010
//且为正数,故值为2
System.out.println(a&b);//2
//-a的原码、补码、反码:10000000 00000000 00000000 00000010
//-a的反码:11111111 11111111 11111111 11111101
//-a的补码:11111111 11111111 11111111 11111110
//~-a的补码:00000000 00000000 00000000 00000001
//符号为0正数,结果为1
System.out.println(~-a);//1
//a的原码、11111111 11111111 11111111 11111101
//~a的反码:11111111 11111111 11111111 11111100
//~a的原码:10000000 00000000 00000000 00000011
//符号位1负数,结果为-3
System.out.println(~a);//-3
}
}
2.2.5 逻辑运算符
1 | //Logical.java |
2.2.6 三元运算符
1 | //Ternary.java |
2.3 流程控制
2.3.1 if-else
1 | import java.util.Scanner; |
2.3.2 if-elseif-else
1 | import java.util.Scanner; |
2.3.3 switch
1 | import java.util.Scanner; |
2.3.4 for
1 | import java.util.Scanner; |
2.3.5 while
1 | //While.java |
2.3.6 do_while
1 | //Do_while.java |
2.4 类与对象(OOP)
所有类均是Object的子类
状态+行为+标识=对象
2.4.1 属性
2.4.1.1 类成员变量
1 | //Object_test.java |
2.4.1.2 构造器
初始化对象,在创建对象时自动调用
构造方法的方法名称与其类名需相同
构造方法可以重载
1 | //Object_test.java |
2.4.1.3 访问修饰符
成员变量和成员方法均可被修饰
2.4.2 重载
1.方法名必须相同
2.参数类型不同或者个数不同
2.4.2.1 方法重载
1 | //overloaded.java |
2.4.2.2 可变参数
接收可变参数个数的方法
1.参数可以是任意个>=0
2.本质其实就是数组
3.形参列表中,可变参数必须在最后
4.一个方法中,只能有一个可变参数
1 | //Variadics.java |
2.4.3 String类
2.4.3.1 String类的介绍
- String 类是final的,意味着它不能被子类继承
- String 类实现了 Serializable 接口,意味着它可以序列化
- String 类实现了 Comparable 接口,意味着最好不要用
==
来比较两个字符串是否相等,而应该用equals()
方法
2.4.3.2 hashCode 方法
每一个字符串都会有一个 hash 值
1 | //String类的hashcode方法实现 |
2.4.3.3 substring 方法
用来截取字符串
1 | //String_01.java |
2.4.3.4 indexOf 方法
indexOf 方法用于查找一个子字符串在原字符串中第一次出现的位置
1 | //String_01.java |
2.4.3.5 引用数据类型的拷贝
1 | //Person.java |
2.4.4 包
在 Java 中,使用
package
来解决名字冲突
Java中的包:如何创建、导入和使用 package 来优化代码结构
例如:
小明的Person
类存放在包ming
下面,因此,完整类名是ming.Person
;
小红的Person
类存放在包hong
下面,因此,完整类名是hong.Person
;
小军的Arrays
类存放在包mr.jun
下面,因此,完整类名是mr.jun.Arrays
;
JDK 的Arrays
类存放在包java.util
下面,因此,完整类名是java.util.Arrays
。
在定义class
的时候,我们需要在第一行声明这个class
属于哪个包。
Java中常用的包
- java.lang.* //默认使用的包(默认引入,不需要手动引入)
- java.util.* //常用工具需要的包
- java.net.* //网络编程需要的包
- java.awt.* //界面开发需要的包
import xxxx
(包名),可以*通配导入,也可以具体类导入(推荐)
2.4.5 封装
将方法封装到类内部,根据需要进行过滤验证,保证安全性
数据被保护在类的内部,尽可能地隐藏内部的实现细节,只保留一些对外接口使之与外部发生联系。
2.4.5.1 无参构造器封装
1 | //encapsulation.java |
2.4.5.2 有参构造器
1 | //encapsulation.java |
2.4.6 继承
解决多类情况下的代码复用问题
在 Java 语言中继承就是子类继承父类的属性和方法,使得子类对象(实例)具有父类的属性和方法,或子类从父类继承方法,使得子类具有父类相同的方法
- 上图继承关系可以递归下去
- Java中继承机制是单继承,即子类只能继承一个父类
- 私有的属性和方法不能在子类直接访问,但是可以间接操作
什么时候适合使用继承?什么是什么的关系才能用
2.4.6.1 简单的继承
1 | //inherit.java |
2.4.6.2 继承细节
2.4.6.2.1 私有属性、方法相关
父类写get方法,来获取父类的私有属性内容
父类写call方法,来间接调用父类的私有方法
1 | //inherit.java |
2.4.6.2.2 初始化相关
子类在初始化时(即:创建对象时(调用构造方法时)),默认调用父类的无参构造器,即默认在子类的构造方法中阴藏写着super();
如果父类没有无参构造器,则必须在子类的构造方法中用super(实参)指定使用父类的哪个构造器,否则报错
super在使用时,必须放在构造器的第一行,且调用一直追溯到Object类
(俗称:先有爸爸后有儿)
super()与this()不可共存
1 | //inherit.java |
2.4.6.2.3 查找关系相关
场景:子类继承了父类,但是子类的属性和父类的相同,那调用使用的是哪一个呢?
- 首先查看子类是否有该属性或者方法
如果有且可以访问,则返回
如果没有则继续向上查找,以此类推
如果查找到一个则返回并终止查询,如果查找过程中出错也终止 - 如果直接使用成员或者this.成员(二者等价),依旧同上规则查找。
- 如果是super.成员,则直接跳过子类去父类查找
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19//Call_Relationship.java
public class Call_Relationship {
public static void main(String[] args) {
Son son = new Son();
System.out.println(son.name);
System.out.println(son.age);
}
}
class GrandFather {
String name="爷爷";
String habby="喝酒";
}
class Father extends GrandFather {
String name="爸爸";
int age=50;
}
class Son extends Father {
String name="儿子";
}
2.4.6.2.4 this和super的比较
2.4.6.3 方法重写
子类有一个方法和父类的某个方法的名称、返回类型、参数一样,即子类的方法覆盖了父类的方法,称为子类重写了父类的方法
1.子类返回类型和父类返回类型一样
2.子类返回类型是父类返回类型的子类
3.子类不能缩小父类成员的访问权限
public > protected > default > private
1 | //override.java |
2.4.6.3.1 重载和重写的比较
2.4.7 多态
解决多类情况下的代码复用问题和减低维护成本
- 方法的多态,类似重载和重写去理解
- 类的多态,重点!!!难点!!!
- 多态的前提:子类继承父类、父类引用指向子类的对象、子类重写父类的方法
- 一个对象的编译类型和运行类型可以不一致,也可一样
Animal animal = new Dog();
和animal =new Cat();
当编译时,animal 的编译类型就是Animal,而指向的是Dog子类对象
当运行时,animal 可以重新改变指向到Cat子类对象 - 多态的向上转型
Object obj = new Dog();
就是例子,Dog是Object的子类,创建对象时,obj指向了子类Dog,即可以理解为Dog类向上转型(类型),但运行时是在Dog类
- 注意事项
- 可以调用父类的所有成员(但是需考虑访问权限)
- 不可以调用子类的方法(如需访问,需要向下转型,见向下转型)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105//Polymorphism.java
public class Polymorphism {
public static void main(String[] args) {
//编译类型是Animal,运行类型是Dogss
Animal animal = new Dogss("旺财");//这里是向上转型
Food food = new Bone("骨头");
Persons person = new Persons("张三");//这里是编译型和运行型一样
person.feed(animal,food);
System.out.println("====================");
//编译类型是Animal,运行类型是Catss
Animal animal1=new Catss("汤姆");
Food food1=new Fish("三文鱼");
person.feed(animal1,food1);
System.out.println("====================");
// animal.sleep();
//报错(编译报错),不能调用子类的方法,因不是重写的方法
System.out.println("====================");
//子类重写父类方法就可以调用,但是子类自己写的方法不能调用
animal.eat();//运行调用,即运行态
//子类重写的方法,运行时就调用重写的,未重写的就调用父类的
}
}
//食物类
class Food{
private String name;
public Food(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Fish extends Food{
public Fish(String name) {
super(name);
}
}
class Bone extends Food{
public Bone(String name) {
super(name);
}
}
//动物类
class Animal{
private String name;
public Animal(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void eat(){
System.out.println(getName()+"父类正在吃饭");
}
}
class Dogss extends Animal{
public Dogss(String name) {
super(name);
}
public void sleep(){
System.out.println(getName()+"正在睡觉");
}
public void eat(){
System.out.println(getName()+"子类正在吃饭");
}
}
class Catss extends Animal{
public Catss(String name) {
super(name);
}
}
//主人
class Persons{
private String name;
public Persons(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//如果不使用多态,形参列表一直在变,即多一个动物就要重载一次,代码冗余度高
public void feed(Animal animal,Food food){
System.out.println(this.name+"喂"+animal.getName()+"吃"+food.getName());
}
}
2.4.7.1 向下转型
子类类型 引用名=(子类类型)父类引用;
- 要求原父类的引用必须指向的是当前目标类型的对象
- 只能强转父类的引用,不能强转父类的对象(对象在堆中,引用在栈中)
- 向下转型后可以访问子类的成员了(上面的例子无法访问)
1
2
3
4
5
6
7
8
9
10
11//上述基础上转类型,即可调用子类的特有方法
//条件1
Animal animal = new Dogss("旺财");//这里是向上转型
Dogss dogss = (Dogss) animal; //这里是向下转型
((Dogss) animal).sleep();
System.out.println("====================");
//Catss catss = (Catss) animal;//报错,意思就说想把狗变成猫
//条件2
Animal animal2 = new Animal("山猪");
Dogss dogss1 = (Dogss) animal2; //报错,原理其实是上面的狗变猫
2.4.7.2 instanceof
判断对象的运行类型是XX或者XX的子类
1 | //Use_Instanceof.java |
2.4.7.3 动态绑定机制
- 当调用对象的方法时,该方法会和对象的运行类型绑定
- 当调用对象的属性时,没有动态绑定机制,那里调用那里使用
- 如果没有,则发挥继承机制查找
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46//Dynamic_binding.java
public class Dynamic_binding {
public static void main(String[] args) {
//编译类型AAA,运行类型BBB
AAA a = new BBB();
//调用链:子类找sum有,直接使用子类sum
System.out.println(a.sum()); //40
//调用链:子类找sum1有,直接使用子类sum1
System.out.println(a.sum1());; //30
//注销掉子类的sum方法的调用链:
// 子类找sum没,去父类找到使用父类sum,但是父类中有个getI方法,但是子类父类都有,用哪个呢?这时就看运行类型是谁就用谁的,如果子类没有sum方法,就找父类,父类也没有,就报错
System.out.println(a.sum());//30
//注销掉子类的sum1方法的调用链:
//子类找sum1没,去父类找到使用父类sum1,但是父类中有个变量i,这个用谁的呢?答案:用父类的
System.out.println(a.sum1());//20
//结论:
// 1.当调用对象的方法时,该方法会和对象的运行类型绑定
// 2.当调用对象的属性时,没有动态绑定机制,那里就调用那里使用
// 3.如果没有,则发挥继承机制查找
}
}
class AAA{
public int i=10;
public int sum(){
return getI()+10;
}
public int getI(){
return i;
}
public int sum1(){
return i+10;
}
}
class BBB extends AAA{
public int i=20;
public int sum(){
return i+20;
}
public int getI(){
return i;
}
public int sum1(){
return i+10;
}
}
2.4.8 Object类
所有类的根类,所有类默认继承自Object类
2.4.8.1 ==
和equal
==
是比较运算符
1.可以比较基本类型,也可以比较引用类型
2.如果比较基本类型,则比较值是否相等
3.如果比较引用数据类型,则比较的是指针的指向是否相等
1 | //Object_test01 |
equal
:Object默认是比较两个对象是否一样,只能判断引用类型
其他很多类都重写了该方法,比较的内容与具体实现有关
1 | //Object_test01 |
2.4.8.2 hashcode
- 如果两个引用指向同一对象,则哈希值一样
- 如果两个引用指向不是统一对象,则哈希值不一样
- 类似指针理解,内存地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16//Object_test02.java
public class Object_test02 {
public static void main(String[] args) {
X x = new X();
X x1 = new X();
X x2=x;
System.out.println("x的hash值="+x.hashCode());
System.out.println("x1的hash值="+x1.hashCode());
System.out.println("x2的hash值="+x2.hashCode());
Y y = new Y();
System.out.println("y的hash值="+y.hashCode());
}
}
class X{}
class Y{}
2.4.8.3 toStirng
- Object类的实现
1
2
3
4//返回完整类名和16进制哈希值
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
} - 重写实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42//Object_test03.java
public class Object_test03 {
public static void main(String[] args) {
Person_s person_s = new Person_s("张三", 18);
//以下方式等价
System.out.println(person_s);
System.out.println(person_s.toString());
}
}
class Person_s {
private String name;
private int age;
public Person_s(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "Person_s{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
2.4.8.4 finalize(java9开始不鼓励使用)
垃圾回收器,类似析构函数
Object的finalize方法有接口没实现,需要可重写
1 | //Object_test04.java |
2.5 递归
自身调用自己,从堆栈内存分析,好理解
2.5.1 汉诺塔
1 | /* |
2.5.2 斐波那契
1 | //Fibonacci.java |
2.6 类变量
2.6.1 静态变量
静态变量使用static修饰,类加载的时候生成
特点:被同一个类的所有对象共享
访问控制权限依旧生效
可以通过对象名调用,也可以通过类名调用
1 | //OOP_styatic_var.java |
2.6.2 静态方法
- 可以不创建对象就使用类的方法(类似的有Math类的一些方法)
- 提高开发效率
- 可以通过对象名调用,也可以通过类名调用
- 静态方法内不能使用this和super
- 静态方法只能访问静态成员
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27//OOP_styatic_methon.java
public class OOP_styatic_methon {
public static void main(String[] args) {
System.out.println(Tools.add(100,200));
System.out.println(Tools.sub(100,200));
System.out.println(Tools.mul(100,200));
System.out.println(Tools.div(100,200));
}
}
class Tools{
public static int add(int a,int b)
{
return a+b;
}
public static int sub(int a,int b)
{
return a-b;
}
public static int mul(int a,int b)
{
return a*b;
}
public static int div(int a,int b)
{
return a/b;
}
}
2.6.3 代码块(类创建调用)
- 解决代码复用情况(多个构造器均引用的代码)
- 代码块调用的顺序优先于构造器
- 创建对象时调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40//CodeBlock.java
public class CodeBlock {
public static void main(String[] args) {
Movie movie = new Movie("你好,张三");
System.out.println("===============");
Movie movie2 = new Movie("人民的名义", 100, "李四");
}
}
class Movie {
private String name;
private double price;
private String director;
{
System.out.println("电影屏幕打开...");
System.out.println("广告开始...");
System.out.println("电影正是开始...");
};
public Movie(String name) {
System.out.println("Movie(String name) 被调用...");
this.name = name;
}
public Movie(String name, double price) {
this.name = name;
this.price = price;
}
public Movie(String name, double price, String director) {
System.out.println("Movie(String name, double price, String director) 被调用...");
this.name = name;
this.price = price;
this.director = director;
}
}
2.6.3.1 静态代码块(类加载调用)细节1
- 对象创建时调用(先类加载,按照继承关系,再创建对象)
- 子类创建时父类加载调用
- 使用类的静态属性或方法调用
- 且只加载一次
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41//CodeBlockDetail.java
public class CodeBlockDetail {
private static BBB bbb;
public static void main(String[] args)
{
//1.对象创建时调用
// AAA aaa = new AAA();
System.out.println("========================");
//2.子类创建时父类加载调用
BBB bbb = new BBB();
System.out.println("========================");
//3.使用类的静态属性或方法调用
//4.一次加载类,只会被调用一次
AAA.staticMethod();
System.out.println(AAA.n);
}
}
class AAA{
public static int n=100;
static {
System.out.println("AAA的静态代码块");
}
public AAA()
{
System.out.println("AAA的构造方法");
}
public static void staticMethod()
{
System.out.println("AAA的静态方法");
}
}
class BBB extends AAA{
static {
System.out.println("BBB的静态代码块");
}
public BBB()
{
System.out.println("BBB的构造方法");
}
}
2.6.3.2 静态代码块细节2
- 调用静态代码块和静态属性初始化变量时,优先级相同,(如果有多个静态代码块)按照顺序执行
- 调用普通代码块和普通属性初始化变量时,优先级相同,(如果有多个普通代码块)按照顺序执行
- 静态代码块优先级高于普通代码块
- 构造器最后执行(构造器中第一行隐藏super()和本类普通代码块)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26//CodeBlockDetail2.java
public class CodeBlockDetail2 {
public static void main(String[] args) {
A a = new A();
}
}
class A{
//静态代码块
public static int n=getN();
static{
System.out.println("A类静态代码块被初始化");
}
public static int getN(){
System.out.println("A类静态变量n被初始化");
return 10;
}
//普通代码块
{
System.out.println("A类普通代码块被初始化");
}
public int n2=100;
public A(){
System.out.println("A类构造方法被初始化");
}
}
2.6.4 类加载和创建对象执行过程
- 父类加载,执行父类静态代码块和静态属性初始化(同优先级)
- 子类加载,执行子类静态代码块和静态属性初始化(同优先级)
- 创建对象
- 执行子类构造器,进入super(),进入父类构造器,再进入父类的普通代码块和普通属性的初始化,执行结束super(),回到子类的构造器继续执行子类的普通代码块和普通属性的初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63//CodeBlockDetail3.java
public class CodeBlockDetail3 {
public static void main(String[] args) {
//1.类加载 先加载 父类 A02 再加载 B02 //2.创建对象 从子类的构造器开始
new B02();//对象
}
}
class A02 { //父类
private static int n1 = getVal01();
static {
System.out.println("A02的一个静态代码块..");//(2)
}
{
System.out.println("A02的第一个普通代码块..");//(5)
}
public int n3 = getVal02();//普通属性的初始化
public static int getVal01() {
System.out.println("getVal01");//(1)
return 10;
}
public int getVal02() {
System.out.println("getVal02");//(6)
return 10;
}
public A02() {//构造器
//隐藏
//super()
//普通代码和普通属性的初始化......
System.out.println("A02的构造器");//(7)
}
}
class B02 extends A02 { //
private static int n3 = getVal03();
static {
System.out.println("B02的一个静态代码块..");//(4)
}
public int n5 = getVal04();
{
System.out.println("B02的第一个普通代码块..");//(9)
}
public static int getVal03() {
System.out.println("getVal03");//(3)
return 10;
}
public int getVal04() {
System.out.println("getVal04");//(8)
return 10;
}
public B02() {//构造器
//隐藏了
//super()
//普通代码块和普通属性的初始化...
System.out.println("B02的构造器");//(10)
}
}