注:此篇文章内容为强化Java语法核心及遗漏知识点而编写 将java知识点串联,覆盖基础重要知识点 适合基础学习及重点梳理
个人整理,欢迎批评指正。
数据类型及变量
字面常量的分类:
1.字符串常量:由" "括起来的
2.整形常量:程序中直接写的数字(无小数点)
3.浮点数常量:程序中直接写的小数
4.字符常量:由单引号括起来的单个字符
5.布尔常量:只有两种true和false
6.空常量:null
数据类型
分为基本数据类型 and 引用数据类型
基本数据类型分四类——整型、浮点型、字符型以及布尔型(四类亦可继续分为八种)

本人疏漏的知识点:
整型默认为int型,浮点型默认为double
int占4字节,long占8字节
8 个二进制位(bit) 为一个字节(Byte)
数据类型有什么作用呢? →定义不同种类的变量
定义变量的语法格式为:
数据类型 变量名 = 初始值;
注:在给变量设置初始值时,值不能超过int的表示范围,否则会导致溢出
变量在使用之前必须要赋初值,否则编译报错
其中 字符型变量以下详细撰写部分
注:
1. Java 中使用 单引号 + 单个字母 的形式表示字符字面值
2. 计算机中的字符本质上是一个整数. 在 C 语言中使用 ASCII 表示字符, 而 Java 中使用 Unicode 表示字符. 因此 一个字符占用两个字节, 表示的字符种类更多, 包括中文
字符编码方式的讨论帖 参见 https://zhuanlan.zhihu.com/p/35172335
Java 的 boolean 类型和 int 不能相互转换, 不存在 1 表示 true, 0 表示 false 这样的用法
Java中,当参与运算数据类型不一致时,就会进行类型转换。Java中类型转换主要分为两类:自动类型转换(隐式) ——(数据范围小的转为数据范围大的时会自动进行)和 强制类型转换(显式)。——(可能精度丢失)
小案例
byte a = 10;
int b = 128;
a = (byte)b;
步骤 1:先看 byte 的存储规则
byte 是 8 位有符号整数,最高位是符号位:
0 表示正数,1 表示负数
取值范围:-128 到 127(因为 8 位最多表示 2⁸=256 个数)
步骤 2:把 128 转成 8 位二进制
128 的十进制转二进制是 10000000(刚好 8 位)
步骤 3:用补码规则解析这个 8 位二进制
对于有符号数,8 位二进制 10000000 的含义是:
最高位 1 表示负数
补码的计算规则是:负数的补码 = 对应正数的原码取反 + 1
反过来,要得到这个负数的十进制,需要:补码 - 1 再取反,最后加负号
计算:10000000 - 1 = 01111111 → 取反 = 10000000(十进制 128)→ 加负号就是 -128
运算符
Java中运算符可分为:算术运算符(+ - * /)、关系运算符(< > ==)、逻辑运算符、位运算符、移位运算符以及条件运算符等。
关系运算符——当需要多次判断时,不能连着写
逻辑运算符(&& || !)
逻辑与 &&
重点:两个表达式都为真,结果才是真,只要有一个是假,结果就是假
逻辑 ||
相当于现实生活中的或
重点:左右表达式至少有一个位真,则结果为真
逻辑非 !
重点:真变假,假变真。
短路求值
System.out.println(10 > 20 && 10 / 0 == 0); // 打印 false
System.out.println(10 < 20 || 10 / 0 == 0); // 打印 true
对于 && , 如果左侧表达式值为 false, 则表达式结果一定是 false, 无需计算右侧表达式
对于 ||, 如果左侧表达式值为 true, 则表达式结果一定是 true, 无需计算右侧表达式
位运算符
Java 中数据存储的最小单位是字节,而数据操作的最小单位是比特位. 字节是最小的存储单位,每个字节是由8个二进制比特位组成的
位运算符主要有四个: & | ~ ^,除 ~ 是一元运算符外,其余都是二元运算符。
位操作——按二进制位运算
1. 按位与 &: 有0则为0

2. 按位或 |: 有1则为1S

