JavaSE基础知识
一、初识Java语言
1.JDK
JDK目录结构:
- bin目录–主要存放JDK的各种工具命令
- conf目录–主要存放jdk的相关配置文件
- include目录–主要存放了一些平台的头文
- jmods目录–主要存放了JDK的各种模
- legal目录–主要存放了JDK各模块的授权文档
- lib目录–主要存放了JDK工具的一些补充jar包和源代码
• JDK– 称为Java开发工具包(Java Development Kit),目前的主流版本为JDK8、JDK11。
• JRE – 称之为Java SE运行时环境(Java SE Runtime Environment),提供了运行Java应用程序所必须的软件环境等。
• javac.exe – 编译器,主要用于将高级Java源代码翻译成字节码文件。
• java.exe – 解释器,主要用于启动JVM对字节码文件进行解释并执行。
2.JDK、JRE、JVM之间的关系
编译和运行
二、变量和数据类型
1.变量的基本概念
• 当需要在程序中记录单个数据内容时,则声明一个变量即可,而声明变量的本质就是在内存中申请一个存储单元,由于该存储单元中的数据内容可以发生改变,因此得名为"变量"。
• 由于存放的数据内容大小不一样,导致所需存储单元的大小不一样,在Java语言中使用数据类型加以描述,为了便于下次访问还需要给该变量指定一个名字,用于记录该变量对应的存储单元。
声明变量: 数据类型 变量名 = 初始值;
变量的注意事项
• Java是强类型语言,变量在使用前必须声明来指明其数据类型。
• 变量在使用之前必须初始化。
• 变量不能重复声明
2.标识符的命名法则
- 由数字、字母、下划线以及$等组成,其中数字不能开头
- 不能使用Java语言的关键字
- 区分大小写,长度没有限制但不宜过长
- 尽量做到见名知意,支持中文但不推荐使用
- 标识符可以给类/变量/属性/方法/包 起名字
3.注释
- 单行注释 // 注释内容
- 多行注释 /* 注释内容*/
- 文档注释 /** 注释内容*/
多行注释不允许嵌套使用
4.基本数据类型
byte、short、int、long、float、double、boolean、char
5.引用数据类型
数组、类、接口、枚举、标注
6.进制转换
正十进制转换为二进制的方式
①除2取余法:使用十进制整数不断地除以2取出余数,直到商为0时将余数逆序排序。
②拆分法:将十进制整数拆分为若干个二进制权重的和,有该权重下面写1,否则写0。
正二进制转换为十进制的方式
加权法:使用二进制中的每个数字乘以当前位的权重再累加起来
负十进制转换为二进制的方式
先将十进制的绝对值转换为二进制,然后进行按位取反再加1。
注意:负数的需要补码:按位取反,再加1 。
负二进制转换为十进制的方式
先减1再按位取反,合并为十进制整数后添加负号
单个字节表示的整数范围
• 在计算机中单个字节表示八位二进制位,其中最高位(最左边)代表符号位,使用0代表非负数,使用1代表负数,具体表示的整数范围如下:
• 非负数表示范围:0000 0000 ~ 0111 1111 => 0 ~ 127 => 0 ~ 2^7-1
• 负数表示范围:1000 0000 ~ 1111 1111 => -128 ~ -1 => -2^7 ~ -2^0
• 单个字节表示的整数范围是:-2^7 ~ 2^7-1,也就是-128 ~ 127.
整数类型
• Java语言中描述整数数据的类型有:byte、short、int、long,荐int类型
• 其中byte类型在内存空间中占1个字节,表示范围是:-2^7 ~ 2^7-1.
• 其中short类型在内存空间中占2个字节,表示范围是:-2^15 ~ 2^15-1.
• 其中int类型在内存空间中占4个字节,表示范围是:-2^31 ~ 2^31-1.
• 其中long类型在内存空间中占8个字节,表示范围是:-2^63 ~ 2^63-1.
• 在Java程序中直接写出的整数数据叫做直接量/字面值/常量,默认为int类型。若希望表达更大的直接量,则在直接量的后面加上l或者L,推荐L。
浮点类型
• Java语言中用于描述小数数据的类型:float 和 double,推荐double类型
• 其中float类型在内存空间占4个字节,叫做单精度浮点数,可以表示7位有效数字,范围:-3.403E38~3.403E38。
• 其中double类型在内存空间占8个字节,叫做双精度浮点数,可以表示15位有效数字,范围:-1.798E308~1.798E308。
• Java程序中直接写出的小数数据叫做直接量,默认为double类型,若希望表达float类型的直接量,则需要在直接量的后面加上f或者F.
布尔类型
• Java语言中用于描述真假信息类型有:boolean,数值只有:true 和 false。
• 布尔类型在内存空间中所占大小没有明确的规定,可以认为是1个字节。
字符类型
• Java语言中用于描述单个字符的数据类型:char类型。如:‘a’、'中’等。
• 其中char类型在内存空间中占2个字节并且没有符号位,表示的范围是:0 ~ 65535,由于现实生活中很少有数据能够被单个字符描述,因此以后的开发中更多的使用由多个字符串起来组成的字符串,使用String类型加以描述,如:“hello”、"world"等。
字符类型
• 计算机的底层只识别0和1组成的二进制序列,对于字符’a’这样的图案来说不满足该规则,因此该数据无法直接在计算机中存储,但现实生活中存在这样的图案数据需要计算机存储,为了使得该数据能够存储起来就可以给该数据指定一个编号,然后将编号存储起来即可,该编号就叫做ASCII。
• 要求掌握的ASCII有:‘0’ - 48 ‘A’ - 65 ‘a’ - 97 空格 - 32 换行符 - 10
基本数据类型之间的转换
• Java语言中基本数据类型之间的转换方式:自动类型转换和强制类型转换。
• 其中自动类型转换主要指从小类型到大类型之间的转换。
• 其中强制类型转换主要指从大类型到小类型之间的转换,语法格式如下:
目标类型 变量名 = (目标类型)源类型变量名;
7.字符编码
常见的编码表
● ASCII:美国标准信息交换码, 使用一个字节的低7位二位进制进行表示。
● ISO8859-1:拉丁码表,欧洲码表,使用一个字节的8位二进制进行表示。
● GB2312:中国的中文编码表,最多使用两个字节16位二进制为进行表示。
● GBK:中国的中文编码表升级,融合了更多的中文文字符号,最多使用两个字节16位二进制位表示。
● Unicode:国际标准码,融合了目前人类使用的所有字符,为每个字符分配唯一的字符码。所有的文字都用两个字节16位二进制位来表示。
三、运算符
1.算术运算符
• + 表示加法运算符
• - 表示减法运算符
• * 表示乘法运算符
• / 表示除法运算符
• % 表示取模/取余运算符
2.字符串连接运算符
• + 可以实现字符串的连接。同时可以实现字符串与其他数据类型“相连”
3.关系/比较运算符
• > 表示是否大于运算符 >= 表示是否大于等于运算符
• < 表示是否小于运算符 <= 表示是否小于等于运算符
• == 表示是否等于运算符 != 表示是否不等于运算符
• 所有以关系运算符作为最终运算的表达式结果一定是boolean类型
4.自增减运算符
• ++ 表示自增运算符,用于使得当前变量自身的数值加1的效果
• – 表示自减运算符,用于使得当前变量自身的数值减1的效果
• 只能用于变量,常数不可以
5.逻辑运算符
• && 表示逻辑与运算符,相当于"并且",同真为真,一假为假。
• || 表示逻辑或运算符,相当于"或者",一真为真,同假为假。
• ! 表示逻辑非运算符,相当于"取反",真为假,假为真。
• 逻辑运算符的操作数均为boolean表达式。
逻辑运算符的短路特性
• 对于逻辑与运算符来说,若第一个表达式为假则结果为假,此时跳过第二个表达式;
• 对于逻辑或运算符来说,若第一个表达式为真则结果为真,此时跳过第二个表达式;
6.条件/三目运算符
• 条件表达式? 表达式1: 表达式2
• 判断条件表达式是否成立,若成立则执行表达式1,否则执行表达式2 。
7.赋值运算符
• = 表示赋值运算符,用于将=右边的数据赋值给=左边的变量,覆盖变量原来的数值。
• 赋值表达式本身也有值,其本身之值即为所赋之值。
• += -= *= /= …
8.移位运算符
• << 左移运算符,用于将数据的二进制位向左移动,右边使用0补充
• >> 右移运算符,用于将数据的二进制位向右移动,左边使用符号位补充
• >>> 表示逻辑右移运算符,用于将数据的二进制位向右移动,左边使用0补充
9.位运算符
• & 表示按位与运算符,按照二进制位进行与运算,同1为1,一0为0.
• | 表示按位或运算符,按照二进制位进行或运算,一1为1,同0为0.
• ~ 表示按位取反运算符,按照二进制位进行取反,1为0,0为1.
• ^ 表示按位异或运算符,按照二进制位进行异或运算,同为0,不同为1.
四、流程控制语句
1.分支结构
//if分支结构
if(条件表达式) {
语句块;
}
//if else分支结构
if(条件表达式) {
语句块1;
} else {
语句块2;
}
//if else if else分支结构
if(条件表达式1) {
语句块1;
}else if(条件表达式2) {
语句块2;
}else {
语句块n;
}
//switch case分支结构
switch(变量/表达式) {
case 字面值1:
语句块1;
break;
case 字面值2:
语句块2;
break;
...
default:
语句块n;
}
/* 注意:switch()中支持的数据类型有:byte、short、char以及int类型,从jdk1.5开始支持枚举类型,从jdk1.7开始支持String类型。*/
2.循环结构
//for循环
for(初始化表达式; 条件表达式; 修改初始值表达式) {
循环体;
}
//双重for循环
for(初始化表达式1; 条件表达式2; 修改初始值表达式3) {
for(初始化表达式4; 条件表达式5; 修改初始值表达式6) {
循环体;
}
}
//break跳出所在循环体
for(...){
for(...){
循环体;
break;
}
}
//使用标号退出外层循环
outer: for(...){
for(...){
循环体;
break outer;
}
}
//while循环:
while(条件表达式) {
循环体;
}
//do while循环(熟悉)
do {
循环体;
} while(条件表达式);
//for each循环
for(元素类型 变量名 : 数组/集合名称) {
循环体;
}
• continue语句用在循环体中,用于结束本次循环而开始下一次循环.
• break用于退出当前语句块,break用在循环体中用于退出循环.
• for( ; ; ) 和while(true)表示无限循环,俗称“死循环”.
五、数组
1.声明一维数组
数据类型[] 数组名称 = new 数据类型[数组的长度];
int[] arr1 = new int[10];
int[] arr2 = {1,2,3,4,5};
2.声明二维数组
数据类型[][] 数组名称 = new 数据类型[行数][列数];
int[][] arr1 = new int[10][5];
int[3][2] arr2 = {{1,2},{3,4},{5,6}};
• 调用数组的length属性可以获取数组的长度.
• 可以通过下标的方式访问数组中的每一个元素。
• 数组的下标从0开始,对于长度为n的数组,下标的范围是0~n-1.
• 基本类型的数组创建后,其元素的初始值:byte、short、char、int、long为0;float和double为0.0;boolean为false。
数组的优缺点
• 可以直接通过下标(或索引)的方式访问指定位置的元素,速度很快。
• 数组要求所有元素的类型相同。
• 数组要求内存空间连续,并且长度一旦确定就不能修改。
• 增加和删除元素时可能移动大量元素,效率低。
3.数组工具类
• java.util.Arrays类可以实现对数组中元素的遍历、查找、排序等操作。
常用方法
4.栈堆
内存结构之栈区
• 栈用于存放程序运行过程当中所有的局部变量。一个运行的Java程序从开始到结束会有多次变量的声明。
内存结构之堆区
• JVM会在其内存空间中开辟一个称为“堆”的存储空间,这部分空间用于存储使用new关键字创建的数组和对象。
java分了5片内存:
①寄存器 ②本地方法区 ③方法区 ④栈 ⑤堆
栈: 存储的都是局部变量(函数中定义的变量,函数上的参数,语句中的变量).
只要数据运算完成所在的区域结束,该数据就会被释放。
堆: 用于存储数组和对象,也就是实体(实体就是用于封装多个数据).
a.每一个实体都有内存首地址值
b.堆内存中的变量都有默认初始化值。因为数据类型不同,值也不一样
c.垃圾回收机制
六、面向对象
面向对象三大特性: 封装、继承、多态
1.类和对象
类和对象的概念
• 对象主要指现实生活中客观存在的实体,在Java语言中对象体现为内存空间中的一块存储区域。
• 类简单来就是“分类”,是对具有相同特征和行为的多个对象共性的抽象描述,在Java语言中体现为一种引用数据类型,里面包含了描述特征/属性的成员变量以及描述行为的成员方法。
• 类是用于构建对象的模板,对象的数据结构由定义它的类来决定。
//类和成员变量的定义 如:
class 类名 { class Person{
数据类型 成员变量名 = 初始值; String name;
} }
//通常情况下,当类名由多个单词组成时,要求每个单词首字母都要大写。
//当成员变量由多个单词组成时,通常要求从第二个单词起每个单词的首字母大写 。
//对象的创建 如:
new 类名(); new Person();
//a.当一个类定义完毕后,可以使用new关键字来创建该类的对象,这个过程叫做类的实例化。
//b.创建对象的本质就是在内存空间的堆区申请一块存储区域, 用于存放该对象独有特征信息。
//引用 如:
类名 引用变量名 = new 类名(); Person p = new Person();
引用变量名.成员变量名; p.name = "张飞";
System.out.println(p.name)
//a.使用引用数据类型定义的变量叫做引用型变量,简称为"引用"。
//b.引用变量主要用于记录对象在堆区中的内存地址信息,便于下次访问。
2.成员变量的初始值
3.成员方法
//成员方法的定义 如:
class 类名 { class Person{
返回值类型 成员方法名(形参列表) { void show(){
成员方法体; System.out.println("这是show方法");
} }
} }
//当成员方法名由多个单词组成时,要求从第二个单词起每个单词的首字母大写。
①返回值类型的详解
• 返回值主要指从方法体内返回到方法体外的数据内容。
• 返回值类型主要指返回值的数据类型,可以是基本数据类型,也可以是引用数据类型。
• 当返回的数据内容是66时,则返回值类型写 int 即可
• 在方法体中使用return关键字可以返回具体的数据内容并结束当前方法。
• 当返回的数据内容是66时,则方法体中写 return 66; 即可
• 当该方法不需要返回任何数据内容时,则返回值类型写void即可。
②形参列表的详解
• 形式参数主要用于将方法体外的数据内容带入到方法体内部。
• 形式参数列表主要指多个形式参数组成的列表,语法格式如下:数据类型 形参变量名1, 数据类型 形参变量名2, …
• 当带入的数据内容是"hello"时,则形参列表写 String s 即可
• 当带入的数据内容是66和"hello"时,则形参列表写 int i, String s 即可
• 若该方法不需要带入任何数据内容时,则形参列表位置啥也不写即可。
③方法体的详解
• 成员方法体主要用于编写描述该方法功能的语句块。
• 成员方法可以实现代码的重用,简化代码。
④方法的调用
• 引用变量名.成员方法名(实参列表);
• 实际参数列表主要用于对形式参数列表进行初始化操作,因此参数的个数、类型以及顺序都要完全一致。
• 实际参数可以传递直接量、变量、表达式、方法的调用等。
Person p = new Person();
p.show();
⑤可变长参数
• 返回值类型 方法名(参数的类型… 参数名)
• 方法参数部分指定类型的参数个数是可以改变的,也就是0~n个 。
• 一个方法的形参列表中最多只能声明一个可变长形参,并且需要放到参数列表的末尾。
⑥方法的传参过程
• int max(int ia, int ib) { … … … } int a = 5; int b=6; int res = m.max(a,b);
- 为main方法中的变量a、b、res分配空间并初始化。
- 调用max方法,为max方法的形参变量ia、ib分配空间。
- 将实参变量的数值赋值到形参变量的内存空间中。
- max方法运行完毕后返回,形参变量空间释放。
- main方法中的res变量得到max方法的返回值。
- main方法结束后释放相关变量的内存空间。
⑦基本数据类型作为方法参数传递的原理分析
⑧参数传递的注意事项
①基本数据类型的变量作为方法的参数传递时,形参变量数值的改变通常 不会影响到实参变量的数值,因为两个变量有各自独立的内存空间;
②引用数据类型的变量作为方法的参数传递时,形参变量指向内容的改变 会影响到实参变量指向内容的数值,因为两个变量指向同一块内存空间 ;
③当引用数据类型的变量作为方法的参数传递时,若形参变量改变指向后 再改变指定的内容,则通常不会影响到实参变量指向内容的改变,因为 两个变量指向不同的内存空间.
class People{
private String name;
private int age;
public People() {
}
public People(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 class Test {
public static void test1(int a){
a+=10;
System.out.println("test1方法中:a=" + a);
}
public static void test2(People p){
p.setName("张飞");
p.setAge(35);
System.out.println("test2方法中:name=" + p.getName() + ",age=" + p.getAge());
}
public static void main(String[] args) {
int a = 10;
//值传递
test1(a);
System.out.println("main方法中:a=" + a);
People p = new People("关羽",40);
//引用传递
test2(p);
System.out.println("main方法中:name==" + p.getName() +",age==" + p.getAge());
}
}
//运行结果
test1方法中:a=20
main方法中:a=10
test2方法中:name=张飞,age=35
main方法中:name==张飞,age==35
● 形参为基本类型时,对形参的处理不会影响实参。
● 形参为引用类型时,对形参的处理会影响实参。
● String、Integer、Double等immutable类型,因为本身没有提供修改函数的操作,每次操作都是生成一个新对象,所以要特殊对待,可以理解为值传递,形参操作不会影响实参对象。
⑨内存结构之栈区
• 栈用于存放程序运行过程当中所有的局部变量。一个运行的Java程序从开始到结束会有多次方法的调用。
• JVM会为每一个方法的调用在栈中分配一个对应的空间,这个空间称为该方法的栈帧。一个栈帧对应一个正在调用中的方法,栈帧中存储了该方法的参数、局部变量等数据。
• 当某一个方法调用完成后,其对应的栈帧将被清除。
⑩传参的相关概念
• 参数分为形参和实参,定义方法时的参数叫形参,调用方法时传递的参数叫实参。
• 调用方法时采用值传递把实参传递给形参,方法内部其实是在使用形参。
• 所谓值传递就是当参数是基本类型时,传递参数的值,比如传递i=10,真实传参时,把10赋值给了形参。当参数是对象时,传递的是对象的值,也就是把对象的地址赋值给形参。
⑪成员变量和局部变量的区别
①成员变量直接定义在类中
局部变量定义在方法中,参数上,语句中
②成员变量在这个类中有效
局部变量只在自己所属的大括号内有效,大括号结束,局部变量失去作用域
③成员变量存在于堆内存中,随着对象的产生而存在,消失而消失
局部变量存在于栈内存中,随着所属区域的运行而存在,结束而释放
⑫成员变量和静态变量的区别
①成员变量所属于对象, 所以也称为实例变量.
静态变量所属于类, 所以也称为类变量.
②成员变量存在于堆内存中.
静态变量存在于方法区中.
③成员变量随着对象创建而存在, 随着对象被回收而消失.
静态变量随着类的加载而存在, 随着类的消失而消失.
④成员变量只能被对象所调用.
静态变量可以被对象调用, 也可以被类名调用.
七、方法和封装
1.构造方法
• 构造方法名与类名完全相同并且没有返回值类型,连void都不许有。
①默认构造方法
• 当一个类中没有定义任何构造方法时,编译器会自动添加一个无参空构造构造方法,叫做默认/缺省构造方法,如:Person(){}
• 若类中出现了构造方法,则编译器不再提供任何形式的构造方法
②构造方法的作用
• 使用new关键字创建对象时会自动调用构造方法实现成员变量初始化工作。
public class Person {
//Person的无参构造
public Person() {
}
}
③构造函数和一般函数的区别
①两个函数定义格式不同.
②构造函数是在对象创建时,就被调用,用于初始化,而且初始化动作只执行一次.而一般函数,是对象创 建后,需要调用才执行,可以被调用多次。
2.方法重载
• 若方法名称相同,参数列表不同,这样的方法之间构成重载关系(Overload)。
①重载的体现形式
• 方法重载的主要形式体现在:参数的个数不同、参数的类型不同、参数的顺序不同,与返回值类型和形参变量名无关,但建议返回值类型最好相同.
• 判断方法能否构成重载的核心:调用方法时能否加以区分.
②重载的实际意义
• 方法重载的实际意义在于调用者只需要记住一个方法名就可以调用各种不同的版本,来实现各种不同的功能。
• 如:java.io.PrintStream类中的println方法。
如何区分重载: 当函数同名时,只看参数列表。和返回值类型没关系.
//方法的重载
class Person {
public void sum(int a, int b){}
public void sum(int a, int b,int c){}
public void sum(int a, int b,double c){}
}
3.this关键字
• 若在构造方法中出现了this关键字,则代表当前正在构造的对象。
• 若在成员方法中出现了this关键字,则代表当前正在调用的对象。
• this关键字本质上就是当前类类型的引用变量。
工作原理
• 在构造方法中和成员方法中访问成员变量时,编译器会加上this.的前缀,而this.相当于汉语中"我的",当不同的对象调用同一个方法时,由于调用方法的对象不同导致this关键字不同,从而this.方式访问的结果也就随之不同。
使用方式
• 当局部变量名与成员变量名相同时,在方法体中会优先使用局部变量(就近原则),若希望使用成员变量,则需要在成员变量的前面加上this.的前缀,明确要求该变量是成员变量。
• this关键字除了可以通过this.的方式调用成员变量和成员方法外,还可以作为方法的返回值。
• 在构造方法的第一行可以使用this()的方式来调用本类中的其它构造方法。
注意事项
• 引用类型变量用于存放对象的地址,可以给引用类型赋值为null,表示不指向任何对象。
• 当某个引用类型变量为null时无法对对象实施访问(因为它没有指向任何对象)。此时,如果通过引用访问成员变量或调用方法,会产生NullPointerException 异常。
4.方法递归调用
• 递归本质就是指在方法体的内部直接或间接调用当前方法自身的形式。
注意事项
• 使用递归必须有递归的规律以及退出条件。
• 使用递归必须使得问题简单化而不是复杂化。
• 若递归影响到程序的执行性能,则使用递推取代之。
public class Array {
public static void main(String[] args) {
//求斐波那契数列第n项 1 1 2 3 5 8 13 21 …
Array arr = new Array();
int i = arr.result(5);
System.out.println(i);
}
//递归
public int result(int n){
if(1 == n | 2 == n){
return 1;
}else {
return result(n-1)+result(n-2);
}
}
}
5.封装
①封装的概念
• 通常情况下可以在测试类给成员变量赋值一些合法但不合理的数值,无论是编译阶段还是运行阶段都不会报错或者给出提示,此时与现实生活不符。
• 为了避免上述错误的发生,就需要对成员变量进行密封包装处理,来隐藏成员变量的细节以及保证成员变量数值的合理性,该机制就叫做封装。
②封装的实现流程
• 私有化成员变量,使用private关键字修饰。
• 提供公有的get和set方法,并在方法体中进行合理值的判断。
• 在构造方法中调用set方法进行合理值的判断。
public class Person {
private String name;
private int age;
//无参构造
public Person() { }
//有参构造
public Person(String name, int age) {
this.name = name;
this.age = age;
}
//get set方法
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;
}
}
③JavaBean的概念
• JavaBean是一种Java语言写成的可重用组件,其它Java 类可以通过反射机制发现和操作这些JavaBean 的属性。
• JavaBean本质上就是符合以下标准的Java类:
类是公共的
有一个无参的公共的构造器
有属性,且有对应的get、set方法
八、static关键字和继承
1.static关键字
①基本概念
• 使用static关键字修饰成员变量表示静态的含义,此时成员变量由对象层级提升为类层级,也就是整个类只有一份并被所有对象共享,该成员变量随着类的加载准备就绪,与是否创建对象无关。
• static关键字修饰的成员可以使用引用.的方式访问,但推荐类名.的方式。
②使用方式
• 在非静态成员方法中既能访问非静态的成员又能访问静态的成员。
• 在静态成员方法中只能访问静态成员不能访问非静态成员。
• 在以后的开发中只有隶属于类层级并被所有对象共享的内容才可以使用。
③构造块和静态代码块
• 构造块:在类体中直接使用{}括起来的代码块。
• 每创建一个对象都会执行一次构造块。
• 静态代码块:使用static关键字修饰的构造块。
• 静态代码块随着类加载时执行一次。
静态代码块、构造代码块、构造函数同时存在时的执行顺序
父类静态代码块 → 子类静态代码块 → 父类构造代码块 → 父类构造函数 → 子类构造代码块 → 子类构造函数
2.继承
①继承的概念
• 当多个类之间有相同的特征和行为时,可以将相同的内容提取出来组成一个公共类,让多个类吸收公共类中已有特征和行为而在多个类型只需要编写自己独有特征和行为的机制,叫做继承。
• 在Java语言中使用extends(扩展)关键字来表示继承关系。
• 如: public class Worker extends Person{} - 表示Worker类继承自Person类
其中Person类叫做超类、父类、基类。
其中Worker类叫做派生类、子类、孩子类。
• 使用继承提高了代码的复用性,可维护性及扩展性,是多态的前提条件。
②继承的特点
①子类不能继承父类的构造方法和私有方法,但私有成员变量可以被继承 只是不能直接访问。
②无论使用何种方式构造子类的对象时都会自动调用父类的无参构造方法, 来初始化从父类中继承的成员变量,相当于在构造方法的第一行增加代 码super()的效果。
③使用继承必须满足逻辑关系:子类 is a 父类,也就是不能滥用继承。
④Java语言中只支持单继承不支持多继承,也就是说一个子类只能有一个父 类,但一个父类可以有多个子类。
③子类对象创建的原理分析
④继承关系框架
⑤方法重写
• 从父类中继承下来的方法不满足子类的需求时,就需要在子类中重新写一个和父类一样的方法来覆盖从父类中继承下来的版本,该方式就叫做方法的重写.
⑥方法重写的原则
①要求方法名相同、参数列表相同以及返回值类型相同,从Java5开始允许 返回子类类型。
②要求方法的访问权限不能变小,可以相同或者变大。
③要求方法不能抛出更大的异常(异常机制)
3.访问控制
①访问控制符
注意:
• public修饰的成员可以在任意位置使用。
• private修饰的成员只能在本类内部使用。
• 通常情况下,成员方法都使用public关键字修饰,成员变量都使用private关键字修饰。
4.final关键字
①基本概念
• final本意为"最终的、不可改变的",可以修饰类、成员方法以及成员变量。
②使用方式
• final关键字修饰类体现在该类不能被继承。
主要用于防止滥用继承,如:java.lang.String类等。
• final关键字修饰成员方法体现在该方法不能被重写但可以被继承。
主要用于防止不经意间造成重写,如:java.text.Dateformat类中format方法等。
• final关键字修饰成员变量体现在该变量必须初始化且不能改变。
主要用于防止不经意间造成改变,如:java.lang.Thread类中MAX_PRIORITY等。
③常量的概念
• 在以后的开发中很少单独使用final关键字来修饰成员变量,通常使用public static final关键字共同修饰成员变量来表达常量的含义,常量的命名规范要求是所有字母都要大写,不同的单词之间采用下划线连。
public static final double PI = 3.14;
九、多态和特殊类
1.多态
①多态的语法格式
父类类型 引用变量名 = new 子类类型();
Person p = new Student();
②多态的特点
①当父类类型的引用指向子类类型的对象时,父类类型的引用可以直接调 用父类独有的方法;
②当父类类型的引用指向子类类型的对象时,父类类型的引用不可以直接 调用子类独有的方法;
③对于父子类都有的非静态方法来说,编译阶段调用父类版本,运行阶段调用子类重写的版本(动态绑定);
④对于父子类都有的静态方法来说,编译和运行阶段都调用父类版本.
③引用数据类型之间的转换
• 引用数据类型之间的转换方式有两种:自动类型转换 和 强制类型转换。
• 自动类型转换主要指小类型向大类型的转换,也就是子类转为父类,也叫做向上转型。
• 强制类型转换主要指大类型向小类型的转换,也就是父类转为子类,也叫做向下转型或显式类型转换。
• 引用数据类型之间的转换必须发生在父子类之间,否则编译报错。
• 若强转的目标类型并不是该引用真正指向的数据类型时则编译通过,运行阶段发生类型转换异常。
• 为了避免上述错误的发生,应该在强转之前进行判断,格式如下:
if(引用变量 instanceof 数据类型)
判断引用变量指向的对象是否为后面的数据类型
④多态的实际意义
• 多态的实际意义在于屏蔽不同子类的差异性实现通用的编程带来不同的效果。
⑤多态的使用场合
通过方法的参数传递形成多态;
public static void draw(Shape s){
s.show();
}
draw(new Rect(1, 2, 3, 4));
在方法体中直接使用多态的语法格式
Account acc = new FixedAccount();
通过方法的返回值类型形成多态
Calender getInstance(){
return new GregorianCalendar(zone, aLocale);
}
2.抽象类
①抽象方法的概念
• 抽象方法主要指不能具体实现的方法并且使用abstract关键字修饰,也就是没有方法体。
• 具体格式如下:
访问权限 abstract 返回值类型 方法名(形参列表);
public abstract void cry();
②抽象类的概念
• 抽象类主要指不能具体实例化的类并且使用abstract关键字修饰,也就是不能创建对象。
③抽象类和抽象方法的关系
- 抽象类中可以有成员变量、构造方法、成员方法;
- 抽象类中可以没有抽象方法,也可以有抽象方法;
- 拥有抽象方法的类必须是抽象类,因此真正意义上的抽象类应该是具有抽象方法并且使用abstract关键字修饰的类。
④抽象类的实际意义
• 抽象类的实际意义不在于创建对象而在于被继承。
• 当一个类继承抽象类后必须重写抽象方法,否则该类也变成抽象类,也就是抽象类对子类具有强制性和规范性,因此叫做模板设计模式。
3.接口
①接口的基本概念
• 接口就是一种比抽象类还抽象的类,体现在所有方法都为抽象方法。
• 定义类的关键字是class,而定义接口的关键字是interface。
②类和接口之间的关系
③抽象类和接口的主要区别
- 定义抽象类的关键字是abstract class,而定义接口的关键字是interface。
- 继承抽象类的关键字是extends,而实现接口的关键字是implements。
- 继承抽象类支持单继承,而实现接口支持多实现。
- 抽象类中可以有构造方法,而接口中不可以有构造方法。
- 抽象类中可以有成员变量,而接口中只可以有常量。
- 抽象类中可以有成员方法,而接口中只可以有抽象方法。
- 抽象类中增加方法时子类可以不用重写,而接口中增加方法时实现类需要重写(Java8以前的版本)。
- 从Java8开始增加新特性,接口中允许出现非抽象方法和静态方法,但非抽象方法需要使用default关键字修饰。
- 从Java9开始增加新特性,接口中允许出现私有方法。
十、特殊类
1.内部类
①内部类的基本概念
• 当一个类的定义出现在另外一个类的类体中时,那么这个类叫做内部类(Inner),而这个内部类所在的类叫做外部类(Outer)。
• 类中的内容:成员变量、成员方法、构造方法、静态成员、构造块和静态代码块、内部类。
②实际作用
• 当一个类存在的价值仅仅是为某一个类单独服务时,那么就可以将这个类定义为所服务类中的内部类,这样可以隐藏该类的实现细节并且可以方便的访问外部类的私有成员而不再需要提供公有的get和set方法。
③内部类的分类
• 普通内部类 ---- 直接将一个类的定义放在另外一个类的类体中。
• 静态内部类 ---- 使用static关键字修饰的内部类,隶属于类层级。
• 局部内部类 ---- 直接将一个类的定义放在方法体的内部时。
• 匿名内部类 ---- 就是指没有名字的内部类。
④普通(成员)内部类的格式
//普通(成员)内部类的格式:
访问修饰符 class 外部类的类名 {
访问修饰符 class 内部类的类名 {
内部类的类体;
}
}
⑤普通内部类的使用方式
- 普通内部类和普通类一样可以定义成员变量、成员方法以及构造方法等。
- 普通内部类和普通类一样可以使用final或者abstract关键字修饰。
- 普通内部类还可以使用private或protected关键字进行修饰。
- 普通内部类需要使用外部类对象来创建对象。
- 如果内部类访问外部类中与本类内部同名的成员变量或方法时,需要使用this关键字。
⑥静态内部类的格式
//静态内部类的格式:
访问修饰符 class 外部类的类名 {
访问修饰符 static class 内部类的类名 {
内部类的类体;
}
}
⑦静态内部类的使用方式
- 静态内部类不能直接访问外部类的非静态成员。
- 静态内部类可以直接创建对象。
- 如果静态内部类访问外部类中与本类内同名的成员变量或方法时,需要使用类名.的方式访问。
⑧局部(方法)内部类的格式
//局部(方法)内部类的格式:
访问修饰符 class 外部类的类名 {
访问修饰符 返回值类型 成员方法名(形参列表) {
class 内部类的类名 {
内部类的类体;
}
}
}
⑨局部内部类的使用方式
- 局部内部类只能在该方法的内部可以使用。
- 局部内部类可以在方法体内部直接创建对象。
- 局部内部类不能使用访问控制符和static关键字修饰符。
- 局部内部类可以使用外部方法的局部变量,但是必须是final的。由局部内部类和局部变量的声明周期不同所致。
⑩回调模式的概念
• 回调模式是指——如果一个方法的参数是接口类型,则在调用该方法时,需要创建并传递一个实现此接口类型的对象;而该方法在运行时会调用到参数对象中所实现的方法(接口中定义的)。
⑪开发经验分享
• 当接口/类类型的引用作为方法的形参时,实参的传递方式有两种:
自定义类实现接口/继承类并重写方法,然后创建该类对象作为实参传递;
使用上述匿名内部类的语法格式得到接口/类类型的引用即可;
⑫匿名内部类的语法格式
接口/父类类型 引用变量名 = new 接口/父类类型() { 方法的重写 };
2.枚举
①枚举的定义
• 使用public static final表示的常量描述较为繁琐,使用enum关键字来定义枚举类型取代常量,枚举类型是从Java5开始增加的一种引用数据类型。
• 枚举值就是当前类的类型,也就是指向本类的对象,默认使用public static final关键字共同修饰,因此采用枚举类型.的方式调用。
• 枚举类可以自定义构造方法,但是构造方法的修饰符必须是private,默认也是私有的
②Enum类的概念和方法
所有的枚举类都继承自java.lang.Enum类,常用方法如下:
③枚举类实现接口的方式
• 枚举类实现接口后需要重写抽象方法,而重写方法的方式有两种:重写一个,或者每个对象都重写。
public interface DirectionInterface {
// 自定义抽象方法
public abstract void show();
}
public enum DirectionEnum implements DirectionInterface {
// 2.声明本类类型的引用指向本类类型的对象
// 匿名内部类的语法格式:接口/父类类型 引用变量名 = new 接口/父类类型() { 方法的重写 };
// public static final Direction UP = new Direction("向上") { 方法的重写 };
UP("向上") {
@Override
public void show() {
System.out.println("贪吃蛇向上移动了一下!");
}
}, DOWN("向下") {
@Override
public void show() {
System.out.println("贪吃蛇向下移动了一下!");
}
}, LEFT("向左") {
@Override
public void show() {
System.out.println("左移了一下!");
}
}, RIGHT("向右") {
@Override
public void show() {
System.out.println("右移了一下!");
}
};
private final String desc; // 用于描述方向字符串的成员变量
// 通过构造方法实现成员变量的初始化,更加灵活
// 1.私有化构造方法,此时该构造方法只能在本类的内部使用
private DirectionEnum(String desc) { this.desc = desc; }
// 通过公有的get方法可以在本类的外部访问该类成员变量的数值
public String getDesc() {
return desc;
}
// 整个枚举类型只重写一次,所有对象调用同一个
/*@Override
public void show() {
System.out.println("现在可以实现接口中抽象方法的重写了!");
}*/
}
public class DirectionEnumTest {
public static void main(String[] args) {
// 1.获取DirectionEnum类型中所有的枚举对象
DirectionEnum[] arr = DirectionEnum.values();
// 2.打印每个枚举对象在枚举类型中的名称和索引位置
for (int i = 0; i < arr.length; i++) {
System.out.println("获取到的枚举对象名称是:" + arr[i].toString());
System.out.println("获取到的枚举对象对应的索引位置是:" + arr[i].ordinal()); // 和数组一样下标从0开始
}
System.out.println("---------------------------------------------------------------");
// 3.根据参数指定的字符串得到枚举类型的对象,也就是将字符串转换为对象
//DirectionEnum de = DirectionEnum.valueOf("向下"); // 编译ok,运行发生IllegalArgumentException非法参数异常
DirectionEnum de = DirectionEnum.valueOf("DOWN");
//DirectionEnum de = DirectionEnum.valueOf("UP LEFT"); // 要求字符串名称必须在枚举对象中存在
//System.out.println("转换出来的枚举对象名称是:" + de.toString());
System.out.println("转换出来的枚举对象名称是:" + de); // 当打印引用变量时,会自动调用toString方法
System.out.println("---------------------------------------------------------------");
// 4.使用获取到的枚举对象与枚举类中已有的对象比较先后顺序
for(int i = 0; i < arr.length; i++) {
// 当调用对象在参数对象之后时,获取到的比较结果为 正数
// 当调用对象在参数对象相同位置时,则获取到的比较结果为 零
// 当调用对象在参数对象之前时,则获取到的比较结果为 负数
System.out.println("调用对象与数组中对象比较的先后顺序结果是:" + de.compareTo(arr[i]));
}
System.out.println("---------------------------------------------------------------");
// 5.使用数组中每个DirectionEnum对象都去调用show方法测试
for (int i = 0; i < arr.length; i++) {
arr[i].show();
}
}
}
3.注解
①注解的基本概念
• 注解(Annotation)又叫标注,是从Java5开始增加的一种引用数据类型。
• 注解本质上就是代码中的特殊标记,通过这些标记可以在编译、类加载、以及运行时执行指定的处理。
②注解的语法格式
访问修饰符 @interface 注解名称 {
注解成员;
}
• 自定义注解自动继承java.lang.annotation.Annotation接口。
• 通过@注解名称的方式可以修饰包、类、 成员方法、成员变量、构造方法、参数、局部变量的声明等。
③注解的使用方式
• 注解体中只有成员变量没有成员方法,而注解的成员变量以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。
• 如果注解只有一个参数成员,建议使用参数名为value,而类型只能是八种基本数据类型、String类型、Class类型、enum类型及Annotation类型
④元注解的概念
• 元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面。
• 元注解主要有 @Retention、@Documented、@Target、@Inherited、
@Repeatable。
a.元注解@Retention
• @Retention 应用到一个注解上用于说明该注解的的生命周期,取值如下:
- RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
- RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中,默认方式。
- RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们
//@Retention(RetentionPolicy.SOURCE) // 表示下面的注解在源代码中有效
//@Retention(RetentionPolicy.CLASS) // 表示下面的注解在字节码文件中有效,默认方式
@Retention(RetentionPolicy.RUNTIME) // 表示下面的注解在运行时有效
@Documented // 表示下面的注解信息可以被javadoc工具提取到API文档中,很少使用
// 表示下面的注解可以用于类型、构造方法、成员变量、成员方法、参数 的修饰
@Target({ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Inherited // 表示下面的注解所修饰的类中的注解使用可以被子类继承
// 若一个注解中没有任何的成员,则这样的注解叫做标记注解/标识注解
public @interface MyAnnotation {
//public Direction value(); // 声明一个String类型的成员变量,名字为value 类型有要求
public String value() default "123"; // 声明一个String类型的成员变量,名字为value
public String value2();
}
b.元注解@Documented
• 使用javadoc工具可以从程序源代码中抽取类、方法、成员等注释形成一个和源代码配套的API帮助文档,而该工具抽取时默认不包括注解内容。
• @Documented用于指定被该注解将被javadoc工具提取成文档。
• 定义为@Documented的注解必须设置Retention值为RUNTIME。
c.元注解@Target
• @Target用于指定被修饰的注解能用于哪些元素的修饰,取值如下
@Repeatable(value = ManTypes.class)
@Target(ElementType.TYPE_USE)
public @interface ManType {
String value() default "";
}
@Target(ElementType.TYPE_USE)
public @interface ManTypes {
ManType[] value();
}
@ManType(value = "职工")
@ManType(value = "超人")
//@ManTypes({@ManType(value = "职工"), @ManType(value = "超人")}) // 在Java8以前处理多个注解的方式
public class Man {
@Deprecated // 表示该方法已经过时,不建议使用
public void show() {
System.out.println("这个方法马上过时了!");
}
public static void main(String[] args) {
int ia = 97;
char c1 = (@ManType char) ia;
}
}
public class ManTest {
public static void main(String[] args) {
Man man = new Man();
man.show();
}
}
d.元注解@Inherited
• @Inherited并不是说注解本身可以继承,而是说如果一个超类被该注解标记过的注解进行注解时,如果子类没有被任何注解应用时,则子类就继承超类的注解。
d.元注解@Repeatable
• @Repeatable表示自然可重复的含义,从Java8开始增加的新特性。
• 从Java8开始对元注解@Target的参数类型ElementType枚举值增加了两个:
• 其中ElementType.TYPE_PARAMETER 表示该注解能写在类型变量的声明语句中,如:泛型。
• 其中ElementType.TYPE_USE 表示该注解能写在使用类型的任何语句中。
e.常见的预制注解
• 预制注解就是Java语言自身提供的注解,具体如下:
十一、Java新特性
1.Java8的新特性
①函数式接口
● 函数式接口主要指只包含一个抽象方法的接口,如:java.lang.Runnable、java.util.Comparator接口等。
● Java8提供@FunctionalInterface注解来定义函数式接口,若定义的接口不符合函数式的规范便会报错。
● Java8中增加了java.util.function包,该包包含了常用的函数式接口,具体如下:
②Lambda表达式
● Lambda 表达式是实例化函数式接口的重要方式,使用 Lambda 表达式可以使代码变的更加简洁紧凑。
● Lambda表达式:参数列表、箭头符号->和方法体组成,而方法体中可以是表达式,也可以是语句块。
● 语法格式:(参数列表) -> { 方法体; } - 其中()、参数类型、{ } 以及return关键字 可以省略。
③方法引用
● 方法引用主要指通过方法的名字来指向一个方法而不需要为方法引用提供方法体,该方法的调用交给函数式接口执行。
● 方法引用使用一对冒号 :: 将类或对象与方法名进行连接,通常使用方式如下:
对象的非静态方法引用 ObjectName :: MethodName
类的静态方法引用 ClassName :: StaticMethodName
类的非静态方法引用 ClassName :: MethodName
构造器的引用 ClassName :: new
数组的引用 TypeName[] :: new
● 方法引用是在特定场景下lambda表达式的一种简化表示,可以进一步简化代码的编写使代码更加紧凑简洁,从而减少冗余代码。
④Stream接口
a.概念
● java.util.stream.Stream接口是对集合功能的增强,可以对集合元素进行复杂的查找、过滤、筛选等操作。
● Stream接口借助于Lambda 表达式极大的提高编程效率和程序可读性,同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势。
b.使用步骤
①创建Stream,通过一个数据源来获取一个流。
②转换Stream,每次转换返回一个新的Stream对象。
③对Stream进行聚合操作并产生结果。
c.创建方式
方式一:通过调用集合的默认方法来获取流,如:default Stream stream()
方式二:通过数组工具类中的静态方法来获取流,如:static IntStream stream(int[] array)
方式三:通过Stream接口的静态方法来获取流,如:static Stream of(T… values)
方式四:通过Stream接口的静态方法来获取流,static Stream generate(Supplier<? extends T>s)
d.中间操作
筛选与切片的常用方法
映射的常用方法
排序的常用方法
e.终止操作
匹配与查找的常用方法
规约的常用方法
收集的常用方法
⑤Optional类
● java.util.Optional类可以理解为一个简单的容器,其值可能是null或者不是null,代表一个值存在或不存在。
● 该类的引入很好的解决空指针异常,不用显式进行空值检测。
常用方法
2.Java9的新特性
①模块化的使用
a.语法格式
在 module-info.java 文件中,我们可以用新的关键词module来声明一个模块,具体如下:
module 模块名称 {
}
b.模块化的优势
①减少内存的开销。
②可简化各种类库和大型应用的 开发和维护。
③安全性,可维护性,提高性能。
c.钻石操作符的使用升级
在Java9中允许在匿名内部类的使用中使用钻石操作符。
②集合工厂方法
a.概念
● Java9的List、Set和Map集合中增加了静态工厂方法of实现不可变实例的创建。
● 不可变体现在无法添加、修改和删除它们的元素。
● 不允许添加null元素对象。
b.实际意义
● 保证线程安全:在并发程序中既保证线程安全性,也大大增强了并发时的效率。
● 被不可信的类库使用时会很安全。
● 如果一个对象不需要支持修改操作,将会节省空间和时间的开销。
● 可以当作一个常量来对待,并且这个对象在以后也不会被改变。
③InputStream的增强
InputStream类中提供了transferTo方法实现将数据直接传输到OutputStream中。
3.Java10的新特性
局部变量类型推断
(1)基本概念
Java10可以使用var作为局部变量类型推断标识符,此符号仅适用于局部变量,增强for循环的索引,以及传统for循环的本地变量。它不能使用于方法形式参数,构造函数形式参数,方法返回类型,字段,catch形式参数或任何其他类型的变量声明。
(2)实际意义
● 标识符var不是关键字,只是一个保留的类型名称。这意味着var用作变量,方法名或包名的代码不会受到影响,但var不能作为类或则接口的名字。
● 避免了信息冗余。
● 对齐了变量名。
● 更容易阅读。
4.Java11的新特性
1.简化的编译运行操作
● 在Java11中可以使用java命令一次性进行编译和运行操作。
● 执行源文件中的第一个类必须包含主方法。
● 不可以使用其它源文件中自定义的类。
2.String类新增方法
Java核心类库请看:
https://blog.csdn.net/qq_50048558/article/details/119631209?spm=1001.2014.3001.5502