一 java概述
1常用的dos命令
2java程序编写和执行过程
步骤1:编写,将java代码编写在.java结尾的源文件中
步骤2:编译,针对.java源文件进行编译操作。格式:javac 源文件名.java
步骤3:执行,针对编译后生成的字节码文件进行解释运行。格式: java 字节码文件名
3输出语句
System.out.println("hello,world!!中国");
System.out.print("hello,world!!中国");
println自动换行
print不换行
4java程序入口
public static void main(String[] args){
}
5源文件名与类名
如果这个类不是public,那么源文件名可以和类名不一致。但是不便于代码维护。
如果这个类是public,那么要求源文件名必须与类名一致。否则编译报错。
一个源文件中可以有多个类,编译后会生成多个.class字节码文件。
但是一个源文件只能有一个public的类。
6注释
单行注释
//这是输出语句
多行注释
/*
这是程序入口,多行注释
*/
多行注释不生成在字节码文件中
文档注释
/**
这是第一个java程序
@author cxxk
@version 1.0
*/
文档注释内容可以被JDK提供的工具 javadoc 所解析,生成一套以网页文件形式体现的该程序的说明文档。
7什么是JDK,JRE
-
JDK (
J
avaD
evelopmentK
it):是Java程序开发工具包,包含JRE
和开发人员使用的工具。 -
JRE (
J
avaR
untimeE
nvironment) :是Java程序的运行时环境,包含JVM
和运行时所需要的核心类库
。
二变量与运算符
2.1变量
2.1.1整型数据
byte ,short,int,long
整型常量默认为int型
long a=123456l
long b=123456L
long型变量数据后面要加小写l或者大写L
2.1.2浮点型数据
float,double
浮点型常量默认为double型
float f1 =1.123f
float变量后面需要加F或f后缀
要求精度更高时,使用BigDecimal类
2.1.3字符类型
char 字符类型为单引号
表示形式1:
char c1 = 'a'
char c2 = '1'
char 后面只能跟一个字符
表示形式2:
char c3 = '\u0023'
跟一个UNICODE编码集
表示形式3:
char c4 = '\n'
表示一个转义字符
表示形式4:
char c5 = 43
表示一个asc码
2.1.4布尔类型
boolean
boolean b1 = true
boolean b2 = false
true 和 false 为小写
2.1.5数据类型之间的自动类型提升
char,byte,short -->int-->long-->float-->double
char,byte,short两两之间运算会自动转换为int型
long l1 = 123
这样写可以,因为常量123默认为int型,自动转换为long型
long l2 = 123123123123
这样写不行,常量123123123123默认为int型,超出了int所能表示的最大范围
float = 12.2
这样不行,浮点型常量12.2默认为double型
所以float后面必须加f后缀
2.1.6强制类型转换
long l = 12
int i = (int) l
double d = 1.1
int i = (int) d
语法:在转换的数字或变量前加(转换类型)
2.1.7string类
string是一个类,不是基本数据类型,string用" "表示
string str = "abc"
string是一个类,用双引号""
string和其他基本数据类型的连接
string str1 = str + 1 + 1.2 + 'a'
输出结果 abc11.2a
string 与其他基本数据类型的连接后还是string 类型
string str2 = "10"
int i = (int) str2
这么写是错误的,与python不一样
如果要将str2转化为int型,这么写:
int i1 = Integer.parseInt(str2)
2.2运算符
2.2.1算术运算符之自增,自减
i++,++i
i--,--i
int i = 1
int m = ++i
int n = i++
m = 2
n = 1
++i 先自增再赋值
i++ 先赋值再运算
自增不等于+1,自增不改变数据类型
byte b = 1
b = b + 1
编译报错,1为int型
b++
b = 2
2.2.2赋值运算符之+=,-=,*=,/=
+=,-=,*=,/=同自增自减,不改变原有数据类型
赋值语句bool类型
boolean x = false
if( x = true ){
}
条件满足
boolean y = true
if (y = false){
}
条件不满足
2.2.3逻辑运算符
& 和 &&的区别
boolean b1 = false
int x1 = 10
if (b1)&(x1++>0){
}
x1 = 11
&左边为false,仍要执行右边的操作
boolean b2 = flase
int x2 = 10
if (b2)&&(x2++>0){
}
x2 = 10
&&左边为false,不再执行右边的操作
2.2.4条件运算符
(条件表达式)? 表达式1 :表达式2
条件表达式的结果为true,返回表达式1的结果
条件表达式的结果为false,返回表达式2的结果
int i = (1==2 ? 100 : 200);
System.out.println(i);//200
boolean marry = false;
System.out.println(marry ? "已婚" : "未婚" );//未婚
三流程控制语句
3.1分支结构
3.1.1scanner类
步骤1:导包 import java.util.Scanner;
步骤2:提供(或创建)一个Scanner类的实例
步骤3:调用Scanner类中的方法,获取指定类型的变量 (nextXxx())
步骤4:关闭资源,调用Scanner类的close()
//步骤1:导包 import java.util.Scanner;
import java.util.Scanner;
//步骤2:提供(或创建)一个Scanner类的实例
Scanner scan = new Scanner(System.in);
//scan为实例化对象名
//步骤3:调用Scanner类中的方法,获取指定类型的变量
String name = scan.next();
int age = scan.nextInt();
double weight = scan.nextDouble();
boolean isSingle = scan.nextBoolean();
char gender = scan.next().charAt(0);
//步骤4:关闭资源,调用Scanner类的close()
scan.close();
注意,没有提供获取char类型变量的方法。需要使用next().charAt(0)
3.1.2获取随机数
1. 可以使用Java提供的API:Math类的random()
2. random()调用以后,会返回一个[0.0,1.0)范围的double型的随机数
3. 需求1:获取一个[0,100]范围的随机整数?
需求2:获取一个[1,100]范围的随机整数?
4. 需求:获取一个[a,b]范围的随机整数?
(int)(Math.random() * (b - a + 1)) + a
double d1 = Math.random();
int num1 = (int)(Math.random() * 101); //[0.0,1.0) --> [0.0,101.0) --->[0,100]
int num2 = (int)(Math.random() * 100) + 1; //[0.0,1.0) --> [0.0,100.0) --->[0,99] ---> [1,100]
3.1.3 if-else语句
if(score == 100){
System.out.println("奖励一辆跑车");
}else if(score > 80 && score <= 99){ //错误的写法:}else if(80 < score <= 99){
System.out.println("奖励一辆山地自行车");
}else if(score >= 60 && score <= 80){
System.out.println("奖励环球影城玩一日游");
}
//else{
// System.out.println("胖揍一顿");
//}
3.1.4switch-case语句
int num = 1;
switch(num){
case 0:
System.out.println("zero");
break;
case 1:
System.out.println("one");
break;
case 2:
System.out.println("two");
break;
case 3:
System.out.println("three");
break;
default:
System.out.println("other");
//break;
}
如果没有break会一直往下执行
3.2循环结构
3.2.1for循环
for(;;){
}
for循环一般用于知道循环次数
3.2.2while循环
while(){
}
while循环一般用于不知道循环次数
3.2.3do-while循环
do{
}while()
do-while循环至少进行一次
3.2.4break和continue的使用
break跳出当前循环
continue跳出当次循环
四数组
4.1一维数组
4.1.1一维数组的声明
4.1.2一维数组的初始化
int[] arr = new int[]{1,2,3};
String[] str = new String[]{"cxk","cln","fcc"};
静态初始化,初始化和赋值在一起
int[] arr = new int[3];
String[] str = new String[5];
arr[0] = 1;
arr[1] = 2;
动态初始化,初始化和赋值分开
4.1.3一维数组的长度
int[] arr = new int[]{1,2,3};
int length = arr.length;//3
数组的length属性代表了数组的长度
4.1.4一维数组的遍历
int[] arr = new int[]{1,2,3};
for(int i =0;i<arr.length;i++){
System.out.println(arr[i]);
}
4.1.5一维数组的默认值
动态初始化之后,整型数组默认值为0,浮点型默认值为0.0,布尔型默认为false,引用型默认为null,字符型默认为asc编码中的0
4.1.6一维数组的内存结构
int[] arr = new int[]{1,2,3};
在虚拟机栈中存放的是变量名arr,arr后面跟着是一个地址,
在堆中存放的是数据1,2,3,一片连续空间存放
sout(arr)输出的是地址
如果想要在堆中开辟一片新空间,必须new一下
4.2二维数组
4.2.1二维数组的声明
4.2.2二维数组的初始化
int[][] arr = new int[][]{{1,2,3},{4,5,6},{7,8,9}};
静态初始化
int[][] arr = new int[3][3];
int[][] arr = new int[3][];
动态初始化
4.2.3二维数组的长度
int[][] arr = new int[][]{{1,2,3},{4,5,6},{7,8,9}};
arr.length;//二维数组的长度,有几个一维数组
arr[0].length//具体某一个一维数组的长度
arr[1].length
arr[2].length
4.2.4二维数组的遍历
int[][] arr = new int[][]{{1,2,3},{4,5,6},{7,8,9}};
for(int i = 0;i<arr.length;i++){
for(int j = 0;j<arr[i].length;j++){
sout(arr[i][j]);
}
}
4.2.5二维数组的默认值
4.2.6二维数组的内存结构
int[][] arr = new int[3][4];
虚拟机栈中存放arr,arr后面跟着一个地址,地址指向一个三行的数组,
该三行的数组每个元素都是一个地址,地址各自指向一个四行的数组,四行数组默认值为0
int[][] arr = new int[3][];
虚拟机栈中存放arr,arr后面跟着一个地址,地址指向一个三行的数组,
该三行数组的每个元素都是null
sout(arr)
sout(arr[0])
sout(arr[1])
sout(arr[2])
这些输出的均为地址
4.3Arrays工具类
导包
import java.util.Arrays;
判断两个数组是否相等
boolean i = Arrays.equals(arr1,arr2);
输出数组
sout(Arrays.toString(arr1);
二分查找
int index = Arrays.binarySearch(arr,12);
快排
Arrays.sort(arr);
填充
Arrays.fill(arr1,5);
五面向对象基础
5.1类和对象
5.1.1类和对象的概念
类:抽象的,概念上的定义
对象:具体的,类的一个一个的实例
面向对象完成具体功能的操作的三步流程:
步骤1:创建类,并设计类的内部成员(属性、方法)
步骤2:创建类的对象。比如:Phone p1 = new Phone();
步骤3:通过对象,调用其内部声明的属性或方法,完成相关的功能
5.1.2对象的内存解析
jvm的划分:
-
虚拟机栈:以栈帧为基本单位,有入栈和出栈操作;每个栈帧入栈操作对应一个方法的执行;方法内的局部变量会存储在栈帧中。
-
堆空间:new 出来的结构(数组、对象):① 数组,数组的元素在堆中 ② 对象的成员变量在堆中。
5.2成员变量(属性)
5.2.1成员变量vs局部变量
5.3方法
5.3.1方法的声明
语法格式:
5.3.2方法的重载
5.3.3可变个数形参的方法
格式:(int ... args)
public int sum(int... nums){
int sum = 0;
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
}
return sum;
}
5.3.4方法的参数传递机制
如果形参是基本数据类型的变量,则将实参保存的数据值赋给形参。
如果形参是引用数据类型的变量,则将实参保存的地址值赋给形参。
5.4封装性
Java规定了4种权限修饰,分别是:private、缺省、protected、public。
体现:
> 场景1:私有化(private)类的属性,提供公共(public)的get和set方法,对此属性进行获取或修改
> 场景2:将类中不需要对外暴露的方法,设置为private
> 场景3:单例模式中构造器private的了,避免在类的外部创建实例。(放到static关键字后讲)
5.5构造器
语法格式:
[修饰符] class 类名{
[修饰符] 构造器名(){
// 实例初始化代码
}
[修饰符] 构造器名(参数列表){
// 实例初始化代码
}
}
六面向对象进阶
6.1this
this用在构造器中:
-
this():调用本类的无参构造器
-
this(实参列表):调用本类的有参构造器
-
this()和this(实参列表)只能声明在构造器首行。
6.2继承
class A extends B{}
Java中继承性的特点
-
局限性:类的单继承性。后续我们通过类实现接口的方式,解决单继承的局限性。
-
支持多层继承,一个父类可以声明多个子类。
6.3方法的重写
方法的重写:
-
前提:类的继承关系
-
子类对父类中同名同参数方法的覆盖、覆写。
6.4super
-
super可以调用的结构:属性、方法;构造器
-
super:父类的
-
super调用父类的属性、方法:
-
如果子父类中出现了同名的属性,此时使用super.的方式,表明调用的是父类中声明的属性。
-
子类重写了父类的方法。如果子类的任何一个方法中需要调用父类被重写的方法时,需要使用super.
-
-
super调用构造器:
-
在子类的构造器中,首行要么使用了"this(形参列表)",要么使用了"super(形参列表)"。
-
如果没写,默认super();
-
6.5多态
-
多态的使用:虚拟方法调用。“编译看左边,运行看右边”。属性,不存在多态性。
-
多态的弊端:不能调用子类特有的属性和方法,若想调用子类特有的属性和方法,就需要向下转型
-
多态的逆过程:向下转型,使用强转符()。
-
为了避免出现强转时的ClassCastException,建议()之前使用instanceOf进行判断。
-
public void feed(Pet pet){
pet.eat();//pet实际引用的对象类型不同,执行的eat方法也不同
}
feed(dog);
feed(cat);
用父类当做方法形参,实际调用时传入的是子类
对象a instanceOf 类型A
6.6object类
类 java.lang.Object
是类层次结构的根类,即所有其它类的父类。每个类都使用 Object
作为超类。
如果一个类没有特别指定父类,那么默认则继承自Object类。
6.6.1toString()
-
Object中toString()调用后,返回当前对象所属的类和地址值。
-
开发中常常重写toString(),用于返回当前对象的属性信息。
6.6.2equals()
-
== 既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型就是比较内存地址
-
equals的话,它是属于java.lang.Object类里面的方法,如果该方法没有被重写过默认也是==;我们可以看到String等类的equals方法是被重写过的,而且String类在日常开发中用的比较多,久而久之,形成了equals是比较值的错误观点。
-
具体要看自定义类里有没有重写Object的equals方法来判断。
-
通常情况下,重写equals方法,会比较类中的相应属性是否都相等。
6.6.3clone()
创建一个新的对象,返回为Object类型,强转
6.6.4getClass()
获取对象的运行时类型
6.7子类实例化的过程
七面向对象高级
7.1抽象类,抽象方法,abstract
public abstract class Animal {
public abstract void eat();
}
抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
抽象类中,也有构造方法,是供子类创建对象时,初始化父类成员变量使用的。
抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类。
abstract修饰类:
> 此类称为抽象类
> 抽象类不能实例化。
> 抽象类中是包含构造器的,因为子类对象实例化时,需要直接或间接的调用到父类的构造器。
> 抽象类中可以没有抽象方法。反之,抽象方法所在的类,一定是抽象类。
abstract修饰方法:
> 此方法即为抽象方法
> 抽象方法只有方法的声明,没有方法体。
> 抽象方法其功能是确定的(通过方法的声明即可确定),只是不知道如何具体实现(体现为没有方法体)
> 子类必须重写父类中的所有的抽象方法之后,方可实例化。否则,此子类仍然是一个抽象类。
7.2 接口interface
7.2.1概述
接口,用来定义一组规范、一种标准。
接口的本质是契约、标准、规范,就像我们的法律一样。制定好后大家都要遵守
7.2.2接口的声明
[修饰符] interface 接口名{
//接口的成员列表:
// 公共的静态常量
// 公共的抽象方法
// 公共的默认方法(JDK1.8以上)
// 公共的静态方法(JDK1.8以上)
// 私有方法(JDK1.9以上)
}
7.2.3接口的使用规则
1、类实现接口(implements)
接口不能创建对象,但是可以被类实现(implements
,类似于被继承)。
【修饰符】 class 实现类 implements 接口{
// 重写接口中抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写
// 重写接口中默认方法【可选】
}
【修饰符】 class 实现类 extends 父类 implements 接口{
// 重写接口中抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写
// 重写接口中默认方法【可选】
}
2、接口的多实现(implements)
之前学过,在继承体系中,一个类只能继承一个父类。而对于接口而言,一个类是可以实现多个接口的,这叫做接口的多实现
。并且,一个类能继承一个父类,同时实现多个接口。
【修饰符】 class 实现类 implements 接口1,接口2,接口3。。。{
// 重写接口中所有抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写
// 重写接口中默认方法【可选】
}
【修饰符】 class 实现类 extends 父类 implements 接口1,接口2,接口3。。。{
// 重写接口中所有抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写
// 重写接口中默认方法【可选】
}
3、接口的多继承(extends)
一个接口能继承另一个或者多个接口,接口的继承也使用 extends
关键字,子接口继承父接口的方法。
4、接口与实现类对象构成多态引用
实现类实现接口,类似于子类继承父类,因此,接口类型的变量与实现类的对象之间,也可以构成多态引用。通过接口类型的变量调用方法,最终执行的是你new的实现类对象实现的方法体。
5、使用接口的静态成员
接口不能直接创建对象,但是可以通过接口名直接调用接口的静态方法和静态常量。
package com.atguigu.interfacetype; public class TestUSB3 { public static void main(String[] args) { //通过“接口名.”调用接口的静态方法 (JDK8.0才能开始使用) USB3.show(); //通过“接口名.”直接使用接口的静态常量 System.out.println(USB3.MAX_SPEED); } }
7.2.4jdk8中接口新特性
在JDK8.0 时,接口中允许声明默认方法
和静态方法
:
默认方法可以看为类中普通的方法
(3)公共的默认的方法:其中public 可以省略,建议保留,但是default不能省略
(4)公共的静态的方法:其中public 可以省略,建议保留,但是static不能省略
(1)类优先原则
当一个类,既继承一个父类,又实现若干个接口时,父类中的成员方法与接口中的抽象方法重名,子类就近选择执行父类的成员方法。
2)接口冲突(左右为难)
-
当一个类同时实现了多个父接口,而多个父接口中包含方法签名相同的默认方法时,怎么办呢?
选择保留其中一个,通过“接口名.super.方法名
"的方法选择保留哪个接口的默认方法。
7.2.5抽象类和接口的对比
7.3内部类
7.3.1是什么,为什么?
将一个类A定义在另一个类B里面,里面的那个类A就称为`内部类(InnerClass)`,类B则称为`外部类(OuterClass)`。
具体来说,当一个事物A的内部,还有一个部分需要一个完整的结构B进行描述,而这个内部的完整的结构B又只为外部事物A
提供服务,不在其他地方单独使用,那么整个内部的完整结构B最好使用内部类。
比如:
Thread类内部声明了State类,表示线程的生命周期
HashMap类中声明了Node类,表示封装的key和value
7.3.2内部类的分类
> 成员内部类:直接声明在外部类的里面。
> 使用static修饰的:静态的成员内部类
> 不使用static修饰的:非静态的成员内部类
> 局部内部类:声明在方法内、构造器内、代码块内的内部类
> 匿名的局部内部类
> 非匿名的局部内部类
7.3.3成员内部类的理解
> 从类的角度看:
- 内部可以声明属性、方法、构造器、代码块、内部类等结构
- 此内部类可以声明父类,可以实现接口
- 可以使用final修饰
- 可以使用abstract修饰
> 从外部类的成员的角度看:
- 在内部可以调用外部类的结构。比如:属性、方法等
- 除了使用public、缺省权限修饰之外,还可以使用private、protected修饰
- 可以使用static修饰
-
外部类访问成员内部类的成员,需要“内部类.成员”或“内部类对象.成员”的方式
-
成员内部类可以直接使用外部类的所有成员,包括私有的数据
-
当想要在外部类的静态成员部分使用内部类时,可以考虑内部类声明为静态的
外部类名.静态内部类名 变量 = new 外部类名.静态内部类名();
变量.非静态方法()
实例化非静态内部类
外部类名 变量1 = new 外部类();
外部类名.非静态内部类名 变量2 = 变量1.new 非静态内部类名();
变量2.非静态方法();
7.3.4匿名局部内部类
new 父类([实参列表]){
重写方法...
}
new 父接口(){
重写方法...
}
interface A{
void method();
}
public class Test{
public static void test(A a){
a.method();
}
public static void main(String[] args){
test(new A(){
@Override
public void method() {
System.out.println("aaaa");
}
});
}
}
匿名内部类的对象作为实参
7.4枚举类
列出的实例系统会自动添加 public static final 修饰。
public enum SeasonEnum {
SPRING("春天","春风又绿江南岸"),
SUMMER("夏天","映日荷花别样红"),
AUTUMN("秋天","秋水共长天一色"),
WINTER("冬天","窗含西岭千秋雪");
private final String seasonName;
private final String seasonDesc;
private SeasonEnum(String seasonName, String seasonDesc) {
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
}
调用枚举类对象:SeansonEnum.SPRING
7.5包装类
7.5.1基本数据类型对应的包装类类型
byte -> Byte
short -> Short
int -> Integer
long -> Long
float -> Float
double ->Double
char -> Character
boolean -> Boolean
7.5.2自动装箱,自动拆箱
缓冲池:
自动装箱时优先调用缓冲池里的,如果是new出来的就在堆里
自动装箱:基本数据类型--->包装类
自动拆箱:包装类---->基本数据类型
基本数据类型---->包装类,valueOf
Integer i1 = Integer.valueOf(5);
Double d1 = Double.valueOf(2.2);
7.5.3String 与 基本数据类型、包装类之间的转换
String转换为基本数据类型
调用包装类的静态方法:parseXxx()
String s1 = "2";
int i = Integer.parseInt(s1);
String s2 = "false";
boolean b = Boolean.parseBoolean(s2);
基本数据类型转换为String
String.valueOf( xxx )
String s = String.valueOf(2);
基本数据类型+""
7.5.4包装类API
Double.compare(double d1, double d2)
Integer.compare(int x, int y)
7.6注解
7.6.1注解的理解
注解(Annotation)是从`JDK5.0`开始引入,以“`@注解名`”在代码中存在。
> Annotation 可以像修饰符一样被使用,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明。
还可以添加一些参数值,这些信息被保存在 Annotation 的 “name=value” 对中。
> 注解可以在类编译、运行时进行加载,体现不同的功能。
7.6.2Java基础涉及到的三个常用注解
`@Override`: 限定重写父类方法,该注解只能用于方法
`@Deprecated`: 用于表示所修饰的元素(类,方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择
`@SuppressWarnings`: 抑制编译器警告
7.6.3元注解
元注解:对现有的注解进行解释说明的注解。
用在注解定义上面
(1)@Target:用于描述注解的使用范围
可以通过枚举类型ElementType的10个常量对象来指定
TYPE,METHOD,CONSTRUCTOR,PACKAGE.....
(2)@Retention:用于描述注解的生命周期
可以通过枚举类型RetentionPolicy的3个常量对象来指定
SOURCE(源代码)、CLASS(字节码)、RUNTIME(运行时)
唯有RUNTIME阶段才能被反射读取到。
(3)@Documented:表明这个注解应该被 javadoc工具记录。
(4)@Inherited:允许子类继承父类中的注解
7.6.4自定义注解
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String value();
}
7.7单元测试
在libraries中点击加号点击from Maven,输入org.junit.jupiter:junit-jupiter-engine:5.7.2
创建的类名不要为Test
八异常
8.1异常体系
java.lang.Throwable:异常体系的根父类
|---java.lang.Error:错误。Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。
一般不编写针对性的代码进行处理。
|---- StackOverflowError、OutOfMemoryError
|---java.lang.Exception:异常。我们可以编写针对性的代码进行处理。
|----编译时异常:(受检异常)在执行javac.exe命令时,出现的异常。
|----- ClassNotFoundException
|----- FileNotFoundException
|----- IOException
|----运行时异常:(非受检异常)在执行java.exe命令时,出现的异常。
|---- ArrayIndexOutOfBoundsException
|---- NullPointerException
|---- ClassCastException
|---- NumberFormatException
|---- InputMismatchException
|---- ArithmeticException
我们处理的是编译时异常
8.2异常处理
8.2.1 try-catch-finally
try{
...... //可能产生异常的代码
}
catch( 异常类型1 e ){
...... //当产生异常类型1型异常时的处置措施
}
catch( 异常类型2 e ){
...... //当产生异常类型2型异常时的处置措施
}
finally{
...... //无论是否发生异常,都无条件执行的语句
}
> 如果声明了多个catch结构,不同的异常类型在不存在子父类关系的情况下,谁声明在上面,谁声明在下面都可以。
如果多个异常类型满足子父类的关系,则必须将子类声明在父类结构的上面。否则,报错。
> catch中异常处理的方式:
① 自己编写输出的语句。
② printStackTrace():打印异常的详细信息。 (推荐)
③ getMessage():获取发生异常的原因。
> 更深刻的理解:无论try中或catch中是否存在仍未被处理的异常,无论try中或catch中是否存在return语句等,finally中声明的语句都一定要被执行。
8.2.2 throws
public void test() throws 异常类型1,异常类型2,.. {
//可能存在编译时异常
}
如果父类被重写方法的方法签名后面没有 “throws 编译时异常类型”,那么重写方法时,方法签名后面也不能出现“throws 编译时异常类型”。
8.2.3 两种方式的选择
- 如果程序代码中,涉及到资源的调用(流、数据库连接、网络连接等),则必须考虑使用try-catch-finally来处理,保证不出现内存泄漏。
- 如果父类被重写的方法没有throws异常类型,则子类重写的方法中如果出现异常,只能考虑使用try-catch-finally进行处理,不能throws。
- 开发中,方法a中依次调用了方法b,c,d等方法,方法b,c,d之间是递进关系。此时,如果方法b,c,d中有异常,我们通常选择使用throws,而方法a中通常选择使用try-catch-finally。
8.3手动抛出异常 throw
throw new 异常类名(参数);
如果是编译时异常类型的对象,同样需要使用throws或者try...catch处理,否则编译不通过。
8.4自定义异常
(1)要继承一个异常类型
自定义一个编译时异常类型:自定义类继承java.lang.Exception
。
自定义一个运行时异常类型:自定义类继承java.lang.RuntimeException
。
(2)建议大家提供至少两个构造器,一个是无参构造,一个是(String message)构造器。
(3)自定义异常需要提供serialVersionUID
-
自定义的异常只能通过throw抛出。
-
自定义异常最重要的是异常类的名字和message属性。当异常出现时,可以根据名字判断异常类型。比如:
TeamException("成员已满,无法添加");
、TeamException("该员工已是某团队成员");
-
自定义异常对象只能手动抛出。抛出后由try..catch处理,也可以甩锅throws给调用者处理。
class MyException extends Exception {
static final long serialVersionUID = 23423423435L;
private int idnumber;
public MyException(String message, int id) {
super(message);
this.idnumber = id;
}
public int getId() {
return idnumber;
}
}
十常用类和api
10.1String类
10.1.1String类的声明
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence
> final:String是不可被继承的
> Serializable:可序列化的接口。凡是实现此接口的类的对象就可以通过网络或本地流进行数据的传输。
> Comparable:凡是实现此接口的类,其对象都可以比较大小。
10.1.2String类的属性
jdk8中:
private final char value[]; //存储字符串数据的容器
> final : 指明此value数组一旦初始化,其地址就不可变。
jdk9开始:为了节省内存空间,做了优化
private final byte[] value; //存储字符串数据的容器。
10.1.3String实例化方式及构造器
第1种方式:String s1 = "hello";
第2种方式:String s2 = new String("hello");
String s2 = new String("hello");在内存中创建了几个对象? 两个!
一个是堆空间中new的对象。另一个是在字符串常量池中生成的字面量。
* `public String() ` :初始化新创建的 String对象,以使其表示空字符序列。
* `public String(String original)`: 初始化一个新创建的 `String` 对象,使其表示一个与参数相同的字符序列;换句话说,新创建的字符串是该参数字符串的副本。
* `public String(char[] value) ` :通过当前参数中的字符数组来构造新的String。
* `public String(char[] value,int offset, int count) ` :通过字符数组的一部分来构造新的String。
* `public String(byte[] bytes) ` :通过使用平台的**默认字符集**解码当前参数中的字节数组来构造新的String。
* `public String(byte[] bytes,String charsetName) ` :通过使用指定的字符集解码当前参数中的字节数组来构造新的String。
10.1.4String连接,常量池
情况1:常量 + 常量: 结果仍然存储在字符串常量池中,返回此字面量的地址。注:此时的常量可能是字面量,也可能是final修饰的常量
情况2:常量 + 变量 或 变量 + 变量 :都会通过new的方式创建一个新的字符串,返回堆空间中此字符串对象的地址
情况3:调用字符串的intern():返回的是字符串常量池中字面量的地址
(了解)情况4:concat(xxx):不管是常量调用此方法,还是变量调用,同样不管参数是常量还是变量,总之,调用完concat()方法都返回一个新new的对象。
10.1.5String常用方法
10.2StringBuffer和StringBuilder
10.2.1StringBuffer和StringBuilder
> String:不可变的字符序列;底层使用char[] (jdk8及之前),底层使用byte[] (jdk9及之后)
> StringBuffer:可变的字符序列;JDK1.0声明,线程安全的,效率低;底层使用char[] (jdk8及之前),底层使用byte[] (jdk9及之后)
> StringBuilder:可变的字符序列;JDK5.0声明,线程不安全的,效率高;底层使用char[] (jdk8及之前),底层使用byte[] (jdk9及之后)
10.2.2源码分析
针对于StringBuilder来说:
内部的属性有:
char[] value; //存储字符序列
int count; //实际存储的字符的个数
不断的添加,一旦count要超过value.length时,就需要扩容:默认扩容为原有容量的2倍+2。
并将原有value数组中的元素复制到新的数组中。
10.2.3常用方法
增:
append(xx)
删:
delete(int start, int end)
deleteCharAt(int index)
改:
replace(int start, int end, String str)
setCharAt(int index, char c)
查:
charAt(int index)
插:
insert(int index, xx)
长度:
length()
10.2.4开发中的用法
> 如果开发中需要频繁的针对于字符串进行增、删、改等操作,建议使用StringBuffer或StringBuilder替换String.
因为使用String效率低。
> 如果开发中,不涉及到线程安全问题,建议使用StringBuilder替换StringBuffer。因为使用StringBuilder效率高
> 如果开发中大体确定要操作的字符的个数,建议使用带int capacity参数的构造器。因为可以避免底层多次扩容操作,性能更高。
十一集合
十二泛型
十三IO流
13.1File类
File file = new File("d:\\atguigu\\javase\\HelloIO.java")
绝对路径
File file = new File("hello.txt")
相对路径,在单元测试test中,相对路径在当前module下,在main方法中,相对路径在projec下
一些常用方法,见课件
File类中只有新建、删除、获取路径等方法,不包含读写文件的方法。此时需要使用IO流
13.2IO流分类
四个抽象基类
Reader(字符读入),Writer(字符读出),InputSream(字节读入),OutputStream(字节读出)
13.3节点流(FileReader、FileWriter、FileInputSream、FileOutputStream)
节点流,直接作用在数据之上
涉及到流的关闭操作,异常应该使用try-catch-finall操作
13.3.1FileReader、FileWriter
new FileWriter(File file,false)或者new FileWriter(File file)默认为覆盖原文件,
new FileWriter(File file,true)为在原有文件后面添加内容
操作字符,用来处理文本文件
创建文件实例,创建流,具体读写操作,关闭流
1创建File实例
File file = new File("dbcp_utf-8.txt");
File file1 = new File("dbcp_utf-8coopyy.txt");
2创建输入、输出流
FileReader fileReader = new FileReader(file);
FileWriter fileWriter = new FileWriter(file1);
3具体的读写操作
char[] chars = new char[5];
int len;
while ((len=fileReader.read(chars))!=-1){
fileWriter.write(chars,0,len);
}
4关闭输入、输出流
fileWriter.close();
fileReader.close();
13.3.2FileInputSream、FileOutputStream
操作字节,处理音频,视频,图片等非文本文件
1创建File实例
File file = new File("屏幕 2023-12-22 013034.png");
File file1 = new File("屏幕 2023-12-22 013034copy.png");
2创建输入、输出流
FileInputStream fileInputStream = new FileInputStream(file);
FileOutputStream fileOutputStream = new FileOutputStream(file1);
3具体的读写操作
byte[] bytes = new byte[1024];
int len;
while ((len=fileInputStream.read(bytes))!=-1){
fileOutputStream.write(bytes,0,len);
}
4关闭输入、输出流
fileOutputStream.close();
fileInputStream.close();
13.4缓冲流
缓冲流要“套接”在相应的节点流之上,根据数据操作单位可以把缓冲流分为:
字节缓冲流:BufferedInputStream
,BufferedOutputStream
字符缓冲流:BufferedReader
,BufferedWriter
缓冲流的基本原理:在创建流对象时,内部会创建一个缓冲区数组(缺省使用8192个字节(8Kb)
的缓冲区),通过缓冲区读写,减少系统IO次数,从而提高读写的效率。
输出流flush()方法
将缓冲区的数据写入到文件中
输出流关闭前flush()一下
String str = BufferedReader.readline();一次读一行
1创建file类对象
File file = new File("屏幕 2023-12-22 013034.png");
File file1 = new File("屏幕 2023-12-22 013034copycopy.png");
2创建输入输出流,缓冲流作用在节点流之上
FileInputStream fileInputStream = new FileInputStream(file);
FileOutputStream fileOutputStream = new FileOutputStream(file1;
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
3具体的读写操作
byte[] bytes = new byte[1024];
int len;
while ((len=bufferedInputStream.read(bytes))!=-1){
bufferedOutputStream.write(bytes,0,len);
}
4关闭流,关闭最外面的流会自动关闭里面的流
bufferedOutputStream.close();
bufferedInputStream.close();
13.5转换流
13.5.1 InputStreamReader、OutputStreamWriter
InputStreamReader:将一个输入型的字节流转换为输入型的字符流。
OutputStreamWriter:将一个输出型的字符流转换为输出型的字节流。
将gbk文件复制一份,并保存为utf8格式,在idea中打开gbk格式的文件为乱码,默认为utf8格式
1创建File实例对象
File file = new File("dbcp_gbk.txt");
File file1 = new File("dbcp_utf8.txt");
2创建流
FileInputStream fileInputStream = new FileInputStream(file);
FileOutputStream fileOutputStream = new FileOutputStream(file1);
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream,"gbk");//指定字符集,默认为utf8
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream,"utf8");
3具体的读写操作
char[] chars = new char[10];
int len;
while ((len=inputStreamReader.read(chars))!=-1){
outputStreamWriter.write(chars,0,len);
}
4关闭流
inputStreamReader.close();
outputStreamWriter.close();
13.5.2字符集
在硬盘上:
ascii:主要用来存储a、b、c等英文字符和1、2、3、常用的标点符号。每个字符占用1个字节。
gbk:用来存储中文简体繁体、a、b、c等英文字符和1、2、3、常用的标点符号等字符。
中文字符使用2个字节存储的。向下兼容ascii,意味着英文字符、1、2、3、标点符号仍使用1个字节。
utf-8:可以用来存储世界范围内主要的语言的所有的字符。使用1-4个不等的字节表示一个字符。
中文字符使用3个字节存储的。向下兼容ascii,意味着英文字符、1、2、3、标点符号仍使用1个字节。
在内存中:
一个字符(char)占用2个字节。在内存中使用的字符集称为Unicode字符集。
13.6对象流
13.6.1ObjectOutputStream、ObjectInputStream*
ObjectOutputStream:将内存中的Java对象保存在文件中或通过网络传输出去
ObjectInputStream:将文件中的数据或网络传输过来的数据还原为内存中的Java对象
一些方法
oos.writeUTF("江山如此多娇,引无数英雄竞折腰");
Person p1 = new Person("Tom",12);
oos.writeObject(p1);
Person person = (Person) ois.readObject();
String str1 = ois.readUTF();
static修饰的,transient修饰的无法序列化
13.6.2 序列化与反序列化
序列化:使用ObjectOutputStream流实现。将内存中的Java对象保存在文件中或通过网络传输出去
反序列化:使用ObjectInputSteam流实现。将文件中的数据或网络传输过来的数据还原为内存中的Java对象
自定义类序列化要求:
① 自定义类需要实现接口:Serializable
② 要求自定义类声明一个全局常量: static final long serialVersionUID = 42234234L;
用来唯一的标识当前的类。
③ 要求自定义类的各个属性也必须是可序列化的。
> 对于基本数据类型的属性:默认就是可以序列化的
> 对于引用数据类型的属性:要求实现Serializable接口
13.7标准输入输出流,打印流
十四网络编程
14.1InetAddress类
不对外提供构造器,创建实例的两种方法:
InetAddress localHost = InetAddress.getLocalHost();
InetAddress atguigu = InetAddress.getByName("www.atguigu.com");
InetAddress atguigu = InetAddress.getByName("192.163.0.1");
InetAddress inetAddress = InetAddress.getByName("www.atguigu.com");
System.out.println(inetAddress);
System.out.println(inetAddress.getHostAddress());获取ip地址
System.out.println(inetAddress.getHostName());获取主机名或域名
14.2Tcp协议
-
TCP协议进行通信的两个应用进程:客户端、服务端。
-
使用TCP协议前,须先
建立TCP连接
,形成基于字节流的传输数据通道 -
传输前,采用“三次握手”方式,点对点通信,是
可靠的
-
TCP协议使用
重发机制
,当一个通信实体发送一个消息给另一个通信实体后,需要收到另一个通信实体确认信息,如果没有收到另一个通信实体确认信息,则会再次重复刚才发送的消息。
-
-
在连接中可进行
大数据量的传输
-
传输完毕,需
释放已建立的连接,效率低
-
@Test public void test15() throws Exception { File file = new File("屏幕 2023-12-22 013034.png"); InetAddress inetAddress = InetAddress.getLocalHost(); int port = 9000; Socket socket = new Socket(inetAddress,port); FileInputStream fileInputStream = new FileInputStream(file); OutputStream outputStream = socket.getOutputStream(); byte[] bytes = new byte[1024]; int len; while ((len = fileInputStream.read(bytes))!=-1){ outputStream.write(bytes,0,len); } System.out.println("数据发送完成,客户端"); 客户端socket关闭输出流 socket.shutdownOutput(); InputStream inputStream = socket.getInputStream(); ByteArrayOutputStream维护了一个byte数组,可以用来读取中文字符 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); byte[] bytes1 = new byte[5]; int len1; while ((len1=inputStream.read(bytes1))!=-1){ byteArrayOutputStream.write(bytes1,0,len1); } 输出ByteArrayOutputStream System.out.println(byteArrayOutputStream.toString()); byteArrayOutputStream.close(); inputStream.close(); outputStream.close(); fileInputStream.close(); socket.close(); }
@Test
public void test16() throws Exception {
int port = 9000;
ServerSocket serverSocket = new ServerSocket(port);
客户端接受来自服务端的socket
Socket accept = serverSocket.accept();
File file = new File("屏幕 2023-12-22 013034客户端.png");
InputStream inputStream = accept.getInputStream();
FileOutputStream fileOutputStream = new FileOutputStream(file);
byte[] bytes = new byte[1024];
int len;
while ((len = inputStream.read(bytes))!=-1){
fileOutputStream.write(bytes,0,len);
}
System.out.println("服务端,文件已接收");
OutputStream outputStream = accept.getOutputStream();
outputStream.write("客户端已成功接受".getBytes());
outputStream.close();
fileOutputStream.close();
inputStream.close();
accept.close();
serverSocket.close();
}
14.3Udp协议
发送端、接收端。
将数据、源、目的封装成数据包(传输的基本单位)Packet的实例,不需要建立连接
-
发送不管对方是否准备好,接收方收到也不确认,不能保证数据的完整性,故是
不可靠的
-
每个数据报的大小限制在
64K
内 -
发送数据结束时
无需释放资源,开销小,通信效率高
-
适用场景:音频、视频和普通数据的传输。例如视频会议
@Test
public void sender() throws Exception {
//1. 创建DatagramSocket的实例
DatagramSocket ds = new DatagramSocket();
//2. 将数据、目的地的ip,目的地的端口号都封装在DatagramPacket数据报中
InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
int port = 9090;
byte[] bytes = "我是发送端".getBytes("utf-8");
DatagramPacket packet = new DatagramPacket(bytes,0,bytes.length,inetAddress,port);
//发送数据
ds.send(packet);
ds.close();
}
//接收端
@Test
public void receiver() throws IOException {
//1. 创建DatagramSocket的实例
int port = 9090;
DatagramSocket ds = new DatagramSocket(port);
//2. 创建数据报的对象,用于接收发送端发送过来的数据
byte[] buffer = new byte[1024 * 64];数据包大小不超过64kB
DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length);
//3. 接收数据
ds.receive(packet);
//4.获取数据,并打印到控制台上
String str = new String(packet.getData(),0,packet.getLength());
System.out.println(str);
ds.close();
}
14.4Url编程
URL的作用:定位互联网上某一资源的地址。
URL的格式
http://192.168.21.107:8080/examples/abcd.jpg?name=Tom ---> "万事万物皆对象"
应用层协议 ip地址 端口号 资源地址 参数列表
/*
* 需求:将URL代表的资源下载到本地
* */
@Test
public void test1() {
HttpURLConnection urlConnection = null;
InputStream is = null;
FileOutputStream fos = null;
try {
//1. 获取URL实例
URL url = new URL("http://127.0.0.1:8080/examples/abcd.jpg");
//2. 建立与服务器端的连接
urlConnection = (HttpURLConnection) url.openConnection();
//3. 获取输入流、创建输出流
is = urlConnection.getInputStream();
File file = new File("dest.jpg");
fos = new FileOutputStream(file);
//4. 读写数据
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
System.out.println("文件下载完成");
} catch (IOException e) {
e.printStackTrace();
} finally {
//5. 关闭资源
try {
if (fos != null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (is != null)
is.close();
} catch (IOException e) {
e.printStackTrace();
}
if (urlConnection != null)
urlConnection.disconnect();
}
}
十五反射
15.1Class类
获取Class实例的三种方式
//1.调用运行时类的静态属性:class
Class clazz1 = User.class;
System.out.println(clazz1);
//2. 调用运行时类的对象的getClass()
User u1 = new User();
Class clazz2 = u1.getClass();
//3. 调用Class的静态方法forName(String className)
String className = "com.atguigu02._class.User"; //全类名
Class clazz3 = Class.forName(className);
15.2类的加载器
15.2.1类的加载过程
5. 类的加载过程(了解)
过程1:类的装载(loading)
将类的class文件读入内存,并为之创建一个java.lang.Class对象。此过程由类加载器完成
过程2:链接(linking)
> 验证(Verify):确保加载的类信息符合JVM规范,例如:以cafebabe开头,没有安全方面的问题。
> 准备(Prepare):正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
> 解析(Resolve):虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
过程3:初始化(initialization)
执行类构造器<clinit>()方法的过程。
类构造器<clinit>()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。
15.2.2类的加载器
> BootstrapClassLoader:引导类加载器、启动类加载器
> 使用C/C++语言编写的,不能通过Java代码获取其实例
> 负责加载Java的核心库(JAVA_HOME/jre/lib/rt.jar或sun.boot.class.path路径下的内容)
> 继承于ClassLoader的类加载器
> ExtensionClassLoader:扩展类加载器
> 负责加载从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/lib/ext子目录下加载类库
> SystemClassLoader/ApplicationClassLoader:系统类加载器、应用程序类加载器
> 我们自定义的类,默认使用的类的加载器。
> 用户自定义类的加载器
> 实现应用的隔离(同一个类在一个应用程序中可以加载多份);数据的加密。
15.2.3通过类的加载器来读取配置文件
配置文件,里面的key和value都为String类型,用来存放数据,实现数据和代码的分离
@Test
public void method1() throws ClassNotFoundException, IOException {
创建properties
Properties properties = new Properties();
通过ClassLoader.getSystemClassLoader()得到一个系统类加载器
通过getResourceAsStream()获取一个输入流,路径为src下的文件
InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream("info.properties");
load()里面应该为一个输入流
properties.load(resourceAsStream);
读取配置文件
String name = properties.getProperty("name");
String age = properties.getProperty("age");
System.out.println(name+":"+age);
}
15.3反射获取运行时类的结构
调用指定的属性
调用指定的属性
获取Class实例
Class personClass = Person.class;
获取运行时类对象(jdk9之后不推荐使用,推荐使用调用构造)
Person person =(Person) personClass.newInstance();
getDeclaredField(String str)获取运行时类指定名的属性
Field name = personClass.getDeclaredField("name");
设置权限
name.setAccessible(true);
get(Object obj) (获取的操作)
或 set(Object obj,Object value) (设置的操作)进行操作
name.set(person,"cxk");
System.out.println(name.get(person));
获取类的属性
Field info = personClass.getDeclaredField("info");
info.setAccessible(true);
填具体对象的地方改为null
info.set(null,"蔡徐坤cln");
System.out.println(info.get(null));
调用指定的方法
Class personClass = Person.class;
Person person =(Person) personClass.newInstance();
Method show = personClass.getDeclaredMethod("show");
show.setAccessible(true);
通过方法名.invoke()来调用方法
show.invoke(person);
方法中含有形参,形参类型.class
Method showNation = personClass.getDeclaredMethod("showNation", String.class, int.class);
showNation.setAccessible(true);
invoke返回值类型默认为Object
String china = (String) showNation.invoke(person, "china", 18);
System.out.println(china);
调用指定的构造器,创建运行时类对象用此方法
Class personClass = Person.class;
空参构造器
Constructor declaredConstructor = personClass.getDeclaredConstructor();
调用newInstance()创建实例
Person person = (Person) declaredConstructor.newInstance();
System.out.println(person);
有参构造器
Constructor cxk = personClass.getDeclaredConstructor(String.class,int.class);
cxk.setAccessible(true);
Person person1 = (Person) cxk.newInstance("蔡徐坤",18);
ystem.out.println(person1);