3. 按位取反 ~: 如果该位为 0 则转为 1, 如果该位为 1 则转为 0
4. 按位异或 ^:相同为0,相异为1
移位运算
移位运算符有三个:> >>>,都是二元运算符,都是按照二进制比特位来运算
左移 <<: 最左侧位不要了, 最右侧补 0
右移 >>: 最右侧位不要了, 最左侧补符号位(正数补0, 负数补1)
无符号右移 >>>: 最右侧位不要了, 最左侧补 0.
条件运算符
表达式1 ? 表达式2 : 表达式3
理解:如果表达式1为真,返回表达式2的值,否则返回表达式3的值
表达式不能单独存在,其产生的结果必须要被使用
注意:运算符之间是有优先级的,在可能存在歧义的代码中加上括号。
程序逻辑控制
- 顺序执行
- 选择执行 if switch
- 循环执行 for while do while
if 语句
f(布尔表达式){
// 语句
}
if(布尔表达式){ // 语句1
}else{
// 语句2
}
if(布尔表达式1){
// 语句1
}else if(布尔表达式2){
// 语句2
}else{
// 语句3
}
switch 语句
switch(表达式){
case 常量值1:{
语句1;
[break;]
}
case 常量值2:{
语句2;
[break;]
}
...
default:{
内容都不满足时执行语句;
[break;]
}
}
执行流程:
1. 先计算表达式的值
2. 和case依次比较,一旦有响应的匹配就执行该项下的语句,直到遇到break时结束
3. 当表达式的值没有与所列项匹配时,执行default
switch的括号内只能是以下类型的表达式:
基本类型:byte、char、short、int,注意不能是long类型(float long boolean double类型)
引用类型:String常量串、枚举类型
循环结构
while 循环
while(循环条件){
循环语句;
}
例:计算 1 - 100 的和

break
break 的功能是让循环提前结束.
代码示例: 找到 100 - 200 中第一个 3 的倍数
int num = 100;
while (num <= 200) {
if (num % 3 == 0) {
System.out.println("找到了 3 的倍数, 为:" + num);
break;
}
num++;
}
continue
continue 的功能是跳过这次循环, 立即进入下次循环
代码示例: 找到 100 - 200 中所有 3 的倍数
int num = 100;
while (num <= 200) {
if (num % 3 != 0) {
num++; // 这里的 ++ 不要忘记! 否则会死循环.
continue;
}
System.out.println("找到了 3 的倍数, 为:" + num);
num++;
}
执行到 continue 语句的时候, 就会立刻进入下次循环(判定循环条件), 从而不会执行到下方的打印语句.
for 循环
for(表达式①;布尔表达式②;表达式③){
表达式④;
}
- 表达式1: 用于初始化循环变量初始值设置,在循环最开始时执行,且只执行一次
- 表达式2: 循环条件,满则循环继续,否则循环结束
- 表达式3: 循环变量更新方式
输出到控制台
println 输出的内容自带 \n, print不带 \n
从键盘输入
使用 Scanner 读取字符串/整数/浮点数
import java.util.Scanner; // 需要导入 util 包
方法的使用
What is a method?
方法就是一个代码片段. 类似于 C 语言中的 "函数"
- 模块化的组织代码
- 重复使用
- 更好理解更简单.
- 可直接调用
示例一:实现一个函数,检测一个年份是否为闰年

