Java尚硅谷核心知识

Java尚硅谷核心知识

一、Java基础部分

1.1 Java语言的主要特性

  • Java是一种跨平台性的语言,满足一次编译,到处运行的语言(Java语言的运行依赖于JVM,不同的操作系统有不同的JVM)
  • Java是一种面向对象的编程语言
  • Java有一个很大的优势在于有自动回收垃圾的机制
  • Java语言是健壮的。Java的强类型机制、异常处理、垃圾的自动收集等是Java程序健壮性的重要保证。

1.2 Java程序运行机制及运行过程

  • Java程序的运行过程
    在这里插入图片描述

  • 层次关系

在这里插入图片描述

1.2.1Java两种核心机制

  • JVM

    1. JVM是一个具有指令集并使用不同的存储区域。负责执行指令,管理数据,内存,寄存器。
    2. 对于不同的平台有不同的虚拟机,平台提供了对应的虚拟机才能运行
    3. 因为JVM的存在,实现了"一次编译,到处运行"
  • GC

    1. 自动监测对象是否超过作用域(可达性分析和引用计数
    2. 将不再使用的内存空间进行回收
    3. 但是Java程序任然会存在内存泄露和内存溢出问题

1.3Java语言的环境搭建

1.3.1 为什么要为JDK设置环境变量

在cmd中可以在任何地方调用JDK指令

  • cmd种常用指令:
dir : 列出当前目录下的文件以及文件夹
md : 创建目录
rd : 删除目录
cd : 进入指定目录
cd.. : 退回到上一级目录
cd\: 退回到根目录
del : 删除文件
exit : 退出 dos 命令行

1.3.2 JDK、JRE、JVM

JDK是Java开发工具包     (JDK=JRE+开发工具包)
JRE是Java运行环境       (JRE=JVM+Java标准类库)
JVM是Java虚拟机

二、Java基本语法

2.1 Java的关键字和保留字

被Java语言赋予了特殊含义,用做专门用途的字符串(单词)。

  • Java中有很多关键字,保留字是指以后的版本可能会用到(goto和const)
  • 在Java中变量的名字不能关键字或者保留字

2.1.1 return关键字的使用:

  1. 结束方法
  2. 针对于有返回值类型的方法,使用"return 数据"方法返回所要的数据。

2.1.2 clone

克隆分为深克隆浅克隆,

Animal a2 = (Animal) a1.clone();

2.2 变量

Java是一种强类型语言

强类型语言:每一种数据都定义了明确的具体数据类型,在内存中分配了不同大小的内存空间.比如int a=10;编译前就为a分配了4个字节
弱类型语言:是指是在编译后确定数据类型,没有明显的类型,随着环境的不同,自动变换类型。比如:var a=10;编译前无法确定它的类型。

变量按作用范围可分为成员变量和局部变量

  • 成员变量: 在方法体外,类体内声明的变量
    1. 实例变量(不以static修饰的)
    2. 类变量(以static修饰的)
  • 局部变量: 在方法体内部声明的变量
    1. 形参(方法、构造器中定义的变量)
    2. 方法内、代码块内定义的局部变量

2.3Java的数据类型

Java的数据类型一共有两种。一种是基本类型,另一种是引用类型。

2.3.1 引用类型包括:

类(class)、接口(Interface)、数组(Array)

注意:字符串在类里面

2.3.2 基本类型包括:

byte、short、int(默认)、long、float、double(默认)、char、boolean

数据类型 字节数 位数 范围 默认值
byte 1 8 -128~127 0
short 2 16 -2^15 ~2^15-1 0
int 4 32 -2^31 ~2^31-1 0
long 8 64 -2^63 ~2^63-1 0
boolean 1 8 true、false false
char 2 16 0~65535
float 4 32 -2^128 ~ 2^128 0.0
double 8 64 -2^1024 ~ 2^1024 0.0

说明:float和double是怎么计算的
float、double和其它类型的计算方式是不一样的,float和double是采取科学计数法来进行计算的

float和double的范围是由指数的位数来决定的。其中float的指数位有8位,而double的指数位有11位。

float: 1bits(符号位) 8bits(指数位) 23bits(尾数位)
double 1bits(符号位) 11bits(指数位) 52bits(尾数位)

举例:

0  01111100  01000000000000000000000==0.15625
+ 124-127     1.25         1.25*2^-3=0.15625
符号位:0代表正1代表负   
指数位:是一个无符号位存在一个偏移量127,计算出的结果需要与127做差
尾数位:尾数位占23个bit,它的表达上有一个特殊点,它被认为是24个bit,第一个bit取值1,且被隐藏掉:有了这个前提,我们就比较好理解尾数的表达了:这24个bit,依次表达2^02^-1, 2^-2, 2^-23,隐藏的第一位表达的指就是1。所以:尾数位全0时表达的值是1,全1时表达的最大值2 - 2^-23

注意:

  • Java中char的范围和c语言中char的范围有区别Java中char占用两个字节,范围是0–65535。
  • 1byte=8bit;
  • long a=12; float b=12;这两种情况不会报错,是因为自动提升的原因
  • byte,short,char之间不会相互转换,他们三者在计算时首先转换为int类型。所以有byte、short、char的混合运算时结果都为int类型

2.3.1 自动提升

基本类型中容量小的和容量大的一起运算时,会发生自动提升现象
注意:float的范围比long的还要大。是因为它们底层的存储结构不同。float/double使用科学计数法来存储,而long使用普通的二进制来存储,也就是说一个存的是多少次方,而一个是只有多少位。

2.3.2 强制准换

是指在大容量向小容量转换时发生的,在强制转换时会有精度损失

注意:类型转换都是基本类型之间的,String不能和基本类型之间进行转换,但是包装类可以和String进行转换。如: String a = “43”; int i = Integer.parseInt(a);

2.4运算符

  • 算术运算:+ - * /
  • 赋值运算:
short s=2;
s=s+2;//编译不通过,类型不一致
s+=2;//编译通过,运算时不会改变类型
  • 比较运算
    instanceof
 System.out.println("hello" instanceof String);//true
  • 逻辑运算
&:逻辑与
&&:短路与
|:逻辑或
||:短路或
^:异或。相同为0不同为1
!:非
  • 位运算
<<左移,相当于*2
>>右移,相当于/2
&与运算,都11
|或运算,有11
^异或运算  110^011=101==>5
~取反运算  ~6=7  (补码)0110<==>(补码)1001==>0111(源码)==>7
  • 三目运算符
    要求 " :"前后两种数据类型要一致
    举个栗子:
//如果a<60,将a的值置为0。
int a=a>60?a:0;
  • 运算符优先级
    在这里插入图片描述

三、数组

数组是一种按照一定顺序排列,且能存储相同数据类型的集合。但是需要占用连续空间数据结构。
需要注意的是:

  • 长度一旦确定,就不能再进行修改。
  • 数组的索引是从0开始的而数据库库的索引是1开始的,在连接数据库时需要注意。

3.1创建数组的内存结构的解析:

在这里插入图片描述

3.2一维数组的内存解析

在这里插入图片描述

3.3二维数组的内存解析

就是两个一维数组组成的数组栈中的引用指向堆,堆中存的也是一个引用
在这里插入图片描述

3.4算法中数组的应用

  1. 数组元素的赋值(杨辉三角、回形数等)
  2. 求数值型数组中元素的最大值、最小值、平均数、总和等
  3. 数组的复制、反转、查找(线性查找、二分法查找)
  4. 数组元素的排序算法(稳定性)A和B相等排序后,A和B的次序不变(应用:商品首先根据销量排序,再根据价格排序,就需要排序的稳定性)

3.5需要注意的问题

     	int [] arr=new int[] {
   1,2,3,4,5};
		System.out.println(arr);//[I@32a1bec0
		char [] arr1=new char[] {
   'a','b'};
		System.out.println(arr1);//ab

出现这种现象是因为重载。(将char型print和int型print进行重载)

四、oop–万物皆对象

在Java中将功能结构封装在类中,通过类的实例化来调用具体功能结构

主流编程语言要么是面向过程(强调考虑怎么做),要么是面向对象(强调谁来做)。程序员从面向过程的执行者转化成了面向对象的指挥者

注意:int 不是对象,因为一般情况下创建对象需要进行new。将int转换为包装类后即是对象。

举个栗子:把大象装进并冰箱

在这里插入图片描述

4.1类和对象的关系

面向对象的两个要素:类和对象

:是指对一类事物的概括,是抽象的、概念上的定义。类的成员包括属性和方法
对象:是指存在的一个真实个体,因而也称为实例(instance)。对象可以调用类的非私有成员。创建类的对象 = 类的实例化 = 实例化类
可以理解为:类 = 抽象概念的人;对象 = 实实在在的某个人
在这里插入图片描述
对象一般通过new来创建。类名 对象名 = new 类名();
在这里插入图片描述

4.1.1对象的内存解析

在这里插入图片描述

4.1.2对象数组的内存解析

在这里插入图片描述

4.1.3匿名对象

不定义对象的句柄,而直接调用这个对象的方法。这样的对象叫做匿名对象。

    new A().show();

注意:匿名对象主要使用在只需要进行一次方法调用

4.2 类的成员之一:属性(成员变量)

属性 = 成员变量 = field = 域、字段
使用形式: 修饰符 数据类型 属性名 = 初始化值 ;

  • private int age; //声明private变量 age

变量的分类:成员变量(属性,存在于方法体外部)与局部变量(存在于方法体内部)
Java中的变量都是先声明后使用,并且作用于自己的作用域

属性 局部变量
定义在类下 定义在方法,构造器等内部
可以用权限修饰符 不可以用权限修饰符
加载到堆中 加载到栈中
有默认初始值 没有初始值

4.2.1成员变量(属性)和局部变量的内存解析

在这里插入图片描述

4.3类的成员之二:方法

方法 = 成员方法 = 函数 = method

方法是类或对象行为特征的抽象,用来完成某个功能操作。

方法的分类:按照是否有形参及返回值。

4.3.1方法的重载和重写

  • 重载:参数个数或者参数型不同的同名方法。与返回值类型无关,只看参数列表,且参数列表必须不同。(参数个数或参数类型)。调用时,根据方法参数列表的不同来区别。

举个例子:

    //返回两个整数的和
    int add(int x,int y){
   return x+y;}
    //返回三个整数的和
    int add(int x,int y,int z){
   return x+y+z;}
    //返回两个小数的和
    double add(double x,double y){
   return x+y;}
  • 重写:同一个方法子类对父类方法功能的改变
public class A {
   
    public void show() {
    System.out.println("ok"); }
}
class B  extends A{
   
    @Override
    public void show() {
    System.out.println("no ok"); }
}

4.3.2 方法参数的值传递机制

Java只有值传递

  • 当传递类型是基本类型时,传递的就是参数真实的值,在方法中对值进行修改,不影响本身的值
  • 当传递类型时引用类型时,传递的就是地址的值,在方法中进行修改数据时,会影响本身的值
    在这里插入图片描述

在这里插入图片描述

不是说只要传递的参数是引用类型的就会修改数据的值,他的大前提是传递前后两个引用指向的是同一块内存。

举个栗子:

String s="hello";
Test test=new Test();
test.change(s);
//    change(String s1){
   
//        s1="hi";
//    }

System.out.println(s); 

在这里插入图片描述

4.3.3方法的递归

一个方法体内调用它自身,注意在递归时要注意弹出时,否则会导致跳不出循环,导致栈溢出

//计算1-100之间所有自然数的和
public int sum(int num){
   
if(num == 1){
   
return 1;
}else{
   
return num + sum(num - 1);
} }

4.4类的成员之三:构造器

构造器的作用:创建对象;给对象进行初始化
构造器:默认构造器、非默认构造器

注意:

  • 多个构造器之间构成重载关系
  • 父类的构造器不可被子类继承

4.4.1拓展:构造器的典型:应用JavaBean

  • 有一个无参的公共的构造器
  • 有属性,且有对应的get、set方法
public class JavaBean {
   
private String name; // 属性一般定义为private
private int age;
public JavaBean() {
   
}
public int getAge() {
   return age; }
public void setAge(int age) {
   this.age = age; }
public String getName() {
   return name; }
public void setName(String n) {
   name = n; } }

4.5 this关键字:指向本地的属性、方法和构造器

this可以用来修饰、调用:属性、方法、构造器们可以理解为当前对象。
在类的方法中,我们可以使用"this.属性"或"this.方法"的方式,调用当前对象属性或方法。但是,通常情况下,我们都选择省略"this."。特殊情况下,如果方法的形参和类的属性同名时,我们必须显式的使用"this.变量"的方式,表明此变量是属性,而非形参。

注意:
在类的构造器中,可以使用this()调用本类的其他构造器,在用this进行调用构造器时必须放在第一行

//boy类的marry方法
marry(Girl girl){
   
       System.out.println("I Love girlA");
}
//girl类的marry方法
marry(Boy boy){
   
       System.out.println("I Love boyA");
       boy.marry(this);//谁调用marry,this就是谁。boyA回应 I Love girlA
}
  • 我们可以用this来区分属性和局部变量。
this.name = name;

4.6super关键字调用父类中的指定操作

super和this的用法相像,this代表本类对象的引用,super代表父类的内存空间的标识

在这里插入图片描述

4. 7oop三大特性

  • 封装:隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来。
  • 继承:一个子类继承一个父类,能够直接调用父类的非私有属性和方法,通过set、get也能访问私有属性。
  • 多态:分为运行时多态(overload)和编译时多态(override),多态的三个必要条件 :1.要有继承(实现implements) 2.要有重写(overWrite&overRide) 3.父类引用指向子类对象

4.7.1封装

将方法和属性进行私有化,对实现细节进行隐藏,只暴露出供外界使用的接口。使用者只能通过事先定制好的方法来访问数据,可以方便地加入控制逻辑,限制对属性的不合理操作

封装的体现:Java中通过将数据声明为私有的(private),再提供公共的(public)方法:getXxx()和setXxx()实现对该属性的操作

修饰符 类内部 同一个包 不同包的子类 同一个工程
private
default
protected
public

注意:对于class的权限修饰只可以用public和default

4.7.2继承

4.7.2.1 何为继承

多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。因为有了继承就可以省略掉很多冗余的代码。

4.7.2.2 继承的体现:
  1. 一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法。特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。只有因为封装性的影响,使得子类不能直接调用父类的结构而已。
  2. 子类继承父类以后,还可以声明自己特有的属性或方法:实现功能的拓展。子类和父类的关系,不同于子集和集合的关系。
4.7.2.3 Java中关于继承性的规定
  • Java是单根继承,一个子类只能继承(extends)一个类。但是可以实现很多个接口
  • 子类继承了父类,可以直接使用父类的非私有的方法和属性。
  • 在子类中,可以使用父类中定义的方法和属性,也可以创建新的数据和方法。
  • 在Java 中,继承的关键字用的是“extends”,即子类不是父类的子集,而是对父类的“扩展”。

说明:所有的子类都会直接或间接继承Object类

4.7.3多态

  • 父类的引用指向子类的对象(或子类的对象赋给父类的引用)
  • 多态性的使用前提: ① 类的继承关系 ② 方法的重写
  • 分为运行时多态(overload)和编译时多态(override)。编译时,看左边;运行时,看右边
4.7.3.1 重载

System.out.println()方法就是典型的重载方法,其内部的声明形式如下:

public void println(byte x)
public void println(short x)
public void println(int x)
public void println(long x)
public void println(float x)
public void println(double x)
public void println(char x)
public void println(double x)
public void println()
//这2个方法不能构成重载,会有编译错误。
public int A(int i);
public double A(int i);
//这2个方法可以形成重载
public int A(int i)public double A(double i);
4.7.3.2 重写:子类中可以根据需要对从父类中继承来的方法进行改造

注意:子类与父类中同名同参数的方法必须同时声明为非static的(即为重写),或者同时声明为static的(不是重写)。因为static方法是属于类的,子类无法覆盖父类的方法。

Java对象的强制转换:

  • 从子类到父类的类型转换可以自动进行
  • 从父类到子类的类型转换必须通过造型(强制类型转换)实现
  • 无继承关系的引用类型间的转换是非法的
  • 在造型前可以使用instanceof操作符测试一个对象的类型

在这里插入图片描述

public class Test {
   
public void method(Person e) {
    // 设Person类中没有getschool() 方法
// System.out.pritnln(e.getschool()); //非法,编译时错误
if (e instanceof Student) {
   
Student me = (Student) e; // 将e强制转换为Student类型
System.out.pritnln(me.getschool());
} }
public static void main(String[] args){
   
Test t = new Test();
Student m = new Student();
t.method(m);
} }

4.8"==“和"equals”

  • == 既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型就是比较内存地址
  • equals没有被重写过默认也是==,但是进行重写(重写的原则:比较两个对象的实体内容是否相同),重写后会比较类中的相应属性是否都相等。

4.8.1 equals()方法的使用

  1. 是一个方法,而非运算符
  2. 只能适用于引用数据类型
  3. Object类中equals()的定义:
public boolean equals(Object obj) {
   
	        return (this == obj);
	  }

说明:Object类中定义的equals()和==的作用是相同的:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体

  1. 像String、Date、File、包装类等都重写了Object类中的equals()方法。重写以后,比较的不是两个引用的地址是否相同,而是比较两个对象的"实体内容"是否相同。

  2. 通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的"实体内容"是否相同。那么,我们就需要对Object类中的equals()进行重写.

4.9包装类

  • 包装类使得一个基本数据类型的数据变成了类。针对八种基本数据类型定义相应的引用类型—包装类(封装类)

  • 包装类使得一个基本数据类型的数据变成对象,包装类内部有一个cache数组用来进行缓存(0–127)
    在这里插入图片描述

4.9.1装箱

基本数据类型包装成包装类的实例

int i = 500;  Integer t = new Integer(i);

4.9.2拆箱

获得包装类对象中包装的基本类型变量

Integer a=10;  int b=a.intValue();

4.9.3字符串–>基本类型

int i = new Integer(12);

4.9.4基本类型–>字符串

int i=10;     String str=String.valueOf(i);

4.10关键字static、abstract、final

4.10.1关键字static

当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份,例如所有的中国人都有个国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中都单独分配一个用于代表国家名称的变量

静态变量(类变量):让一个类的所有实例共享数据(被static修饰的变量是类变量)
注意:

被static修饰的成员有以下特点:

  • 随着类的加载而加载,不依赖于对象而存在,优先于对象存在。
  • 不因对象的不同而改变,将这些属性设置为类属性或者类方法
  • 访问权限允许时,可不创建对象,可以通过"类.静态变量"的方式进行调用。
  • 由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中。

类变量 vs 实例变量内存解析:
在这里插入图片描述
说明:首先将所有人的属性置为中国,也可以根据自己的需要进行改变,不进行操作则nation属性为中国

4.10.2关键字final

注意:

  • 被final修饰的变量不能被修改,作为常量出现
  • 被final修饰的类是"太监类"不能被继承
  • 被final修饰的方法也不能被重写
  • final标记的变量(成员变量或局部变量)即称为常量。名称大写,且只能被赋值一次。

4.10.3关键字abstract

用abstract关键字来修饰一个类或者方法,这个类叫做抽象类或者抽象方法,抽象类不能进行实例化,抽象类中一定有构造器,便于子类实例化时调用,开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作

注意:

  • 抽象方法只有方法的声明,没有实现细节
  • 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。
  • abstract不能用来修饰:属性、构造器等结构
  • abstract不能用来修饰私有方法、静态方法、final的方法、final的类

4.10.4 接口Interface

接口和类是并列的两个结构,接口通过让类去实现(implements)的方式来使用,接口中不能定义构造器的!意味着接口不可以实例化

接口中的全局常量是用public static final来进行修饰,方法用public abstract进行修饰

4.10.5 接口和抽象类的区别

实现类在继承父类或者实现接口时候,需要将全部抽象方法进行实现

接口和抽象类的概念不一样。接口是对动作的抽象,抽象类是对根源的抽象。

抽象类表示的是,这个对象是什么。接口表示的是,这个对象能做什么。

比如,男人,女人,这两个类(如果是类的话……),他们的抽象类是人。说明,他们都是人。
人可以吃东西,狗也可以吃东西,你可以把“吃东西”定义成一个接口,然后让这些类去实现它.

所以,在高级语言上,一个类只能继承一个类(抽象类)(正如人不可能同时是生物和非生物),但是可以实现多个接口(吃饭接口、走路接口)。

  • 接口中的常量都是public final static的,方法都是public abstract的,可以省略,系统会自动加上
  • 接口是抽象类的变体,接口中所有的方法都是抽象的。而抽象类是声明方法的存在而不去实现它的类。
  • 接口可以多继承,抽象类不行
  • 接口定义方法,不能实现,而抽象类可以实现部分方法。
  • 接口中基本数据类型为static 而抽类象不是的。

当你关注一个事物的本质的时候,用抽象类;当你关注一个操作的时候,用接口。

抽象类的功能要远超过接口,但是,定义抽象类的代价高。因为高级语言来说(从实际设计上来说也是)每个类只能继承一个类。在这个类中,你必须继承或编写出其所有子类的

所有共性。虽然接口在功能上会弱化许多,但是它只是针对一个动作的描述。而且你可以在一个类中同时实现多个接口。在设计阶段会降低难度的。

4.11内部类

当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。

通俗的说就是两个类,其中类A包含于类B里面,只有B类需要调用A类,此时就可以将A类写进B类里面,形成内部类。

head类即为person类的内部类
public class person{
   
    class head{
   
    
    }
}

内部类分为成员内部类(静态、非静态)和局部内部类(方法内、代码块内、构造器内)以及匿名内部类

4.11.1 成员内部类
  • 一方面,可以作为外部类的成员:调用外部类的结构,也可以被static修饰,还可以被4种不同的权限修饰
  • 另一方面,作为一个类可以定义属性、方法、构造器等,也可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承。还可以被abstract修饰
4.11.2 匿名内部类

就是没有名字的一种嵌套类,一个接口/类的方法的某个实现方式在程序中只会执行一次,但为了使用它,我们需要创建它的实现类/子类去实现/重写。此时可以使用匿名内部类的方式,可以无需创建新的类,减少代码冗余。

假设当前有一个接口,接口中只有一个方法

public interface Interface01 {
   
    void show();
}

为了使用该接口的show方法,我们需要去创建一个实现类,同时书写show方法的具体实现方式

public class Interface01Impl implements Interface01{
   
    @Override
    public void show() {
   
        System.out.println("I'm a impl class...");
    }
}

如果实现类Interface01Impl全程只使用一次,那么为了这一次的使用去创建一个类,未免太过麻烦。我们需要一个方式来帮助我们摆脱这个困境。匿名内部类则可以很好的解决这个问题。

我们使用匿名内部类

public static void main(String[] args) {
   
    Interface01 interface01 = new Interface01() {
   
        @Override
        public void show() {
   
            System.out.println("这里使用了匿名内部类");
        }
    };
    //调用接口方法
    interface01.show();
}

五、异常处理

在Java语言中,将程序执行中发生的不正常情况称为“异常”

Java程序在执行过程中所发生的异常事件可分为两类:

  • Error:Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError和OOM。一般不编写针对性的代码进行处理
  • Exception: 其它因编程错误或偶然的外在因素导致的一般性问题,可以使
    用针对性的代码进行处理。例如:空指针访问、试图读取不存在的文件、网络连接中断、数组角标越界

5.1异常类型

在这里插入图片描述

Java.lang.Throwable
     |--------java.lang.Error:一般不编写针对性代码进行处理
          |-------java.lang.StackOverflowError
          |-------java.lang.OutOfMemoryError 
     |--------Java.lang.Exception:可以进行异常处理
         |-------编译时异常(checked)
                 |--------IOException
                        |---------FileNotFoundException
                 |--------ClassNotFoundException
         |--------运行时异常(unchecked)
                 |--------NullPointerException
                 |--------ArrayIndexOutofBoundException
                 |--------ClassCastException
                 |--------NumberFormatException
                 |--------InputMismatchException
                 |--------ArithmaticExceptio
                 

5.1.0 堆溢出和栈溢出:

	public static void main(String[] args) {
   
		//1.栈溢出:java.lang.StackOverflowError
 		main(args);//递归,自己不断调用自己出不来了
		//2.堆溢出:java.lang.OutOfMemoryError 
		int[] arr = new int[1024*1024*1024];//开的空间大于Integer的最大内存1024*1024*1024=2^30,但是int[-2^30--2^30-1]。超出了int的可表示范围,弹出堆溢出错误
		
	}

5.1.1运行时异常:

//空指针异常
String a=null;
System.out.println(a.charAt(0));

//数组索引越界
int []a=new int[3];
System.out.println(a[3]);

//类型转换异常
Object obj=new Date();
String a=(String) obj;

//NumberFormatException
String str="abc";//如果是String str="123"就不会报错
int num=Integer.parsentInt(str);

//输入不匹配异常
int a=sc.nextInt();//输入非数字就会抛出该异常

//算数异常
int  a=2/0;

5.1.2编译时异常:

		File file = new File("hello.txt");
        //Unhandled exception: java.io.FileNotFoundException
		FileInputStream fis = new FileInputStream(file);

		int data = fis.read();
		while(data != -1){
   
			System.out.print((char)data);
			data = fis.read();
		}

		fis.close();

开发过程中的语法、逻辑错误不算异常
异常分为两类:

  • 一类是Error:是指Java虚拟机无法解决的严重问题。如JVM系统内部错误,栈溢出、资源耗尽,内存溢出(OutOfMemory)等等。一般情况下不能进行针对性代码进行处理
  • 另一类是Exception:遇见某些特殊情况才会发生比如1/0会有ArithmeticException,比如空指针、数组越界,类型转换异常。一般情况下,异常指的是Exception。
  • Exception又分为编译时异常和运行时异常。比如:除数为0,数组下标越界等

5.2异常处理机制

有两种方式处理异常:

  • try-catch-finally(是在想办法处理异常)
  • throws+异常类型(是在向上抛出异常,没有真正解决)

5.2.1 异常处理的方式一:try-catch-finally

5.2.1.1 异常的处理:抓抛模型

过程一:“抛”:程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象。并将此对象抛出。一旦抛出对象以后,其后的代码就不再执行。

	关于异常对象的产生:
	① 系统自动生成的异常对象
	② 手动的生成一个异常对象,并抛出(throw)

过程二:“抓”:可以理解为异常的处理方式:① try-catch-finally ② throws

5.2.1.2 try-catch-finally的使用
  try{
   
  		//可能出现异常的代码
  }catch(异常类型1 变量名1){
   
  		//处理异常的方式1
 }catch(异常类型2 变量名2){
   
  		//处理异常的方式2
  }catch(异常类型3 变量名3){
   
  		//处理异常的方式3
  }
  ....
  finally{
   
  		//一定会执行的代码
  }
5.2.1.3 说明以及体会:

说明:

  1. finally是可选的。一旦选用,finally中声明的是一定会被执行的代码。即使catch中又出现异常了,try中有return语句,catch中有return语句等情况。
  2. 像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己手动的进行资源的释放。此时的资源释放,就需要声明在finally中。
  3. 使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配
  4. 一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出当前的 try-catch结构(在没有写finally的情况)。继续执行其后的代码
  5. catch中的异常类型如果没有子父类关系,则谁声明在上,谁声明在下无所谓。catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面。否则,报错
  6. 常用的异常对象处理的方式: ① String getMessage() ②printStackTrace()
  7. 在try结构中声明的变量,再出了try结构以后,就不能再被调用
  8. try-catch-finally结构可以嵌套

体会:

  • 体会1:使用try-catch-finally处理编译时异常,是得程序在编译时就不再报错,但是运行时仍可能报错。
    相当于我们使用try-catch-finally将一个编译时可能出现的异常,延迟到运行时出现。

  • 体会2:开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了。
    针对于编译时异常,我们说一定要考虑异常的处理。

5.2.2 异常处理的方式二:throws + 异常类型

  • "throws + 异常类型"写在方法的声明处。指明此方法执行时,可能会抛出的异常类型一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。异常代码后续的代码,就不再执行!

  • 体会:try-catch-finally:真正的将异常给处理掉了。throws的方式只是将异常抛给了方法的调用者。 并没有真正将异常处理掉。

  • 开发中如何选择使用try-catch-finally 还是使用throws?

    1. 如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法中有异常,必须使用try-catch-finally方式处理。
    2. 执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。我们建议这几个方法使用throws的方式进行处理。而执行的方法a可以考虑使用try-catch-finally方式进行处理。

在这里插入图片描述

5.2.3练习

在这里插入图片描述
运行结果:
在这里插入图片描述

5.3总结:

在这里插入图片描述

  • throw和throws的区别
    throw表示抛出一个异常类的对象,声明在方法内部
    throws属于异常处理的一种方式,声明在方法声明处

  • finally中声明的是一定会被执行的代码。即使catch中出现异常或者return,也会执行finally

  • "throws+异常类型"写在声明处。指明此方法执行时可能会抛出的异常类型。一旦方法体执行时,出现异常,会在异常代码生成一个异常类对象,此对象满足throws后异常类型时,就会被抛出。异常代码的后续就不会再执行

六、多线程

  • 程序(program)是为完成特定任务、用某种语言编写的一组指令的集合
  • 进程(process)是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。——生命周期
  • 线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径
    1. 若一个进程同一时间并行执行多个线程,就是支持多线程的
    2. 线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小
    3. 一个进程中的多个线程共享相同的内存单元/内存地址空间它们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全的隐患。

6.1多线程的优点

  1. 提高应用程序的响应。对图形化界面更有意义,可增强用户体验。
  2. 提高计算机系统CPU的利用率
  3. 改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改

6.2进程与线程

进程是分配资源的基本单位,线程是执行的基本单位

6.3并行与并发

  • 并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。
  • 并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事。

6.4线程的创建

Java语言的JVM允许程序运行多个线程,它通过java.lang.Thread类来体现。

每个线程都是通过某个特定Thread对象的run()方法来完成操作的,经常把run()方法的主体称为线程体。通过该Thread对象的start()方法来启动这个线程,而非直接调用run()

6.4.1继承Thread类

步骤:

  1. 创建一个继承于Thread类的子类
  2. 重写Thread类的run() --> 将此线程执行的操作声明在run()中
  3. 创建Thread类的子类的对象
  4. 通过此对象调用start()
public class A {
   
    public static void main(String[] args) {
   
         MyThread myThread=new MyThread();
         myThread.start();
    }
}
class MyThread extends Thread{
   
    @Override
    public void run() {
   
        System.out.println("ok");
    }
}
6.4.1.1注意:
  1. 如果自己手动调用run()方法,那么就只是普通方法,没有启动多线程模式。
  2. run()方法由JVM调用,什么时候调用,执行的过程控制都有操作系统的CPU
    调度决定。
  3. 想要启动多线程,必须调用start方法。
  4. 一个线程对象只能调用一次start()方法启动,如果重复调用了,则将抛出以上
    的异常“IllegalThreadStateException”。

6.4.2实现Runnable接口

步骤:

  1. 创建一个实现了Runnable接口的类
  2. 实现类去实现Runnable中的抽象方法:run()
  3. 创建实现类的对象
  4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
  5. 通过Thread类的对象调用start()
public class A {
   
    public static void main(String[] args) {
   
        MyThread myThread=new MyThread();
        new Thread(myThread).start();

    }
}
class MyThread implements Runnable{
   
    @Override
    public void run() {
   
        System.out.println("ok");
    }
}

6.4.3两种创建线程的区别

继承Thread:线程代码存放Thread子类run方法中。
实现Runnable:线程代码存在接口的子类的run方法。

开发中:优先选择:实现Runnable接口的方式
原因:

  1. 实现的方式没有类的单继承性的局限性
  2. 实现的方式更适合来处理多个线程有共享数据的情况。
6.4.3.1Runnable的好处
  • 避免了单继承的局限性
  • 多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源

6.4.4 Thread类的有关方法

  • void start(): 启动线程,并执行对象的run()方法
  • run(): 线程在被调度时执行的操作
  • String getName(): 返回线程的名称
  • void setName(String name):设置该线程名称
  • static Thread currentThread(): 返回当前线程。在Thread子类中就是this,通常用于主线程和Runnable实现类
  • static void yield():释放当前cpu的执行权
    1. 暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程
  • join() :当某个程序执行流中调用其他线程的 join() 方法时,调用线程将被阻塞,直到 join() 方法加入的 join 线程执行完为止
    1. 低优先级
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值