1.java概述
1.1 特点
面向对象性:
两个要素:类、对象
三个特征:封装、继承、多态
健壮性:① 去除了C语言中的指针 ②自动的垃圾回收机制 -->仍然会出现内存溢出、内存泄漏
跨平台型:write once,run anywhere:一次编译,到处运行
功劳归功于:JVM
1.2 运行原理
HelloWorld.java ->javac HelloWorld.java -> java HelloChina
1.3 注释与API文档
- 注释: Comment
分类:
单行注释://
多行注释:/* /
文档注释:/* */
作用:
① 对所写的程序进行解释说明,增强可读性。
② 调试所写的代码
特点:
①单行注释和多行注释,注释了的内容不参与编译。
换句话说,编译以后生成的.class结尾的字节码文件中不包含注释掉的信息
② 注释内容可以被JDK提供的工具 javadoc 所解析,生成一套以网页文件形式体现的该程序的说明文档。
③ 多行注释不可以嵌套使用 - Java API 文档:
API:application programming interface。习惯上:将语言提供的类库,都称为api.
API文档:针对于提供的类库如何使用,给的一个说明书。类似于《新华字典》
2. 基本语法
2.1 java关键字与标识符
1. 关键字
- 关键字: 被Java语言赋予了特殊含义,用做专门用途的字符串(单词)
- 特点:关键字中所字母都为小写
- 示例:
2. 保留字
保留字: 现Java版本尚未使用,但以后版本可能会作为关键字使用。
具体哪些保留字:goto 、const
注意:自己命名标识符时要避免使用这些保留字
3. 标识符
标识符:凡是自己可以起名字的地方都叫标识符。
- 涉及到的结构:
包名、类名、接口名、变量名、方法名、常量名
规则:(必须要遵守。否则,编译不通过)
- 由26个英文字母大小写,0-9,_或$组成
- 数字不可以开头。
- 不可以使用关键字和保留字,但能包含关键字和保留字。
- Java中严格区分大小写,长度无限制。
- 标识符不能包含空格。
规范:(可以不遵守,不影响编译和运行。但是要求大家遵守)
- 包名:多单词组成时所有字母都小写:xxxyyyzzz
- 类名、接口名:多单词组成时,所有单词的首字母大写:XxxYyyZzz(驼峰)
- 变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写:xxxYyyZzz(驼峰)
- 常量名:所有字母都大写。多单词时每个单词用下划线连接:XXX_YYY_Zzz
- 更多编码规范参考阿里巴巴开发手册
- 在起名字时,为了提高阅读性,要尽量意义,“见名知意”
2.3 变量的使用(重点)
- 按数据类型分类
- 基本数据类型介绍
Java语言提供了八种基本类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。
数据类型 | 默认值 | 占用/字节 | 大小/位 |
---|---|---|---|
byte | 0 | 1 | 8 |
short | 0 | 2 | 16 |
int | 0 | 4 | 32 |
long | 0 | 8 | 64 |
float | 0.0 | 4 | 32 |
double | 0.0 | 8 | 64 |
char | 0.0 | \u0 | 16 |
boolean | false | 8 | 64 |
- 引用数据类型
- 类(class), 接口(interface),数组(array),(枚举类型、注解类型、字符串(String)类型)
简单来说,只要不是基本数据类型都是引用数据类型。 - 所有引用类型的默认值都是null。
- 在Java中,引用类型的变量非常类似于C/C++的指针。引用类型指向一个对象,指向对象的变量是引用变量。这些变量在声明时被指定为一个特定的类型,变量一旦声明后,类型就不能被改变了。
- 不同点
- 基本数据类型:变量名指向具体的数值
- 基本数据类型:被创建时,在栈内存中会被划分出一定的内存,并将数值存储在该内存中
- 引用数据类型:变量名不是指向具体的数值,而是指向存数据的内存地址,为及时hash值
- 引用数据类型:被创建时,首先会在栈内存中分配一块空间,然后在堆内存中也会分配一块具体的空间用来存储数据的具体信息,即hash值,然后由栈中引用指向堆中的对象地址
- 定义变量的格式
- 数据类型 变量名 = 变量值;(int a =10;)
- 数据类型 变量名;
变量名 = 变量值;
int a;
a = 10;
- 变量使用的注意点
- 变量必须先声明,后使用
- 变量都定义在其作用域内。在作用域内,它是有效的。换句话说,出了作用域,就失效了
- 同一个作用域内,不可以声明两个同名的变量
-
自动类型转换(只涉及7种基本数据类型)
结论: 当容量小的数据类型的变量与容量大的数据类型的变量做运算时,结果自动提升为容量大的数据类型。
byte 、char 、short --> int --> long --> float --> double
特别的:当byte、char、short三种类型的变量做运算时,结果为int型
说明:此时的容量大小指的是,表示数的范围的大和小。比如:float容量要大于long的容量 -
强制类型转换(只涉及7种基本数据类型):自动类型提升运算的逆运算
1.需要使用强转符:()
2.注意点:强制类型转换,可能导致精度损失。 -
String可以和8种基本数据类型变量做运算,且运算只能是连接运算:+
-
运算的结果仍然是String类型
-
比较
- 基本数据类型:判断数据是否相等,用==和!=判断
- 引用数据类型:判断数据是否相等,用equals()方法,==和!=是比较数值的而equals()方法是比较内存地址的
-
内存举例
int a =1;
int b = 2;
String str1 = "hello";
String str2 = "hello";
a,b为栈内存,分别开辟了内存空间,
str1,str2在栈内存中被开辟,同时指向在堆内存中的hello.
2.4 运算符
- 算术运算符: + - + - * / % (前)++ (后)++ (前)-- (后)-- + (连接符)
- 赋值运算符:= += -= *= /= %=
- 比较运算符(关系运算符): == != > < >= <= instanceof
- 逻辑运算符:& && | || ! ^
1. 区分& 与 &&
相同点1:& 与 && 的运算结果相同
相同点2:当符号左边是true时,二者都会执行符号右边的运算
不同点:当符号左边是false时,&继续执行符号右边的运算。&&不再执行符号右边的运算。
开发中,推荐使用&&
2. 区分:| 与 ||
相同点1:| 与 || 的运算结果相同
相同点2:当符号左边是false时,二者都会执行符号右边的运算
不同点3:当符号左边是true时,|继续执行符号右边的运算,而||不再执行符号右边的运算
开发中,推荐使用|| - 三元运算符:(条件表达式)? 表达式1 : 表达式2
2.5 流程控制
-
顺序结构:程序从上到下执行。
-
分支结构:
if-else if - else
switch-case:
① 根据switch表达式中的值,依次匹配各个case中的常量。一旦匹配成功,则进入相应case结构中,调用其执行语句。
当调用完执行语句以后,则仍然继续向下执行其他case结构中的执行语句,直到遇到break关键字或此switch-case结构
末尾结束为止。
② break,可以使用在switch-case结构中,表示一旦执行到此关键字,就跳出switch-case结构
③ switch结构中的表达式,只能是如下的6种数据类型之一:
byte 、short、char、int、枚举类型(JDK5.0新增)、String类型(JDK7.0新增)
④ case 之后只能声明常量。不能声明范围。
⑤ break关键字是可选的。
⑥ default:相当于if-else结构中的else.
default结构是可选的,而且位置是灵活的。 -
循环结构:
for
while
do-while -
break和continue关键字的使用
- break:结束当前循环
- continue:结束当次循环
2.6 Scanner类的使用
如何从键盘获取不同类型的变量:需要使用Scanner类
具体实现步骤:
1.导包:import java.util.Scanner;
2.Scanner的实例化:Scanner scan = new Scanner(System.in);
3.调用Scanner类的相关方法(next() / nextXxx()),来获取指定类型的变量
注意:
需要根据相应的方法,来输入指定类型的值。如果输入的数据类型与要求的类型不匹配时,会报异常:InputMisMatchException
导致程序终止。
//1.导包:import java.util.Scanner;
import java.util.Scanner;
class ScannerTest{
public static void main(String[] args){
//2.Scanner的实例化
Scanner scan = new Scanner(System.in);
//3.调用Scanner类的相关方法
System.out.println("请输入你的姓名:");
String name = scan.next();
System.out.println(name);
System.out.println("请输入你的芳龄:");
int age = scan.nextInt();
System.out.println(age);
System.out.println("请输入你的体重:");
double weight = scan.nextDouble();
System.out.println(weight);
System.out.println("你是否相中我了呢?(true/false)");
boolean isLove = scan.nextBoolean();
System.out.println(isLove);
//对于char型的获取,Scanner没有提供相关的方法。只能获取一个字符串
System.out.println("请输入你的性别:(男/女)");
String gender = scan.next();//"男"
char genderChar = gender.charAt(0);//获取索引为0位置上的字符
System.out.println(genderChar);
}
}
3. 数组
3.1 数组概述
-
数组的理解:数组(Array),是多个相同类型数据一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理。
-
数组的特点:
a. 数组是序排列的
b. 数组属于引用数据类型的变量。数组的元素,既可以是基本数据类型,也可以是引用数据类型
c. 创建数组对象会在内存中开辟一整块连续的空间
d. 数组的长度一旦确定,就不能修改。
3.2 一维数组的声明与初始化
正确的方式:
int num;//声明
num = 10;//初始化
int id = 1001;//声明 + 初始化
int[] ids;//声明
//1.1 静态初始化:数组的初始化和数组元素的赋值操作同时进行
ids = new int[]{1001,1002,1003,1004};
//1.2动态初始化:数组的初始化和数组元素的赋值操作分开进行
String[] names = new String[5];
int[] arr4 = {1,2,3,4,5};//类型推断
错误的方式:
// int[] arr1 = new int[];
// int[5] arr2 = new int[5];
// int[] arr3 = new int[3]{1,2,3};
- 2.一维数组元素的引用:通过角标的方式调用。
String[] names = new String[5];
//数组的角标(或索引从0开始的,到数组的长度-1结束。
names[0] = "王铭";
names[1] = "王赫";
names[2] = "张学良";
names[3] = "孙居龙";
names[4] = "王宏志";//charAt(0)
- 3.数组的属性:length
System.out.println(names.length);//5
说明:
数组一旦初始化,其长度就是确定的。arr.length
数组长度一旦确定,就不可修改。
- 4.一维数组的遍历
for(int i = 0;i < names.length;i++){
System.out.println(names[i]);
}
-
5.一维数组元素的默认初始化值
数组元素是整型:0
数组元素是浮点型:0.0
数组元素是char型:0或’\u0000’,而非’0’
数组元素是boolean型:false
数组元素是引用数据类型:null
3.2 二维数组的声明与初始化
正确的方式:
int[] arr = new int[]{1,2,3};//一维数组
//静态初始化
int[][] arr1 = new int[][]{{1,2,3},{4,5},{6,7,8}};
//动态初始化1
String[][] arr2 = new String[3][2];
//动态初始化2
String[][] arr3 = new String[3][];
//也是正确的写法:
int[] arr4[] = new int[][]{{1,2,3},{4,5,9,10},{6,7,8}};
int[] arr5[] = {{1,2,3},{4,5},{6,7,8}};//类型推断
错误的方式:
// String[][] arr4 = new String[][4];
// String[4][3] arr5 = new String[][];
// int[][] arr6 = new int[4][3]{{1,2,3},{4,5},{6,7,8}};
- 二维数组元素的默认初始化值
-
规定:二维数组分为外层数组的元素,内层数组的元素
int[][] arr = new int[4][3];
外层元素:arr[0],arr[1]等
内层元素:arr[0][0],arr[1][2]等 -
数组元素的默认初始化值
针对于初始化方式一:比如:int[][] arr = new int[4][3];
外层元素的初始化值为:地址值
内层元素的初始化值为:与一维数组初始化情况相同 -
针对于初始化方式二:比如:int[][] arr = new int[4][];
外层元素的初始化值为:null
内层元素的初始化值为:不能调用,否则报错。
-
- 内存结构
3.3 Arrays工具类的使用
- 定义在java.util包下。
- Arrays:提供了很多操作数组的方法。
- 使用
//1.boolean equals(int[] a,int[] b):1.判断两个数组是否相等。
int[] arr1 = new int[]{1,2,3,4};
int[] arr2 = new int[]{1,3,2,4};
boolean isEquals = Arrays.equals(arr1, arr2);
System.out.println(isEquals); //false
//2.String toString(int[] a):2.输出数组信息。
System.out.println(Arrays.toString(arr1));//[1, 2, 3, 4]
//3.void fill(int[] a,int val):3.将指定值填充到数组之中。
Arrays.fill(arr1,10);
System.out.println(Arrays.toString(arr1)); //[10, 10, 10, 10]
//4.void sort(int[] a):4.对数组进行排序。
Arrays.sort(arr2);
System.out.println(Arrays.toString(arr2)); //[1, 2, 3, 4]
//5.int binarySearch(int[] a,int key) 5.二分查找
int[] arr3 = new int[]{-98,-34,2,34,54,66,79,105,210,333};
int index = Arrays.binarySearch(arr3, 210); //8
if(index >= 0){
System.out.println(index);
}else{
System.out.println("未找到");
}
3.4 常见异常
1.数组角标越界异常:ArrayIndexOutOfBoundsException
int[] arr = new int[]{1,2,3,4,5};
// for(int i = 0;i <= arr.length;i++){
// System.out.println(arr[i]);
// }
// System.out.println(arr[-2]);
// System.out.println("hello");
2.空指针异常:NullPointerException
//情况一:
// int[] arr1 = new int[]{1,2,3};
// arr1 = null;
// System.out.println(arr1[0]);
//情况二:
// int[][] arr2 = new int[4][];
// System.out.println(arr2[0][0]);
//情况:
String[] arr3 = new String[]{"AA","BB","CC"};
arr3[0] = null;
System.out.println(arr3[0].toString());
4. 面向对象
4.1 面向对象的概念
类:对一类事物的描述,是抽象的、概念上的定义
对象:是实际存在的该类事物的每个个体,因而也称为实例(instance)
面向对象程序设计的重点是类的设计
设计类,就是设计类的成员。
4.2 对象的创建
典型代码:
Person p1 = new Person();
Person p2 = new Person();
Person p3 = p1;//没有新创建一个对象,共用一个堆空间中的对象实体。
- 匿名对象:我们创建的对象,没显式的赋给一个变量名。即为匿名对象
- 如果对一个对象只需要进行一次方法调用,那么就可以使用匿名对象。
- 我们经常将匿名对象作为实参传递给一个方法调用。
- 特点:匿名对象只能调用一次。
//匿名对象的使用
mall.show(new Phone());
4.3 属性
对比:属性 vs 局部变量
1.相同点:
- 定义变量的格式:数据类型 变量名 = 变量值
- 先声明,后使用
- 变量都其对应的作用域
2.不同点:
2.1 在类中声明的位置的不同
- 属性:直接定义在类的一对{}内
- 局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量
2.2 关于权限修饰符的不同
- 属性:可以在声明属性时,指明其权限,使用权限修饰符。
- 常用的权限修饰符:private、public、缺省、protected —>封装性
- 局部变量:不可以使用权限修饰符。
2.3 默认初始化值的情况:
-
属性:类的属性,根据其类型,都默认初始化值。
-
整型(byte、short、int、long:0)
-
浮点型(float、double:0.0)
-
字符型(char:0 (或’\u0000’))
-
布尔型(boolean:false)
-
引用数据类型(类、数组、接口:null)
-
局部变量:没默认初始化值。
-
意味着,我们在调用局部变量之前,一定要显式赋值。
-
特别地:形参在调用时,我们赋值即可。
2.4 在内存中加载的位置:
- 属性:加载到堆空间中 (非static)
- 局部变量:加载到栈空间
总结: 属性赋值的先后顺序
① 默认初始化
② 显式初始化
③ 构造器中初始化
④ 通过"对象.方法" 或 "对象.属性"的方式,赋值
以上操作的先后顺序:① - ② - ③ - ④
4.4 方法
- 方法的声明:
权限修饰符 返回值类型 方法名(形参列表){
方法体
}
; - 重载(Overload)和重写(Override)的区别
- 方法的重载
定义:在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。常用的地方是构造器的重载。 - 方法的重写
定义:重写发生在子类与父类之间,重写方法返回值和形参都不能改变,与方法返回值和访问修饰符无关,即重载的方法不能根据返回类型进行区分。即外壳不变,核心重写!
- 重载注意点
不能根据返回值类型来区分重载的方法。因为调用时不指定类型信息,编译器不知道你要调用哪个函数。
int min(int a);
float min(int b);
4.5 构造器
- 构造器的作用:
- 创建对象 Person p = new Person();
- 初始化对象的信息
- 使用说明:
- 如果没显式的定义类的构造器的话,则系统默认提供一个空参的构造器
- 定义构造器的格式:权限修饰符 类名(形参列表){}
- 一个类中定义的多个构造器,彼此构成重载
- 一旦我们显式的定义了类的构造器之后,系统就不再提供默认的空参构造器
- 一个类中,至少会有一个构造器。
- javaBean
所谓JavaBean,是指符合如下标准的Java类:- 类是公共的
- 一个无参的公共的构造器
- 属性,且对应的get、set方法
4.6 java的值传递机制
- 如果参数是基本数据类型,此时实参赋给形参的是实参真实存储的数据值。
- 如果参数是引用数据类型,此时实参赋给形参的是实参存储数据的地址值。
4.7 面向对象的特征一:封装
- 封装: 封装是面向对象的特征之一,是对象和类概念的主要特性。封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
- Java规定的四种权限修饰符
权限从小到大顺序为:private < 缺省 < protected < public
- 4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类
- 修饰类的话,只能使用:缺省、public
- 关键字:this
this理解为:当前对象 或 当前正在创建的对象.
用法: 1.在类的方法中,我们可以使用"this.属性"或"this.方法"的方式,调用当前对象属性或方法,防止方法的形参和类的属性同名。
2.在类的构造器中,我们可以使用"this.属性"或"this.方法"的方式,调用当前正在创建的对象属性或方法,防止构造器的形参和类的属性同名。
3.this调用构造器(减少代码量)
① 我们在类的构造器中,可以显式的使用"this(形参列表)"方式,调用本类中指定的其他构造器
② 构造器中不能通过"this(形参列表)“方式调用自己
③ 如果一个类中有n个构造器,则最多有 n - 1构造器中使用了"this(形参列表)”
④ 规定:"this(形参列表)“必须声明在当前构造器的首行
⑤ 构造器内部,最多只能声明一个"this(形参列表)”,用来调用其他的构造器
4.8 面向对象的特征二:继承
- 继承:继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。通过继承创建的新类称为“子类"或"派生类",被继承的类称为"基类"、“父类"或“超类”。
- 继承性的格式:
class A extends B{}
* A:子类、派生类、subclass
* B:父类、超类、基类、superclass
-
子类继承父类以后有哪些不同?
- 体现:一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法。
- 特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私的结构。只因为封装性的影响,使得子类不能直接调用父类的结构而已。
-
子类继承父类以后,还可以声明自己特有的属性或方法:实现功能的拓展。
- 子类和父类的关系,不同于子集和集合的关系。
- extends:延展、扩展
-
Java中继承性的说明
- 一个类可以被多个子类继承。
- Java中类的单继承性:一个类只能有一个父类
- 子父类是相对的概念。
- 子类直接继承的父类,称为:直接父类。间接继承的父类称为:间接父类
- 子类继承父类以后,就获取了直接父类以及所间接父类中声明的属性和方法
-
方法的重写
1. 定义:重写发生在子类与父类之间,重写方法返回值和形参都不能改变,与方法返回值和访问修饰符无关,即重载的方法不能根据返回类型进行区分。即外壳不变,核心重写!
2. 方法重写的规则之一:
子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型 -
super关键字
- super 关键字可以理解为:父类的
- 可以用来调用的结构:属性、方法、构造器
-
super调用属性、方法:
- 我们可以在子类的方法或构造器中。通过使用"super.属性"或"super.方法"的方式,显式的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略"super."
- 特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的使用"super.属性"的方式,表明调用的是父类中声明的属性。
- 特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的使用"super.方法"的方式,表明调用的是父类中被重写的方法。
-
super调用构造器:
- 我们可以在子类的构造器中显式的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器
- "super(形参列表)"的使用,必须声明在子类构造器的首行!
- 我们在类的构造器中,针对于"this(形参列表)"或"super(形参列表)"只能二一,不能同时出现
- 在构造器的首行,没显式的声明"this(形参列表)“或"super(形参列表)”,则默认调用的是父类中空参的构造器:super()
- 在类的多个构造器中,至少一个类的构造器中使用了"super(形参列表)",调用父类中的构造器
4.9 面向对象的特征三:多态性
-
多态性。它是指在父类中定义的属性和方法被子类继承之后,可以具有不同的数据类型或表现出不同的行为,这使得同一个属性或方法在父类及其各个子类中具有不同的含义。
-
java如何实现多态?
本质上多态分两种:
1、编译时多态(又称静态多态)
2、运行时多态(又称动态多态)重载(overload)就是编译时多态的一个例子,编译时多态在编译时就已经确定,运行的时候调用的是确定的方法。
我们通常所说的多态指的都是运行时多态,也就是编译时不确定究竟调用哪个具体方法,一直延迟到运行时才能确定。这也是为什么有时候多态方法又被称为延迟方法的原因。
Java实现多态有3个必要条件:继承、重写和向上转型。只有满足这3个条件,开发人员才能够在同一个继承结构中使用统一的逻辑实现代码处理不同的对象,从而执行不同的行为。
- 继承:在多态中必须存在有继承关系的子类和父类。
- 重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法
- 向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才既能可以调用父类的方法,又能调用子类的方法。
有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。如何才能调用子类特的属性和方法?使用向下转型。
-
Object类的使用
-
1.Object类是所Java类的根父类
-
2.如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类
-
3.Object类中的功能(属性、方法)就具通用性。
-
属性:无
-
方法:equals() / toString() / getClass() /hashCode() / clone() / finalize()/ wait() 、 notify()、notifyAll()
-
4.Object类只声明了一个空参的构造器
-
-
== 和equals区别是什么?
== 常用于相同的基本数据类型之间的比较,也可用于相同类型的对象之间的比较,- 如果==比较的是基本数据类型,那么比较的是两个基本数据类型的值是否相等;
- 如果==是比较的两个对象,那么比较的是两个对象的引用,也就是判断两个对象是否指向了同一块内存区域;
- equals方法主要用于两个对象之间,检测一个对象是否等于另一个对象.
- Object类中equals方法的源码:
public boolean equals(object obj) {
return (this == obj);
}
equals的作用也是判断两个对象是否相等,一般有两种使用情况:
情况1,类没有覆盖equals()方法。则通过equals()比较该类的两个对象时,等价于通过“=="比较这两个对象。
情况2,类覆盖了equals()方法。一般,我们都覆盖equals()方法来两个对象的内容相等;若它们的内容相等,则返回true(即,认为这两个对象相等)。
5.0 包装类的使用
- 为什么要有包装类(或封装类)
为了使基本数据类型的变量具有类的特征,引入包装类。 - 基本数据类型与对应的包装类:
注意类和基本数据类型的默认值不一样,类的默认值为null
5.1 常用关键字
static
- 介绍
当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份,例如所有的中国人都有个国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中都单独分配一个用于代表国家名称的变量。 - 可以用来修饰的结构
主要用来修饰类的内部结构:属性、方法、代码块、内部类 - static修饰属性:静态变量(或类变量)
- 属性,是否使用static修饰,又分为:静态属性 vs 非静态属性(实例变量)
- 实例变量:我们创建了类的多个对象,每个对象都独立的拥一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改。
- 静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的。
- static修饰属性的其他说明:
① 静态变量随着类的加载而加载。可以通过"类.静态变量"的方式进行调用
② 静态变量的加载要早于对象的创建。
③ 由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中。 - static修饰方法:静态方法、类方法
① 随着类的加载而加载,可以通过"类.静态方法"的方式进行调用
② 静态方法中,只能调用静态的方法或属性
③ 非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性 - 是否可以在static环境中访问非static变量?
不可以,static变量在Java中是属于类的,它在所有的实例中的值是一样的。当类被Java虚拟机载入的时候,会对static变量进行初始化。如果你的代码尝试不用实例来访问非static的变量,编译器会报错,因为这些变量还没有被创建出来,还没有跟任何实例关联上。
代码块(初始化块)
- 1.代码块的作用:用来初始化类、对象的信息
- 2.分类:代码块要是使用修饰符,只能使用static
分类:静态代码块 vs 非静态代码块
静态代码块:
内部可以输出语句
随着类的加载而执行,而且只执行一次
作用:初始化类的信息
如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行
静态代码块的执行要优先于非静态代码块的执行
静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构
非静态代码块:
内部可以输出语句
随着对象的创建而执行
每创建一个对象,就执行一次非静态代码块
作用:可以在创建对象时,对对象的属性等进行初始化
如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行
非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法
final
-
可以用来修饰:类、方法、变量
-
final 用来修饰一个类:此类不能被其他类所继承。
比如:String类、System类、StringBuffer类
- final 用来修饰方法:表明此方法不可以被重写
比如:Object类中getClass();
- final 用来修饰变量:此时的"变量"就称为是一个常量
- final修饰属性:可以考虑赋值的位置:显式初始化、代码块中初始化、构造器中初始化
- final修饰局部变量:
尤其是使用final修饰形参时,表明此形参是一个常量。当我们调用此方法时,给常量形参赋一个实参。一旦赋值以后,就只能在方法体内使用此形参,但不能进行重新赋值。
-
static final 用来修饰属性:全局常量
-
final、finally、finalize的区别?
final 用于修饰变量、方法和类。
· final变量:被修饰的变量不可变,不可变分为引用不可变和对象不可变),final指的是引用不可变,final修饰的变量必须初始化,通常称被修饰的变量为常量。
· final方法:被修饰的方法不允许任何子类重写,子类可以使用该方法。. final类:被修饰的类不能被继承,所有方法不能被重写。finally作为异常处理的一部分,它只能在try/catch语句中,并且附带一个语句块表示这段语句最终一定被执行(无论是否抛出异常),经常被用在需要释放资源的情况下,System.exit(0)可以阻断finally执行。
finalize是在java.lang.object里定义的方法,也就是说每一个对象都有这么个方法这个方法在gc启动,该对象被回收的时候被调用。
一个对象的 finalize方法只会被调用一次,finalize被调用不一定会立即回收该对象,所以有可能调用finalize后,该对象又不需要被回收了,然后到了真正要被回收的时候,因为前面调用过一次,所以不会再次调用finalize 了,进而产生问题,因此不推荐使用finalize方法。
abstract
- 可以用来修饰:类、方法
- abstract修饰类:抽象类
1.此类不能实例化
2.抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)
3.开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作
4.抽象的使用前提:继承性
- abstract修饰方法:抽象方法
1.抽象方法只方法的声明,没方法体
2.包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。
3.若子类重写了父类中的所的抽象方法后,此子类方可实例化
4.若子类没重写父类中的所的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰
- 注意点:
1.abstract不能用来修饰:属性、构造器等结构
2.abstract不能用来修饰私方法、静态方法、final的方法、final的类
interface
-
接口使用interface来定义
-
Java中,接口和类是并列的两个结构
-
如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的默认方法。–>类优先原则
-
如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么在实现类没有重写此方法的情况下,报错。–>接口冲突,这需要我们必须在实现类中重写此方法。
-
JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法
接口中不能定义构造器的!意味着接口不可以实例化 -
Java开发中,接口通过让类去实现(implements)的方式来使用.
如果实现类覆盖了接口中的所抽象方法,则此实现类就可以实例化
如果实现类没覆盖接口中所的抽象方法,则此实现类仍为一个抽象类 -
Java类可以实现多个接口 —>弥补了Java单继承性的局限性
格式:class AA extends BB implements CC,DD,EE -
接口与接口之间可以继承,而且可以多继承
-
接口的具体使用,体现多态性
-
接口,实际上可以看做是一种规范
-
接口中定义的静态方法,只能通过接口来调用
内部类
- 定义:Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类.
5 异常
5.1 异常的体系结构
- 从程序执行过程,看编译时异常和运行时异常
编译时异常:执行javac.exe命名时,可能出现的异常
运行时异常:执行java.exe命名时,出现的异常
5.2 异常的处理
1.java异常处理的抓抛模型
- 过程一:“抛”:程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象。并将此对象抛出,一旦抛出对象以后,其后的代码就不再执行。
关于异常对象的产生:① 系统自动生成的异常对象
② 手动的生成一个异常对象,并抛出(throw)
- 过程二:“抓”:可以理解为异常的处理方式:① try-catch-finally ② throws
异常处理方式一:try-catch-finally
try{
//可能出现异常的代码
}catch(异常类型1 变量名1){
//处理异常的方式1
}catch(异常类型2 变量名2){
//处理异常的方式2
}catch(异常类型3 变量名3){
//处理异常的方式3
}
....
finally{
//一定会执行的代码
}
说明:
1. finally是可以省略的。
2. 使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配
3. 一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出当前的try-catch结构(在没写finally的情况。继续执行其后的代码
4. catch中的异常类型如果没子父类关系,则谁声明在上,谁声明在下无所谓。
catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面。否则,报错
5. 常用的异常对象处理的方式: ① String getMessage()
② printStackTrace()
6. 在try结构中声明的变量,再出了try结构以后,就不能再被调用
7. try-catch-finally结构可以嵌套
总结: 如何看待代码中的编译时异常和运行时异常?
-
体会1:使用try-catch-finally处理编译时异常,是得程序在编译时就不再报错,但是运行时仍可能报错。相当于我们使用try-catch-finally将一个编译时可能出现的异常,延迟到运行时出现。
-
体会2:开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了。针对于编译时异常,我们说一定要考虑异常的处理。
2. finally的再说明:
- 1.finally是可以省略的
- 2.finally中声明的是一定会被执行的代码。即使catch中又出现异常了,try中return语句,catch中return语句等情况。
- 3.像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己手动的进行资源的释放。此时的资源释放,就需要声明在finally中。
3. final、finally、finalize的区别?
final 用于修饰变量、方法和类。
· final变量:被修饰的变量不可变,不可变分为引用不可变和对象不可变),final指的是引用不可变,final修饰的变量必须初始化,通常称被修饰的变量为常量。
· final方法:被修饰的方法不允许任何子类重写,子类可以使用该方法。. final类:被修饰的类不能被继承,所有方法不能被重写。
finally作为异常处理的一部分,它只能在try/catch语句中,并且附带一个语句块表示这段语句最终一定被执行(无论是否抛出异常),经常被用在需要释放资源的情况下,System.exit(0)可以阻断finally执行。
finalize是在java.lang.object里定义的方法,也就是说每一个对象都有这么个方法这个方法在gc启动,该对象被回收的时候被调用。
一个对象的 finalize方法只会被调用一次,finalize被调用不一定会立即回收该对象,所以有可能调用finalize后,该对象又不需要被回收了,然后到了真正要被回收的时候,因为前面调用过一次,所以不会再次调用finalize 了,进而产生问题,因此不推荐使用finalize方法。
4.异常处理方式二:
- "throws + 异常类型"写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。
- 一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。异常代码后续的代码,就不再执行!
5. 对比两种处理方式
- try-catch-finally:真正的将异常给处理掉了。
- throws的方式只是将异常抛给了方法的调用者。并没真正将异常处理掉。
6. 手动抛出异常对象
-
1.使用说明
在程序执行中,除了自动抛出异常对象的情况之外,我们还可以手动的throw一个异常类的对象。 -
2.面试题
throw 和 throws区别:
throw 表示抛出一个异常类的对象,生成异常对象的过程。声明在方法体内。
throws 属于异常处理的一种方式,声明在方法的声明处。 -
3.典型例题
class Student{
private int id;
public void regist(int id) throws Exception {
if(id > 0){
this.id = id;
}else{
//手动抛出异常对象
// throw new RuntimeException("您输入的数据非法!");
// throw new Exception("您输入的数据非法!");
throw new MyException("不能输入负数");
}
}
@Override
public String toString() {
return "Student [id=" + id + "]";
}
}
7. 自定义一个异常类
- 继承于现的异常结构:RuntimeException 、Exception
- 提供全局常量:serialVersionUID
- 提供重载的构造器
/*
* 如何自定义异常类?
* 1. 继承于现的异常结构:RuntimeException 、Exception
* 2. 提供全局常量:serialVersionUID
* 3. 提供重载的构造器
*
*/
public class MyException extends Exception{
static final long serialVersionUID = -7034897193246939L;
public MyException(){
}
public MyException(String msg){
super(msg);
}
}