【注意事项】
在java当中,方法必须写在类当中
在java当中,方法不能嵌套定义
在java当中,没有方法声明一说
方法调用的执行过程
调用方法--->传递参数--->找到方法地址--->执行被调方法的方法体--->被调方法结束返回--->回到主调方法继续往下执行
实参和形参的关系
形参的改变 不影响实参的值
形参只是方法在定义时需要借助的一个变量,用来保存方法在调用时传递过来的值。
注:在Java中,实参的值永远都是拷贝到形参中,形参和实参本质是两个实体
没有返回值的方法
没有时返回值类型必须写成void
方法重载
在Java中,如果多个方法的名字相同,参数列表不同,则称该几种方法被重载了。
注意:
1. 方法名必须相同
2. 参数列表必须不同(参数的个数不同、参数的类型不同、类型的次序必须不同)
3. 与返回值类型是否相同无关
递归
将原问题拆分成其子问题之后,子问题与原问题有相同的解法,等子问题解决之后,原问题就迎刃而解了。
数组的定义与使用
数组:可以看成是 相同类型元素的一个集合。在内存中是一段连续的空间
T[] 数组名 = new T[N];
T:表示数组中存放元素的类型
T[]:表示数组的类型
N:表示数组的长度
数组的初始化
动态初始化:在创建数组时,直接指定数组中元素的个数
int[] array = new int[10];
静态初始化:在创建数组时不直接指定数据元素个数,而直接将具体的数据内容进行指定
T[] 数组名称 = {data1, data2, data3, ..., datan};
静态初始化tips
- 编译器在编译时会根据{}中元素个数来确定数组的长度。
- {}中数据类型必须与[]前数据类型一致。
- 可以简写,省去后面的new T[]
tips
1、数组是一段连续的内存空间,因此支持随机访问,即通过下标访问快速访问数组中任意位置的元素
2、N为元素个数,不能越界,否则会报出下标越界异常
java.lang.ArrayIndexOutOfBoundsException 异常.
使用数组一定要下标谨防越界
遍历数组
"遍历" 是指将数组中的所有元素都访问一遍, 访问是指对数组中的元素进行某种操作
循环
int[]array = new int[]{10, 20, 30, 40, 50};
for(int i = 0; i < 5; i++){
System.out.println(array[i]);
}
在数组中可以通过 数组对象.length来获取数组的长度
int[]array = new int[]{10, 20, 30, 40, 50};
for(int i = 0; i < array.length; i++){
System.out.println(array[i]);
}
for-each 遍历数组 遍历array数组 值放在x
int[] array = {1, 2, 3};
for (int x : array) {
System.out.println(x);
}
初始JVM的内存分布

基本类型变量与引用类型变量的区别
基本数据类型创建的变量,称为基本变量,该变量空间中直接存放的是其所对应的值;
而引用数据类型创建的变量,一般称为对象的引用,其空间中存储的是对象所在空间的地址。
public static void func() {
int a = 10;
int b = 20;
int[] arr = new int[]{1,2,3};
}
a、b是内置类型的变量,因此其空间中保存的就是给该变量初始化的值。
array是数组类型的引用变量,其内部保存的内容可以简单理解成是数组在堆空间中的首地址
引用变量并不直接存储对象本身,可以简单理解成存储的是对象在堆中空间的起始地址。通过该 地址,引用变量便可以去操作对象
null

数组是引用类型,按照引用类型来进行传递,是可以修改其中存放的内容的
总结: 所谓的 "引用" 本质上只是存了一个地址. Java 将数组设定成引用类型, 这样的话后续进行数组参数传参, 其实 只是将数组的地址传入到函数形参中. 这样可以避免对整个数组的拷贝(拷贝开销就会很大)
类和对象
何为面向对象
面向对象是解决问题的一种思想,主要依靠对象之间的交互完成一件事情
面向对象与面向过程
面向对象编程
1 找对象
2 创建对象
3 使用对象
简单认识类
类是用来对一个实体(对象)来进行描述的
类的定义格式
在java中定义类时需要用到class关键字
注意事项
- 类名注意采用大驼峰定义
- 成员前写法统一为public,后面会详细解释
- 此处写的方法不带 static 关键字. 后面会详细解释
类的实例化
定义了一个类,就相当于在计算机中定义了一种新的类型
用类类型创建对象的过程,称为类的实例化,在java中采用new关键字,配合类名来实例化对象
注意事项
- new 关键字用于创建一个对象的实例.
- 使用 . 来访问对象中的属性和方法.
- 同一个类可以创建对个实例.
类和对象的说明
1. 类只是一个模型一样的东西,用来对一个实体进行描述,限定了类有哪些成员.
2. 类是一种自定义的类型,可以用来定义变量.
3. 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量
4. 类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图
this引用
this引用指向当前对象(成员方法运行时调用该成员方法的对象),在成员方法中所有成员变量的操作,都是通过该 引用去访问
public class Date {
public int year;
public int month;
public int day;
public void setDay(int year, int month, int day){
this.year = year;
this.month = month;
this.day = day;
}
public void printDate(){
System.out.println(this.year + "/" + this.month + "/" + this.day);
}
}
注意:this引用的是调用成员方法的对象。
this引用的特性
1. this的类型:对应类类型引用,即哪个对象调用就是哪个对象的引用类型
2. this只能在"成员方法"中使用------不能在静态方法情况中使用
3. 在"成员方法"中,this只能引用当前对象,不能再引用其他对象
4. this是“成员方法”第一个隐藏的参数,编译器会自动传递,在成员方法执行时,编译器会负责将调用成员方法对象的引用传递给该成员方法,this负责来接收
this的用法有3种:
1.可以通过this访问 当前对象的成员变量
2.可以通过this访问 当前对象的非静态的成员方法
3.可以通过this访问 当前对象的其他构造方法

