Java基础——狂神说
文章目录
- Java基础——狂神说
- 一、第一章——基础语法
- 二、第二章——流程控制
- 三、第三章——方法
- 四、第四章——数组
- 五、第五章——面向对象
- 1、什么是面向对象
- 2、类和对象
- 3、类的五个成员
- 4、面向对象的三大特征
- 5、其它关键字:this、super、static、final、abstract、interface、package、import等
- 六、第六章——异常
一、第一章——基础语法
1、标识符
合法标识符的规范
Java | C语言 |
---|---|
由26个英文字母大小写,0-9 ,_或 $ 组成 | 由字母、下划线(_)或数字(0~9)组成 |
数字不可以开头。 | 必须由一个字母或下划线(_)开头 |
不可以使用关键字,但能包含关键字。 | |
Java中严格区分大小写,长度无限制。 | |
标识符不能包含空格。 |
2、变量、数据类型、常量
2.1、变量的分类
2.1.1、按数据类型分类
分为:基本数据类型和引用数据类型
1、基本数据类型包括:数值型(byte,short,int,long,float,double)、字符型(char)、布尔型(boolean)
2、引用数据类型包括:类(字符串包括在内)、接口、数组
2.1.1.1、浮点类型
注意:最好避免完全使用float数进行比较(下面代码是反例)
public static void main(String[] args) {
float num1 = 1321546541212154f;
float num2 = num1 + 100.54152113124f;
System.out.println(num1 == num2);
}
/*
运行结果为:true
*/
2.1.2、按声明的位置的不同分类
在方法体外,类体内声明的变量成为成员变量
在方法体内声明的变量成为局部变量
1、成员变量包括:实例变量、类变量
2、局部变量包括:形参、方法局部变量、代码块局部变量
3、基本数据类型转换
容量大——>容量小:强制类型转换
容量小——>容量大:自动类型转换
自动类型转换:容量小的类型自动转换为容量大的数据类型。数据类型按容量大小排序为:(char、byte、short)、int、long、float、double
public static void main(String[] args) {
double num1 = 14.235;
int num2 = (int)num1;
System.out.println(num2);//(强制类型转换)
int num3 = 123;
double num4 = num3;
System.out.println(num4);//(自动类型转换)
}
/*
运行结果为:14
123.0
*/
4、常量
常量:初始化后不能再改变值!不会变动的值
常量名一般使用大写字符
final 常量名 = 值;
final double PT = 3.14;
5、名称的命名规则
包名:多单词组成时所有字母都小写:xxxyyyzzz。
类名、接口名:多单词组成时,所有单词的首字母大写:XxxYyyZzz。
变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写:xxxYyyZzz。
常量名:所有字母都大写。多单词时每个单词用下划线链接:XXX_YYY_ZZZ。
6、运算符
大部分与c语言的运算符类似,新增了“instanceof”运算符,用来测试指定对象是否是指定类型(类或子类或接口)的实例。
需要注意的是 :逻辑与(&)和短路与(&&)的区别:
“&”:左边表达式无论真假,右边都进行运算。
“&&”:如果左边为真,右边参与运算,如果左边为假,那么右边不参与运算。
二、第二章——流程控制
1、用户交互Scanner
1.1 基本语法
Scanner scan = new Scannner(System.in);
1.2 next()和nextLine()的区别
1、next():获取输入的字符串
next()不能得到带有空格的字符串,不能获得空白
2、hasNext():判断是否还有输入的数据
public static void main(String[] args) {
//创建一个Scanner对象
Scanner scanner = new Scanner(System.in);
System.out.println("使用next方式接受:");
//判断是否输入字符串
if(scanner.hasNext()){
String str = scanner.next();
System.out.println("输入的内容为:" + str);
}
scanner.close();
}
/*
键盘输入内容:hello world
输出结果:hello
*/
1、nextLine():获取输入的字符串
next()以Enter为结束符,可以获得空白
2、hasNexLinet():判断是否还有输入的数据
public static void main(String[] args) {
//创建一个Scanner对象
Scanner scanner = new Scanner(System.in);
System.out.println("使用nextLine方式接受:");
//判断是否输入字符串
if(scanner.hasNextLine()){
String str = scanner.nextLine();
System.out.println("输入的内容为:" + str);
}
scanner.close();
}
/*
键盘输入内:hello world
输出结果:hello world
*/
2、顺序结构
与c语言内容类似,不赘述。
3、选择结构
与c语言内容类似,不赘述。
4、循环结构
其他内容与c语言内容类似,不赘述。
增强for循环
增强for循环:主要用于数组或集合
声明语句:声明新的局部变量,该类型与数组元素的类型匹配。
表达式:访问的数组名,或者是返回值为数组的方法
for(声明语句 : 表达式)
{
//代码句子
}
三、第三章——方法
1、方法的重载
1、方法名称必须相同
2、参数列表必须不同(个数不同、类型不同、参数排序顺序不同)
3、方法的返回类型可以相同也可以不相同
2、可变参数
1、在方法声明中,在指定参数类型后加一个省略号(…)。
2、可变参数必须是方法的最后一个参数。
public static void main(String[] args) {
double[] scores = new double[]{60,70,80,90,40};
demo01.test(scores.length,scores);
}
public static void test(int count , double ... scores){
if(count <= 0){
System.out.println("无输入的数据");
}else{
System.out.println("输入的数据为:");
for (int i = 0; i < scores.length; i++) {
System.out.print(scores[i] + "\t");
}
}
}
/*
输出的结果为:
输入的数据为:
60.0 70.0 80.0 90.0 40.0
*/
3、递归算法
3.1、递归的思路
一个方法在执行时,调用自身被称为“递归”。
递归相当于数学归纳法,有一个起始条件,有一个递推公式。
递归可以分为:单路递归和多路递归(如二叉树和斐波那契数列)。
3.2、代码演示——斐波那契数列
斐波那契数列:1 1 2 3 5 8 13
public static void main(String[] args) {
System.out.println(fib(7));//输出的结果为:13
}
public static int fib(int n){
if(n==1 || n==2){
return 1;
}
return fib(n-1) + fib(n-2);
}
四、第四章——数组
1、一维数组
1.1、一维数组的声明
dataType[] arrayRefVar;//首选
或者
dateType arrayRefVar[];
1.2、一维数组的三种初始化
1.2.1、静态初始化
int arr[] = new int[]{3,9,8};
或者
int[] arr = {3,9,8};
或者
String names[] = {"张三","李四","王五"};
1.2.2、动态初始化
int[] arr = new int[3];
arr[0] = 3;
arr[1] = 9;
arr[2] = 8;
或者
String names[];
names = new String[3];
names[0] = "张三";
names[1] = "李四";
names[2] = "王五";
1.2.3、默认初始化及默认初始化值
数组一经分配空间,其中每个元素也被隐式初始化。
默认初始化值:
数组元素类型 | 元素默认初始化值 |
---|---|
byte | 0 |
short | 0 |
int | 0 |
long | 0L |
float | 0.0F |
double | 0.0 |
char | 0或写为:‘\u0000’(表现为空) |
boolean | false |
引用类型 | null |
1.3、内存分析
内存解析实例
2、二维数组
2.1、二维数组的两种初始化方式
2.1.1、静态初始化
int[][] arr = new int[][]{{{3,8,2},{2,7},{9,0,1,6}};
注意特殊写法:int[] x, y[];
x是一维数组,y是二维数组。
2.1.2、动态初始化
格式1:int[][] arr = new int[3][2];
格式2:int[][] arr = new int[3][];
2.内存解析
3、Arrays工具类的使用
boolean equals(int []a,int []b):判断两个数组是否相等。
String toString(int[] a):输出数组信息。
void fill(int []a,int val):将指定值填充到数值之中。
void sort(int[] a):对数组进行排序。
int binarySearch(int[] a, int key):对排序后的数组进行二分法检索指定的值。
public static void main(String[] args) {
int[] arr = new int[]{11,22,33,445,4535,-43656};
//输出数组信息
System.out.println(Arrays.toString(arr));
//将指定值填充到数组中,包前不包后
Arrays.fill(arr,2,4,0);
System.out.println(Arrays.toString(arr));
//判断两个数组是否相等
boolean isFlag = Arrays.equals(arr, new int[]{10,20});
System.out.println(isFlag);
}
/*
输出结果为:
[11, 22, 33, 445, 4535, -43656]
[11, 22, 0, 0, 4535, -43656]
false
*/
4、稀疏数组
当一个数组中大部分元素为0,或者为同一值的数组时,可以使用稀疏数组来保存该数组。
行数:非0元素个数+1。其中第一行的分别代表着数组的行数、列数和非0元素个数。第二行开始每一行分别代表着非0元素所在的行号、列号和元素值。
列数:3列
public static void main(String[] args) {
int[][] arr1 = new int[][]{
{0,0,0,22,0,0,15},
{0,11,0,0,0,17,0},
{0,0,0,-6,0,0,0},
{0,0,0,0,0,39,0},
{91,0,0,0,0,0,0},
{0,0,28,0,0,0,0}
};
//输出原数组,并记录非零元素个数
int totalCount = 0;
System.out.println("原数组为:");
for (int[] x : arr1) {
for(int y : x){
System.out.print(y + "\t");
if(y != 0)
totalCount++;
}
System.out.println();
}
System.out.println("******************************");
//创建稀疏数组
int[][] arr2 = new int[totalCount + 1][3];
//初始化稀疏数组
arr2[0][0] = arr1.length;
arr2[0][1] = arr1[0].length;
arr2[0][2] = totalCount;
int count = 1;//记录稀疏数组的行数
for (int i = 0; i < arr1.length; i++) {
for (int j = 0; j < arr1[i].length; j++) {
if(arr1[i][j] != 0){
arr2[count][0] = i;
arr2[count][1] = j;
arr2[count][2] = arr1[i][j];
count++;
}
}
}
//输出稀疏数组
System.out.println("稀疏数组为:");
for (int[] x : arr2) {
for(int y : x){
System.out.print(y + "\t");
if(y != 0)
totalCount++;
}
System.out.println();
}
}
/*
输出结果:
原数组为:
0 0 0 22 0 0 15
0 11 0 0 0 17 0
0 0 0 -6 0 0 0
0 0 0 0 0 39 0
91 0 0 0 0 0 0
0 0 28 0 0 0 0
******************************
稀疏数组为:
6 7 8
0 3 22
0 6 15
1 1 11
1 5 17
2 3 -6
3 5 39
4 0 91
5 2 28
*/
五、第五章——面向对象
1、什么是面向对象
1.1、面向对象和面向过程
面向过程:强调的是功能行为,以函数为最小单位,考虑怎么做。
面向对象:强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。
1.2、面向对象的本质及三大特性
面向对象的本质:以类的方式组织代码,以对象的组织(封装)数组。
三大特性:封装、继承、多态。
2、类和对象
2.1、概念
类:对一类事物的描述,是抽象的、概念上的定义
对象:是实际存在的该类事物的每个个体,因而也称为实例(instance)
--------面向对象程序设计的重点是类的设计
--------设计类,就是设计类的成员。
2.2、二者关系
对象,是由类new出来的,派生出来的。
2.3、对象的创建与对象的内存解析
2.3.1、对象的创建
Person p1 = new Person();
Person p2 = new Person();
Person p3 = p1;//没有创建一个对象,p3和p1共用一个堆空间的对象实体。
2.3.2对象的内存解析
2.4、匿名对象
匿名对象:我们创建的对象,没显式的赋给一个变量名。
特点:匿名对象只能调用一次。
new Phone().sendEmail();
new Phone().playGame();
new Phone.price = 1999;
new Phone().showPrice();//0.0
3、类的五个成员
类的成员:属性、方法、构造器;代码块、内部类。
3.1、属性
3.1.1、属性和局部变量的相同点
- 定义变量的格式:数据类型 变量名 = 变量值
- 先声明,后使用
- 变量都其对应的作用域
3.1.2、属性和局部变量的不同点
- 1、在类中声明的位置的不同
——属性:直接定义在类的一对{}内
——局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量- 2、关于权限修饰符的不同
——属性:可以在声明属性时,指明其权限,使用权限修饰符。
常用的权限修饰符:private、public、缺省、protected —>封装性
——局部变量:不可以使用权限修饰符。- 3、默认初始化值的情况
——属性:类的属性,根据其类型,都默认初始化值。
整型(byte、short、int、long:0)
浮点型(float、double:0.0)
字符型(char:0 (或’\u0000’))
布尔型(boolean:false)
引用数据类型(类、数组、接口:null)
——局部变量:没默认初始化值。
意味着,我们在调用局部变量之前,一定要显式赋值。
特别地:形参在调用时,我们赋值即可。- 4、在内存中加载的位置
—— 属性:加载到堆空间中 (非static)
——局部变量:加载到栈空间
3.2、方法
3.2.1、方法的声明
权限修饰符 返回值类型 方法名(形参列表){ 方法体 }
3.2.2、说明
- 1、关于权限修饰符:默认方法的权限修饰符先都使用public
Java规定的4种权限修饰符:private、public、缺省、protected -->封装性再细说- 2、返回值类型: 返回值 vs 没返回值
——如果方法返回值,则必须在方法声明时,指定返回值的类型。同时,方法中,需要使用return关键字来返回指定类型的变量或常量:“return 数据”。
——如果方法没返回值,则方法声明时,使用void来表示。通常,没返回值的方法中,就不需要使用return.但是,如果使用的话,只能“return;”表示结束此方法的意思。- 3、方法名:属于标识符,遵循标识符的规则和规范,“见名知意”
- 4、形参列表: 方法可以声明0个,1个,或多个形参。
——格式:数据类型1 形参1,数据类型2 形参2,…- 5、方法体:方法功能的体现。
3.3、构造器
3.3.1、作用
1.创建对象
2.初始化对象的信息
3.3.2、使用说明
1.如果没显式的定义类的构造器的话,则系统默认提供一个空参的构造器
2.定义构造器的格式:权限修饰符 类名(形参列表){}
3.一个类中定义的多个构造器,彼此构成重载
4.一旦我们显式的定义了类的构造器之后,系统就不再提供默认的空参构造器
5.一个类中,至少会有一个构造器。
3.3.3、代码举例
//构造器
public Person(){
System.out.println("Person().....");
}
public Person(String n){
name = n;
}
public Person(String n,int a){
name = n;
age = a;
}
3.4、代码块
3.4.1、作用
代码块的作用:用来初始化类、对象的信息
3.4.2、静态代码块 VS 非静态代码块
静态代码块 | 非静态代码块 |
---|---|
作用:初始化类的信息 | 作用:可以在创建对象时,对对象的属性等进行初始化 |
内部可以输出语句 | 内部可以输出语句 |
随着类的加载而执行,而且只执行一次 | 随着对象的创建而执行 |
如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行 | 如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行 |
静态代码块的执行要优先于非静态代码块的执行 | 每创建一个对象,就执行一次非静态代码块 |
静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构 | 非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法 |
3.4.3、属性赋值的顺序
①默认初始化
②显式初始化/⑤在代码块中赋值
③构造器中初始化
④有了对象以后,可以通过"对象.属性"或"对象.方法"的方式,进行赋值
执行的先后顺序:① ——>② / ⑤ ——> ③—— > ④
3.5、内部类
3.5.1、定义
定义:Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类.
3.5.2、分类
成员内部类(静态、非静态 )VS 局部内部类(方法内、代码块内、构造器内)
3.5.3、对于成员内部类的理解
一方面,作为外部类的成员:
1.调用外部类的结构
2.可以被static修饰
3.可以被4种不同的权限修饰
另一方面,作为一个类:
1.类内可以定义属性、方法、构造器等
2.可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承
3.可以被abstract修饰
3.5.4、成员内部类
3.5.4.1、如何创建成员内部类的对象?(静态的,非静态的)
//创建静态的Dog内部类的实例(静态的成员内部类):
Person.Dog dog = new Person.Dog();
//创建非静态的Bird内部类的实例(非静态的成员内部类):
//Person.Bird bird = new Person.Bird();//错误的
Person p = new Person();
Person.Bird bird = p.new Bird();
3.5.4.2、如何在成员内部类中调用外部类的结构?
class Person{
String name = "小明";
public void eat(){
}
//非静态成员内部类
class Bird{
String name = "杜鹃";
public void display(String name){
System.out.println(name);//方法的形参
System.out.println(this.name);//内部类的属性
System.out.println(Person.this.name);//外部类的属性
Person.this.eat();//外部类的方法
}
}
}
3.5.5、局部内部类的使用
//返回一个实现了Comparable接口的类的对象
public Comparable getComparable(){
//创建一个实现了Comparable接口的类:局部内部类
//方式一:
// class MyComparable implements Comparable{
//
// @Override
// public int compareTo(Object o) {
// return 0;
// }
//
// }
//
// return new MyComparable();
//方式二:
return new Comparable(){
@Override
public int compareTo(Object o) {
return 0;
}
};
}
4、面向对象的三大特征
面向对象的三大特征为:封装性、继承性、多态性
4.1、封装性
4.1.1、封装性思想具体的代码体现:
- 体现一:将类的属性xxx私化(private),同时,提供公共的(public)方法来获取(getXxx)和设置(setXxx)此属性的值
private double radius;
public void setRadius(double radius){
this.radius = radius;
}
public double getRadius(){
return radius;
}
- 体现二:不对外暴露的私有的方法
- 体现三:单例模式(将构造器私有化)
- 体现四:如果不希望类在包外被调用,可以将类设置为缺省的。
4.1.2、Java规定的四种权限修饰符
4.1.2.1、权限从小到大顺序为:private < 缺省 < protected < public
4.1.2.2、具体的修饰范围:
private:类内部。
(缺省):类内部、同一个包下。
protected:类内部、同一个包下、不同包的子类。
public:类内部、同一个包下、不同包的子类、同一个工程。
4.1.2.3、权限修饰符可用来修饰的结构说明:
4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类。
修饰类的话,只能使用:缺省、public
4.2、继承性
4.2.1、继承性的格式:
class A extends B{}
* A:子类、派生类、subclass
* B:父类、超类、基类、superclass
4.2.2、子类继承父类以后有哪些不同?
- 1.体现:一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法。
特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私的结构。只因为封装性的影响,使得子类不能直接调用父类的结构而已。- 2.子类继承父类以后,还可以声明自己特有的属性或方法:实现功能的拓展。
子类和父类的关系,不同于子集和集合的关系。
extends:延展、扩展
4.2.3、Java中继承性的说明
1.一个类可以被多个子类继承。
2.Java中类的单继承性:一个类只能有一个父类
3.子父类是相对的概念。
4.子类直接继承的父类,称为:直接父类。间接继承的父类称为:间接父类
5.子类继承父类以后,就获取了直接父类以及所间接父类中声明的属性和方法
4.3、多态性
4.3.1、何为多态性?
对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
举例:
Person p = new Man();
Object obj = new Date();
4.3.2、多态性的使用前提:
① 类的继承关系 ② 方法的重写
4.3.3、代码举例
public void func(Animal animal){//Animal animal = new Dog();
animal.eat();
animal.shout();
}
4.3.4、多态性使用的注意点:
对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
4.3.5、关于向上转型与向下转型:
4.3.5.1、向上转型:多态
4.3.5.2、向下转型:
- 1 为什么使用向下转型:
有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。如何才能调用子类特的属性和方法?使用向下转型。- 2 如何实现向下转型:
使用强制类型转换符:()- 3 使用时的注意点:
① 使用强转时,可能出现ClassCastException的异常。
② 为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。- 4 instanceof的使用:
① a instanceof A:判断对象a是否是类A的实例。如果是,返回true;如果不是,返回false。
② 如果 a instanceof A返回true,则 a instanceof B也返回true.其中,类B是类A的父类。
③ 要求a所属的类与类A必须是子类和父类的关系,否则编译错误。
5、其它关键字:this、super、static、final、abstract、interface、package、import等
5.1、this关键字
可以调用的结构:属性、方法;构造器
5.1.1、this调用属性、方法:
- this理解为:当前对象 或 当前正在创建的对象
- 1、在类的方法中,我们可以使用"this.属性"或"this.方法"的方式,调用当前对象属性或方法。但是,
通常情况下,我们都择省略"this."。特殊情况下,如果方法的形参和类的属性同名时,我们必须显式
的使用"this.变量"的方式,表明此变量是属性,而非形参。- 2 、在类的构造器中,我们可以使用"this.属性"或"this.方法"的方式,调用当前正在创建的对象属性或方法。但是,通常情况下,我们都择省略"this."。特殊情况下,如果构造器的形参和类的属性同名时,我们必须显式的使用"this.变量"的方式,表明此变量是属性,而非形参。
5.1.2、this调用构造器:
① 我们在类的构造器中,可以显式的使用"this(形参列表)"方式,调用本类中指定的其他构造器
② 构造器中不能通过"this(形参列表)“方式调用自己
③ 如果一个类中有n个构造器,则最多有 n - 1构造器中使用了"this(形参列表)”
④ 规定:"this(形参列表)“必须声明在当前构造器的首行
⑤ 构造器内部,最多只能声明一个"this(形参列表)”,用来调用其他的构造器
5.2、关键字:package/import
5.2.1、package的使用
- 1.为了更好的实现项目中类的管理,提供包的概念
- 2.使用package声明类或接口所属的包,声明在源文件的首行
- 3.包,属于标识符,遵循标识符的命名规则、规范(xxxyyyzzz)、“见名知意”
- 4.每"."一次,就代表一层文件目录。
5.2.2、JDK中的主要包介绍:
1、java.lang:包含一些常用类,如String、Math、Integer、System和Thread。
2、java.net:包含执行与网络相关的操作的类和接口。
3、java.io:提供多种输入/输出功能的类。
4、java.util:包含一些使用工具类。
5、java.text:包含一些java格式化相关的类。
6、java.sql:包含java进行JDBC数据库编程的相关类/接口。
7、java.awt:包含了构成抽象窗口工具集的多个类。
5.2.2、import的使用
import:导入
- 在源文件中显式的使用import结构导入指定包下的类、接口
- 声明在包的声明和类的声明之间
- 如果需要导入多个结构,则并列写出即可
- 可以使用"xxx.*"的方式,表示可以导入xxx包下的所结构
- 如果使用的类或接口是java.lang包下定义的,则可以省略import结构
- 如果使用的类或接口是本包下定义的,则可以省略import结构
- 如果在源文件中,使用了不同包下的同名的类,则必须至少一个类需要以全类名的方式显示。
- 使用"xxx.*"方式表明可以调用xxx包下的所结构。但是如果使用的是xxx子包下的结构,则仍需要显式导入
- import static:导入指定类或接口中的静态结构:属性或方法。
5.3、关键字:super
- super 关键字可以理解为:父类的
- 可以用来调用的结构:属性、方法、构造器
5.3.1、super调用属性、方法:
- 1 我们可以在子类的方法或构造器中。通过使用"super.属性"或"super.方法"的方式,显式的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略"super."
- 2 特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的使用"super.属性"的方式,表明调用的是父类中声明的属性。
- 3 特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的使用"super.方法"的方式,表明调用的是父类中被重写的方法。
5.3.2、super调用构造器:
- 1 我们可以在子类的构造器中显式的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器
- 2 "super(形参列表)"的使用,必须声明在子类构造器的首行!
- 3 我们在类的构造器中,针对于"this(形参列表)"或"super(形参列表)"只能二一,不能同时出现
- 4 在构造器的首行,没显式的声明"this(形参列表)“或"super(形参列表)”,则默认调用的是父类中空参的构造器:super()
- 5 在类的多个构造器中,至少一个类的构造器中使用了"super(形参列表)",调用父类中的构造器
5.4、关键字:static
static可以用来修饰的结构:主要用来修饰类的内部结构,如属性、方法、代码块、内部类
5.4.1、static修饰属性:静态变量(或类变量)
- 1 属性,是否使用static修饰,又分为:静态属性 vs 非静态属性(实例变量)
实例变量:我们创建了类的多个对象,每个对象都独立的拥一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改。
静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的。- 2 static修饰属性的其他说明:
① 静态变量随着类的加载而加载。可以通过"类.静态变量"的方式进行调用
② 静态变量的加载要早于对象的创建。
③ 由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中。- 3 静态属性举例:System.out; Math.PI;
5.4.2、静态变量内存解析:
5.4.3、static修饰方法:静态方法、类方法
① 随着类的加载而加载,可以通过"类.静态方法"的方式进行调用
② 静态方法中,只能调用静态的方法或属性
非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性
5.4.4、使用举例
举例一:Arrays、Math、Collections等工具类
举例二:
class Circle{
private double radius;
private int id;//自动赋值
public Circle(){
id = init++;
total++;
}
public Circle(double radius){
this();
// id = init++;
// total++;
this.radius = radius;
}
private static int total;//记录创建的圆的个数
private static int init = 1001;//static声明的属性被所对象所共享
public double findArea(){
return 3.14 * radius * radius;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
public int getId() {
return id;
}
public static int getTotal() {
return total;
}
}
5.5、关键字:final
final可以用来修饰:类、方法、变量
- 1 final 用来修饰一个类:此类不能被其他类所继承。
比如:String类、System类、StringBuffer类- 2 final 用来修饰方法:表明此方法不可以被重写
比如:Object类中getClass();- 3 final 用来修饰变量:此时的"变量"就称为是一个常量
①final修饰属性:可以考虑赋值的位置:显式初始化、代码块中初始化、构造器中初始化
② final修饰局部变量: 尤其是使用final修饰形参时,表明此形参是一个常量。当我们调用此方法时,给常量形参赋一个实参。一旦赋值以后,就只能在方法体内使用此形参,但不能进行重新赋值。
static final 用来修饰属性:全局常量
5.6、关键字:abstract
abstract可以用来修饰:类、方法
5.6.1、abstract修饰类:抽象类
- 此类不能实例化
- 抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)
- 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作 —>抽象的使用前提:继承性
5.6.2、abstract修饰方法:抽象方法
- 抽象方法只方法的声明,没方法体
- 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。
- 若子类重写了父类中的所的抽象方法后,此子类方可实例化
若子类没重写父类中的所的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰
5.6.3、注意点
- 1.abstract不能用来修饰:属性、构造器等结构
- 2.abstract不能用来修饰私有方法、静态方法、final的方法、final的类
5.6.4、abstract的应用举例:
举例一:
public abstract class Vehicle{
public abstract doulbe calcFuelEfficiency();//计算燃料效率的抽象方法
public abstract doulbe calcTripDistance();//计算行驶距离的抽象方法
}
public class Truck extends Vehicle{
public doulbe calcFuelEfficiency();{//写出计算卡车的燃料距离的具体方法}
public doulbe calcTripDistance();{//写出计算卡车行驶距离的具体方法}
}
public class RiverBarge extends Vehicle{
public doulbe calcFuelEfficiency();{//写出计算驳船的燃料距离的具体方法}
public doulbe calcTripDistance();{//写出计算驳船行驶距离的具体方法}
}
举例二:
abstract class GeometricObject{
public abstract double findArea();
}
class Circle extends GeometricObject{
private double radius;
public double findArea(){
return 3.14 * radius * radius;
};
}
5.7、关键字:interface
5.7.1、使用说明
- 1.接口使用interface来定义
- 2.Java中,接口和类是并列的两个结构
- 3.如何定义接口:定义接口中的成员
3.1 JDK7及以前:只能定义全局常量和抽象方法
全局常量:public static final的.但是书写时,可以省略不写
抽象方法:public abstract的
3.2 JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法(略- 4.接口中不能定义构造器的!意味着接口不可以实例化
- 5.Java开发中,接口通过让类去实现(implements)的方式来使用.
如果实现类覆盖了接口中的所抽象方法,则此实现类就可以实例化
如果实现类没覆盖接口中所的抽象方法,则此实现类仍为一个抽象类- 6.Java类可以实现多个接口 —>弥补了Java单继承性的局限性
格式:class AA extends BB implements CC,DD,EE- 7.接口与接口之间可以继承,而且可以多继承
- 8.接口的具体使用,体现多态性
- 9.接口,实际上可以看做是一种规范
5.7.2、举例:
class Computer{
public void transferData(USB usb){//USB usb = new Flash();
usb.start();
System.out.println("具体传输数据的细节");
usb.stop();
}
}
interface USB{
//常量:定义了长、宽、最大最小的传输速度等
void start();
void stop();
}
class Flash implements USB{
@Override
public void start() {
System.out.println("U盘开启工作");
}
@Override
public void stop() {
System.out.println("U盘结束工作");
}
}
class Printer implements USB{
@Override
public void start() {
System.out.println("打印机开启工作");
}
@Override
public void stop() {
System.out.println("打印机结束工作");
}
}
5.7.3、Java8中关于接口的新规范
- 1.接口中定义的静态方法,只能通过接口来调用。
- 2.通过实现类的对象,可以调用接口中的默认方法。
如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法- 3.如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,那么子类在没重写此方法的情况下,默认调用的是父类中的同名同参数的方法。–>类优先原则
- 4.如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,
那么在实现类没重写此方法的情况下,报错。–>接口冲突。
这就需要我们必须在实现类中重写此方法- 5.如何在子类(或实现类)的方法中调用父类、接口中被重写的方法
public void myMethod(){
method3();//调用自己定义的重写的方法
super.method3();//调用的是父类中声明的
//调用接口中的默认方法
CompareA.super.method3();
CompareB.super.method3();
}
六、第六章——异常
- 1.异常:在Java语言中,将程序执行中发生的不正常情况称为“异常”。 (开发过程中的语法错误和逻辑错误不是异常)
- 2.Java程序在执行过程中所发生的异常事件可分为两类:
①Error:Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError和OOM。一般不编写针对性的代码进行处理。
②Exception: 其它因编程错误或偶然的外在因素导致的一般性问题,可以使
用针对性的代码进行处理。例如:空指针访问、试图读取不存在的文件、网络连接中断、数组角标越界。
1、异常体系及异常分类
1.1、异常体系
java.lang.Throwable
|-----java.lang.Error:一般不编写针对性的代码进行处理。
|-----java.lang.Exception:可以进行异常的处理
|------编译时异常(checked)
|-----IOException
|-----FileNotFoundException
|-----ClassNotFoundException
|------运行时异常(unchecked,RuntimeException)
|-----NullPointerException
|-----ArrayIndexOutOfBoundsException
|-----ClassCastException
|-----NumberFormatException
|-----InputMismatchException
|-----ArithmeticException
1.2、异常分类
1.2.1、运行时异常
是指编译器不要求强制处置的异常。一般是指编程时的逻辑错误。对于这类异常,可以不做处理。
1.2.2、编译时异常
是指编译器要求必须处理的异常。
2、常见的异常
2.1、常见的异常
java.lang.RuntimeException
ClassCastException
ArrayIndexOutOfBoudsException
NullPointerException
ArithmeticException
NumberFormatException
InputMismatchException
java.io.IOException
FiletNotFoundExcption
EOFException
java.lang.ClassNotFoutdException
java.lang.InterruptedException
java.io.RuntimeException
java.sql.SQLException
2.2、代码举例
2.2.1、ArrayIndexOutOfBoundsException
public static void main(String[] args) {
String friends[] = { "lisa", "bily", "kessy" };
for (int i = 0; i < 5; i++) {
System.out.println(friends[i]); // friends[4]?
}
System.out.println("\nthis is the end");
}
/*
运行结果:
lisa
bily
kessy
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException:3
atcom.atguigu.Exception.java.ArrayIndexOutOfBoundsException.main(ArrayIndexOutOfBoundsException.java:11)
*/
2.2.2、NullPointerException
public class NullRef {
int i = 1;
public static void main(String[] args) {
NullRef t = new NullRef();
t = null;
System.out.println(t.i);
}
}
/*
运行结果:
Exception in thread "main" java.lang.NullPointerException
at com.atguigu.Exception.java.NullRef.main(NullRef.java:12)
*/
2.2.3、ArithmeticException
public class ArithmeticExceptionTest {
int x;
public static void main(String[] args) {
int y;
ArithmeticExceptionTest c=new ArithmeticExceptionTest();
y=3/c.x;
System.out.println("program ends ok!");
}
}
/*
运行结果:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.atguigu.Exception.java.ArithmeticExceptionTest.main(ArithmeticExceptionTest.java:12)
*/
2.2.4、ClassCastException
public class ClassCastExceptionTest {
public static void main(String[] args) {
Object obj = new ClassCastExceptionTest();
String str = (String)obj;
}
}
/*
运行结果;
Exception in thread "main" java.lang.ClassCastException: com.atguigu.Exception.java.ClassCastExceptionTest cannot be cast to java.lang.String
at com.atguigu.Exception.java.ClassCastExceptionTest.main(ClassCastExceptionTest.java:10)
*/
3、异常处理
3.1、java异常处理的抓抛模型
- 过程一:“抛”:程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象。 并将此对象抛出。一旦抛出对象以后,其后的代码就不再执行。
关于异常对象的产生:① 系统自动生成的异常对象 ② 手动的生成一个异常对象,并抛出(throw)- 过程二:“抓”:可以理解为异常的处理方式:① try-catch-finally ② throws
3.2、异常处理方式一:try-catch-finally
3.2.1、使用说明
try{
//可能出现异常的代码
}catch(异常类型1 变量名1){
//处理异常的方式1
}catch(异常类型2 变量名2){
//处理异常的方式2
}catch(异常类型3 变量名3){
//处理异常的方式3
}
....
finally{
//一定会执行的代码
}
- 说明:
- 1.finally是可选的。
- 2.finally中声明的是一定会被执行的代码。即使catch中又出现异常了,try中return语句,catch中return语句等情况。
- 3.像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己手动的进行资源的释放。此时的资源释放,就需要声明在finally中。
- 4.使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配
- 5.一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出当前的try-catch结构(在没写finally的情况。继续执行其后的代码
- 6.catch中的异常类型如果没子父类关系,则谁声明在上,谁声明在下无所谓。
catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面。否则,报错- 7.常用的异常对象处理的方式: ① String getMessage() ② printStackTrace()
- 8.在try结构中声明的变量,再出了try结构以后,就不能再被调用
- 9.try-catch-finally结构可以嵌套
3.2.2、代码举例
1、ArrayIndexOutOfBoundsException的处理
public static void main(String[] args) {
String friends[] = { "lisa", "bily", "kessy" };
try {
for (int i = 0; i < 5; i++) {
System.out.println(friends[i]);
}
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("index err");
}
System.out.println("this is the end");
}
/*
运行结果:
lisa
bily
kessy
index err
this is the end
*/
2、ArithmeticException的处理
public class ArithmeticExceptionTest {
int x;
public static void main(String[] args) {
int y;
ArithmeticExceptionTest c = new ArithmeticExceptionTest();
try {y = 3 / c.x;
} catch (ArithmeticException e) {
System.out.println("divide by zero error!");
}
System.out.println("program ends ok!");
}
}
/*
运行结果:
divide by zero error!
program ends ok!
*/
3.3、异常处理方式二:“throws + 异常类型”
"throws + 异常类型"写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。
一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。异常代码后续的代码,就不再执行!
代码举例:
public class ThrowsTest{
public static void main(String[] args) throws InterruptedException{
snow();
}
public static void snow() throws InterruptedException{
for(int i =0;i<5;i++){
System.out.println(i);
Thread.sleep(1000);//睡眠1s
}
}
}
/*
运行结果:
0
1
2
3
4
*/
3.4、两种方式的区别及如何选择
3.4.1、区别
try-catch-finally:真正的将异常给处理掉了。
throws的方式只是将异常抛给了方法的调用者。并没真正将异常处理掉。
3.4.2、如何选择
- 1.如果父类中被重写的方法没throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法中异常,必须使用try-catch-finally方式处理。
- 2.执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。我们建议这几个方法使用throws的方式进行处理。而执行的方法a可以考虑使用try-catch-finally方式进行处理。
4、手动抛出异常对象
4.1、使用说明
在程序执行中,除了自动抛出异常对象的情况之外,我们还可以手动的throw一个异常类的对象。
4.2、代码举例
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 + "]";
}
}
5、自定义异常类
5.1、如何自定义一个异常类?
- 1.继承于现的异常结构:RuntimeException 、Exception
- 2.提供全局常量:serialVersionUID
- 3.提供重载的构造器
5.2、代码举例
public class MyException extends Exception{
static final long serialVersionUID = -7034897193246939L;
public MyException(){
}
public MyException(String msg){
super(msg);
}
}