对象的构造及初始化
在Java方法内部定义一个局部变量时,必须要初始化


一个对象的生成
1.为对象分配内存
2.调用合适的构造方法(名字必须与类名相同,在创建对象时,由编译器自动调用,并且 在整个对象的生命周期内只调用一次)
特性
1.名字与类名相同
2.无返回值类型
3.在对象的生命周期内只能调用一次
4.构造方法可以重载
5.没有定义任何构造方法,编译器会默认生成一个不带参数的构造方法
6.构造方法中,可以通过this调用其他构造方法来简化代码
默认初始化
Date d = new Date(2021,6,9)
从JVM层面来解析下
1. 检测对象对应的类是否加载了,如果没有加载则加载
2. 为对象分配内存空间
3. 处理并发安全问题
比如:多个线程同时申请对象,JVM要保证给对象分配的空间不冲突
4. 初始化所分配的空间

5. 设置对象头信息
6. 调用构造方法,给对象中各个成员赋值
封装
简单来说——就是套壳屏蔽细节
面向对象程序三大特性:封装、继承、多态
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互
访问限定符
类可以将数据以及封装数据的方法结合在一起,访问权限用来控制方法或者字段能否直接在类外使用
Java中提供了四种访问限定符:

封装扩展之包
为了更好的管理类,把多个类收集在一起成为一组,称为软件包。有点类似于目录
包是对类、接口等的封装机制的体现。
包的重要的作用:在同一个工程中允许存在相同名称的类,只要处在 不同的包中即可。
导入包中的类
以使用 java.util.Date 导入 java.util 这个包中的 Date 类.
亦可以使用 import语句导入包(推荐)
import java.util.Date;
使用 java.util 中的其他类, 可以使用 import java.util.*
import java.util.*;
建议显式的指定要导入的类名. 否则还是容易出现冲突的情况.
自定义包
在文件的最上方加上一个 package 语句指定该代码在哪个包中
包名需要尽量指定成唯一的名字
包名要和代码路径相匹配
如果一个类没有 package 语句, 则该类被放到一个默认包中.
static成员
在Java中,被static修饰的成员,称之为静态成员,也可以称为类成员,其不属于某个具体的对 象,是所有对象所共享的。
static修饰成员变量
静态成员变量特性
- 所有对象共享,不存储在某个对象的空间中
- 既可以通过对象访问,也可以通过类名访问
- 类变量存储在方法区当中
随类的加载而创建,随类的卸载而销毁
静态方法特性
- 不属于某个具体的对象,是类方法
- 可以通过对象调用,也可以通过类名.静态方法名(...)方式调用
- 不能在静态方法中访问任何非静态成员变量
- 静态方法中不能调用任何非静态方法——因为非静态方法有this参数
static成员变量初始化
static中 不能出现this关键字

静态成员变量的初始化分为两种:就地初始化 和 静态代码块初始化
前者指:在定义时直接给出初始值
后者 先介绍代码块
代码块
使用{}定义的一段代码称为代码块
普通代码块:定义在方法中的代码块.
构造块:定义在类中的代码块——实例代码块。构造代码块一般用于初始化实例成员变量
静态代码块:使用static定义的代码块。一般用于初始化静态成员变量。

注意
- 静态代码块不管生成多少个对象,其只会执行一次
- 静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的
- 如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行(合并)
- 实例代码块只有在创建对象时才会执行
内部类
在 Java 中,可以将一个类定义在另一个类或者一个方法的内部, 前者称为内部类,后者称为外部类。内部类也是封装的一种体现。
注:
- 定义在class 类名{}花括号外部的,即使是在一个文件里,都不能称为内部类
- 内部类和外部类共用同一个java源文件,但是经过编译之后,内部类会形成单独的字节码文件
内部类的分类
根据内部类定义的位置不同
public class OutClass {
// 成员位置定义:未被static修饰 --->实例内部类
public class InnerClass1{
}
// 成员位置定义:被static修饰 ---> 静态内部类
static class InnerClass2{
}
}
实例内部类的访问
要访问实例内部类中成员,必须要创建实例内部类的对象
而普通内部类定义与外部类成员定义位置相同,因此创建实例内部类对象时必须借助外部类
注意
- 外部类中的任何成员都可以在实例内部类方法中直接访问
- 实例内部类所处的位置与外部类成员位置相同,因此也受public、private等访问限定符的约束
- 在实例内部类方法中访问同名的成员时,优先访问自己的,如果要访问外部类同名的成员,必须:外部类名 称.this.同名成员 来访问
- 实例内部类对象必须在先有外部类对象前提下才能创建
- 实例内部类的非静态方法中包含了一个指向外部类对象的引用
- 外部类中,不能直接访问实例内部类中的成员,如果要访问必须先要创建内部类的对象。
注意
- 在静态内部类中只能访问外部类中的静态成员
- 创建静态内部类对象时,不需要先创建外部类对象
继承和多态
面向对象思想中提出了继承的概念,专门用来进行共性抽取,实现代码复用。
继承(inheritance)机制:允许程序员在保持原有类特性的基础上进行扩展,增加新功能,这样产生新的类,称派生类

上述图示中,Dog和Cat都继承了Animal类,其中:Animal类称为父类/基类或超类,Dog和Cat可以称为Animal的 子类/派生类
继承的语法
需要借助extends关键字
修饰符 class 子类 extends 父类 {
// ...
}
注意:
1. 子类会将父类中的成员变量或者成员方法继承到子类中了
2. 子类继承父类之后,必须要新添加自己特有的成员,体现出与基类的不同,否则就没有必要继承了
子类中访问父类的成员变量
1. 子类和父类不存在同名成员变量
2. 子类和父类成员变量同名
在子类方法中 或者 通过子类对象访问成员时:
- 如果访问的成员变量子类中有,优先访问自己的成员变量。
- 如无,则访问父类继承下来的,如果父类也没有定义,则编译报错。
- 如果访问的成员变量与父类中成员变量同名,则优先访问自己的。
总结为一句话——成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找。
- 通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到则访问,否则编译报错。
- 通过派生类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用方法适传递的参数选择合适的方法访问,如果没有则报错;
super关键字
Java提供了super关键字,该关键字主要作用:在子类方法中访问父类的成员。
注:
- 只能在非静态方法中使用
- 在子类方法中,访问父类的成员变量和方法。
子类对象中成员是有两部分组成的,基类继承下来的以及子类新增加的部分 。在构造子类对象时候 ,先要调用基类的构造方法,将从基类继承下来的成员构造完整,然后再调用子类自己的构造方法,将子类自己新增加的成员初始化完整 。
super和this
【相同点】
- 关键字
- 只能在类的非静态方法中使用,用来访问非静态成员方法和字段
- 在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在
【不同点】
- this是当前对象的引用,当前对象即调用实例方法的对象,super相当于是子类对象中从父类继承下来部分成员的引用
- 在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性
- 在构造方法中:this(...)用于调用本类构造方法,super(...)用于调用父类构造方法,两种调用不能同时在构造方法中出现
- 构造方法中一定会存在super(...)的调用,用户没有写编译器也会增加,但是this(...)用户不写则没有
(*)继承关系上的执行顺序(简略:静态 先于 实例and构造 父类 先于 子类)
1、父类静态代码块优先于子类静态代码块执行,且是最早执行
2、父类实例代码块和父类构造方法紧接着执行
3、子类的实例代码块和子类构造方法紧接着再执行
4、第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行
注意:Java中不支持多继承 但可以通过接口去实现
final 关键字
1. 修饰变量或字段,表示常量
2. 修饰类:表示此类不能被继承
3. 修饰方法:表示该方法不能被重写
组合
继承表示对象之间是is-a的关系,比如:狗是动物,猫是动物
组合表示对象之间是has-a的关系,比如:汽车和其轮胎、发动机、方向盘、车载系统
能用组合尽量用组合
多态
去完成某个行为,当不同的对象去完成时会产生出不同的状态
多态实现条件
在java中要实现多态,必须要满足如下几个条件:
1. 必须在继承体系下
2. 子类必须要对父类中方法进行重写
3. 通过父类的引用调用重写的方法
多态体现:
在代码运行时,当传递不同类对象时,会调用对应类中的方法
重写(override)
也称为覆盖。重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程 进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写。
子类可以根据需要,定义特定于自己的行为。子类能够根据需要实现父类的方法
*重写的方法, 可以使用 @Override 注解来显式指定,这个注解能帮我们进行一些合法性校验

方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现

向上转移和向下转型

向上转型:实际就是创建一个子类对象,将其当成父类对象来使用。
语法格式:父类类型 对象名 = new 子类类型()
【使用场景】
- 直接赋值
- 方法传参
- 方法返回
向上转型的优点:让代码实现更简单灵活。
向上转型的缺陷:不能调用到子类特有的方法———只能调用父类自己的
向下转型

Java中为了提高向下转型的安全性,引入了 instanceof ,如果该表达式为true,则可以安全转换。
多态的优缺点
使用多态的好处:
- 能够降低代码的 "圈复杂度"(描述一段代码复杂程度的方式), 避免使用大量的 if - else
- 可扩展能力更强
多态缺陷:代码的运行效率降低。
- 属性没有多态性
- 构造方法没有多态性
结论: "用尽量简单的方式使对象进入可工作状态", 尽量不要在构造器中调用方法(如果这个方法被子类重写, 就会触发动态绑定, 但是此时子类对象还没构造完成)
抽象类和接口
何为抽象类?→ 可以继承
如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类
重点
- 抽象方法可以没有具体的实现
- 如果一个类中有抽象方法,那这个类一定是抽象类
- 抽象类和普通类 区别在于 抽象类中可以包含抽象方法
- 不能表示一个确定的对象 不能进行实例化
抽象类语法
一个类如果被 abstract 修饰称为抽象类,抽象类中被abstract 修饰的方法称为抽象方法,抽象方法不用给出具体的实现体。
如果一个普通类继承了这个抽象类 那么这个此时这个普通类 必须重写这个抽象类当中的抽象方法
所以抽象类的出现就是为了被继承
抽象类特性
- 抽象类不能直接实例化对象
- 抽象方法不能是 private 的
- 抽象方法不能被final和static修饰,因为抽象方法要被子类重写
- 抽象类必须被继承,并且继承后子类要重写父类中的抽象方法,否则子类也是抽象类,必须要使用abstract修饰
- 抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类
- 抽象类中可以有构造方法,供子类创建对象时,初始化父类的成员变量
抽象类的作用
抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类. 然后让子类重写抽象类中的抽象方法.使用抽象类相当于多了一重编译器的校验
接口
在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型
接口的语法规则
接口的定义格式与定义类的格式基本相同,将class关键字换成 interface 关键字,就定义了一个接口
public class 类名称 implements 接口名称{
// ...
}
子类和父类之间是extends 继承关系,类与接口之间是 implements 实现关系。
接口特性
- 接口类型是一种引用类型,但是不能直接new接口的对象
- 接口中的方法会被隐式的指定为 public abstract (只能是 public abstract)
- 接口中的方法是不能在接口中实现的
- 重写接口中方法时,不能使用默认的访问权限
- 接口中的变量会被隐式的指定为 public static final 变量
- 接口中不能有静态代码块和构造方法
- 接口编译完成后字节码文件的后缀格式也是.class
- 如果类没有实现接口中的所有的抽象方法,则类必须设置为抽象类
实现多个接口
Java中不支持多继承,但是一个类可以实现多个接口。
用接口可以达到多继承的目的。
Clonable 接口
Object 类中存在一个 clone 方法, 调用这个方法可以创建一个对象的 "拷贝". 但是要想合法调用 clone 方法, 必须要 先实现 Clonable 接口(Java内置), 否则就会抛出 CloneNotSupportedException 异常.
class Animal implements Cloneable
抽象类和接口的区别
核心区别
抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中 不能包含普通方法, 子类必须重写所有的抽象方法

Object类——“所有类的父类”
Object是Java默认提供的一个类。Java里面除了Object类,所有的类都是存在继承关系的。默认会继承Object父类。即所有类的对象都可以使用Object的引用进行接收。
对象比较equals方法
在Java中,==进行比较时:
a.如果==左右两侧是基本类型变量,比较的是变量中值是否相同
b.如果==左右两侧是引用类型变量,比较的是引用变量地址是否相同
c.如果要比较对象中内容,必须重写Object中的equals方法,因为equals方法默认也是按照地址比较的
hashcode方法
hashcode方法源码:
public native int hashCode();
该方法是一个native方法,底层是由C/C++代码写的
也可以重写hashcode()方法
结论:
1、hashcode方法用来确定对象在内存中存储的位置是否相同
2、事实上hashCode() 在散列表中才有用——(获取对象的散列码,进而确定该对象在散列表中的位置)在其它情况下没用。
String 类
字符串构造
public static void main(String[] args) {
// 使用常量串构造
String s1 = "hello bit";
System.out.println(s1);
// 直接newString对象
String s2 = new String("hello bit");
System.out.println(s1);
// 使用字符数组进行构造
char[] array = {'h','e','l','l','o','b','i','t'};
String s3 = new String(array);
System.out.println(s1);
}
注:
- String是引用类型,内部并不存储字符串本身
- 在Java中“”引起来的也是String类型对象
String对象的比较
- ==比较是否引用同一个对象
- boolean equals(Object anObject)方法:按照字典序比较
字典序:字符大小的顺序
结论:只要是自定义的类型,涉及到了大小的比较,目前的一个结论是:一定得实现Comparable接口!
有了接口可以做到标准上的统一
3.int compareTo(String s)方法: 按照字典序进行比较
字符串查找

字符串替换

字符串拆分

字符串截取

其他操作方法
字符串的不可变性
1.String类在设计时就是不可改变的,String类实现描述中已经说明了

String类中的字符实际保存在内部维护的value字符数组中,该图还可以看出:
- String类被final修饰,表明该类不能被继承
- value被final修饰,表明value自身的值不能改变,即不能引用其它字符数组,但是其引用空间中 的内容可以修改。
所有涉及到可能修改字符串内容的操作都是创建一个新对象,改变的是新对象
final修饰类表明该类不想被继承,final修饰引用类型表明该引用变量不能引用其他对象,但是其引用对象中的内容是可以修改的。
字符串修改
注意:尽量避免直接对String类型对象进行修改,因为String类是不能修改的
StringBuilder和StringBuffer
String的内容无法修改,而StringBuilder的内容可修改

String、StringBuffer、StringBuilder的区别
- String的内容不可修改,StringBuffer与StringBuilder的内容可以修改.
- StringBuffer与StringBuilder大部分功能是相似的
- StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作
异常

- Throwable:是异常体系的顶层类,其派生出两个重要的子类, Error 和 Exception
- Error:指的是Java虚拟机无法解决的严重问题,比如:JVM的内部错误、资源耗尽等
- Exception:异常产生后程序员可以通过代码进行处理,使程序继续执行
异常的分类
1. 编译时异常
受检查异常(Checked Exception)
2.运行时异常
非受检查异常(Unchecked Exception)
异常处理主要的5个关键字:throw、try、catch、final、throws
异常的抛出
可以借助throw关键字,抛出一个指定的异常对象
throw new XXXException("异常产生的原因");
注意
1. throw必须写在方法体内部
2. 抛出的对象必须是Exception 或者 Exception 的子类对象
3. 如果抛出的是 RunTimeException 或者 RunTimeException 的子类,则可以不用处理,直接交给JVM来处理
4. 如果抛出的是编译时异常,用户必须处理,否则无法通过编译
5. 异常一旦抛出,其后的代码就不会执行
异常的捕获
异常声明throws
语法格式:
修饰符 返回值类型 方法名(参数列表) throws 异常类型1,异常类型2...{
}
用户不想处理该异常,此时就可以借助throws将异常抛给方法的调用者来处理。即当前方法不处理异常,提醒方法的调用者处理异常
注意
1. throws必须跟在方法的参数列表之后
2. 声明的异常必须是 Exception 或者 Exception 的子类
3. 方法内部如果抛出了多个异常,throws之后必须跟多个异常类型,之间用逗号隔开,如果抛出多个异常类型 具有父子关系,直接声明父类即可。
4. 调用声明抛出异常的方法时,调用者必须对该异常进行处理,或者继续使用throws抛出
try-catch捕获并处理
try{
// 将可能出现异常的代码放在这里
}catch(要捕获的异常类型 e){
// 如果try中的代码抛出异常了,此处catch捕获时异常类型与try中抛出的异常类型一致时,或者是try中抛出异常的基类
时,就会被捕获到
// 对异常就可以正常处理,处理完成后,跳出try-catch结构,继续执行后序代码
}[catch(异常类型 e){
// 对异常进行处理
}finally{
// 此处代码一定会被执行到
}]
// 后序代码
// 当异常被捕获到时,异常就被处理了,这里的后序代码一定会执行
// 如果捕获了,由于捕获时类型不对,那就没有捕获到,这里的代码就不会被执行
注意
1. try块内抛出异常位置之后的代码将不会被执行
2. 如果抛出异常类型与catch时异常类型不匹配,即异常不会被成功捕获,也就不会被处理,继续往外抛,直到 JVM收到后中断程序----异常是按照类型来捕获的
3. try中可能会抛出多个不同的异常对象,则必须用多个catch来捕获----即多种异常,多次捕获
如果异常之间具有父子关系,一定是子类异常在前catch,父类异常在后catch,否则语法错误
finally
有些特定的代码,不论程序是否发生异常,都需要执行,比如程序中打开的资源:网络连接、数据库 连接、IO流等,在程序正常或者异常退出时,必须要对资源进进行回收。另外,因为异常会引发程序的跳转,可能导致有些语句执行不到,finally就是用来解决这个问题的
不管什么异常 finally 都会被执行
finally 一般用于资源的释放 比如资源需要被回收 那么可以用finally
注意:finally中的代码一定会执行的,一般在finally中进行一些资源清理的扫尾工作。
异常处理流程总结

- 程序先执行 try 中的代码
- 如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配.
- 如果找到匹配的异常类型, 就会执行 catch 中的代码
- 如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者.
- 无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行).
- 如果上层调用者也没有处理的了异常, 就继续向上传递.
- 一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止.
自定义异常类
具体方式:
1. 自定义异常类,然后继承自Exception 或者 RunTimeException
2. 实现一个带有String类型参数的构造方法
注
自定义异常通常会继承自 Exception 或者 RuntimeException
继承自 Exception 的异常默认是受查异常
继承自 RuntimeException 的异常默认是非受查异常.
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/Parallax_/article/details/163174536




