目录

  • 前言
  • 1. 面向对象
    • 1.1 final关键字
    • 1.2 抽象类
    • 1.3 接口
    • 1.4 接口与抽象类结合
    • 1.5 接口的作用
    • 1.6 包和import
    • 1.7 访问控制权限
    • 1.8 Object类
    • 1.9 匿名内部类
  • 2. 数组
    • 2.1 一维数组的声明和使用
    • 2.2 二维数组的声明和使用
    • 2.3 排序查找算法
  • 3. 常用类
    • 3.1 String类
      • 3.11 charAt方法
      • 3.12 compare To方法
      • 3.13 contains方法
      • 3.14 endsWith方法
      • 3.15 equals方法
      • 3.16 equalsIgnoreCase方法
      • 3.17 getBytes方法
      • 3.18 indexOf与lastIndexOf方法
      • 3.19 isEmpty()方法
      • 3.20 replace方法
      • 3.21 split方法
      • 3.22 startsWith方法
      • 3.23 substring方法
      • 3.24 toCharArray方法
      • 3.25 toLowerCase与toUpperCase方法
      • 3.26 trim方法
      • 3.27 valueOf方法
    • 3.2 stringbuffer类
    • 3.3 基础类型对应的 8 个包装类
      • 3.31 自动装箱和自动拆箱
      • 3.32 Integer常用方法
    • 3.4 日期相关类
    • 3.5 数字相关类
    • 3.6 Random类
    • 3.7 enum类
  • 4.异常处理
    • 4.1 异常处理方式
    • 4.2 异常捕捉和上报联合使用
    • 4.3 try catch深入
    • 4.4 异常对象常用方法
    • 4.5 finally子句
    • 4.6 final、finally与finalize区别
    • 4.7 自定义异常
    • 4.8 面试题
    • 4.9 实际开发场景

前言

这部分知识一共有4个文档
第二个是当前这个文档

  1. java零基础从入门到精通(全)
  2. javaSE从入门到精通的二十万字总结(一)
  3. javaSE从入门到精通的二十万字总结(二)
  4. javaSE从入门到精通的二十万字总结(三)

补充idea快捷键
快速生成main方法psvm
快速生成System.out.println(),sout
快速改写接口实现类crtl+o
删除一行ctrl + y
运行ctrl + shift + F10
任何新增/新建/添加的快捷键alt + insert
窗口变大,变小ctrl + shift + F12
切换java程序alt + 右箭头或者alt + 左箭头
切换窗口alt + 标号
提示方法的参数ctrl + p
注释单行注释:ctrl + /多行注释:ctrl + shift + /
idea当中复制一行ctrl + d

1. 面向对象

1.1 final关键字

  • final 修饰的类不能被继承
  • final 修饰的方法不能被覆盖
  • final 修饰的变量不能被修改
  • 构造方法不能被 final 修饰

代码示列:修饰的类、方法、变量
变量在没有赋值的时候可以定义可以声明
但如果输出变量就必须给予定义以及赋值

//修饰的类:
final class A1 {public void test1() {}
}//不能继承 A1,因为 A1 采用 final 修饰了
class B1 extends A1 {public void test2() {}
}//修饰的方法:
class A1 {public final void test1() {}
}
class B1 extends A1 {//覆盖父类的方法,改变其行为
//因为父类的方法是 final 修饰的,所以不能覆盖public void test1() {}public void test2() {}
}//修饰的变量也不能修改
public class FinalTest03 {private static final long CARD_NO = 878778878787878L;public static void main(String[] args) {//不能进行修改,因为 CARD_NO 采用 final 修改了CARD_NO = 99999999999999L;}
}
  • 如果修饰的引用,那么这个引用只能指向一个对象,也就是说这个引用不能再次赋值,即地址已经确定了不能修改,但被指向的对象(属性值)是可以修改的

对象p不能再重新new空间,但是对象p的属性值是可以修改的。final固定的只是引用对象的地址,地址内的内容是可以修改的。

public class FinalTest05 {public static void main(String[] args) {Person p1 = new Person();
//可以赋值p1.name = "张三";System.out.println(p1.name);final Person p2 = new Person();p2.name = "李四";System.out.println(p2.name);
//不能编译通过
//p2 采用 final 修饰,主要限制了 p2 指向堆区中的地址不能修改(也就是p2 只能指向一个对象)
//p2 指向的对象的属性是可以修改的p2 = new Person();}
}

小tips:局部变量没有赋值输出的时候有误,而实列变量的输出系统会默认赋值,但final修饰的实例变量系统不管赋值默认值,需要程序员手动赋值

class user{//实例变量没赋值编译器出错
final int age;//实例变量手动赋值程序不出错
final double height=1.8;//实例变量结合构造函数初始化不出错
final double weight;
public user(){//不一定要在第一行this.weight=90;//系统默认赋值的时候是在这个时候,即赶在系统赋默认值之前就可this.weight=50;//在重新赋值会出错,不可以
}

实例对象存储在堆中,一个对象一份存储,既然设置final不变化,还浪费其内存,故将其设置成static静态,节省内存空间
static final 联合修饰的变量为常量,常量名件以全部大写,每个单词之间采用下划线衔接

  • 常量和静态变量一样,常量不能变化,且常量和静态变量都存在方法区,都在类加载的时候初始化
  • 调用该变量通过 类名.属性

总结:final修饰的东西都不能变即可

1.2 抽象类

抽象类是类和类之间的共同特征,将这些共同特征进一步形成抽象类,由于类本身不存在,所以抽象类无法创建对象。
类到对象是实例化,对象到类是抽象
抽象方法不能被 final 修饰,因为抽象方法就是被子类实现的

  • 采用 abstract 关键字定义的类就是抽象类,采用 abstract 关键字定义的方法就
    是抽象方法
  • 抽象的方法只需在抽象类中,提供声明,不需要实现
  • 如果一个类中含有抽象方法,那么这个类必须定义成抽象类。抽象类中不一定有抽象方法,抽象方法必须出现在抽象类中
  • final和abstract不能同时同时使用,这两个关键字是对立的
  • 抽象类的子类可以是抽象类。也可以是非抽象类
  • 一个非抽象的类,继承抽象类,必须将抽象类中的抽象方法进行覆盖/重写/实现

抽象类无法实例化,无法创建对象,抽象类是被子类来继承的
抽象类可以有抽象类的子类

public class AbstractTest01 {public static void main(String[] args) {new a();//错误,不可创建对象}
}
//定义一个抽象类
abstract  class a{}
//不是抽象类可以继承抽象类并且实例化
class b extends a{}//抽象子类可以继承抽象类
abstract class c extends a{}

抽象类无法实例化,但抽象类有构造函数可以供子类使用
如果抽象类定义了有参构造函数而没有无参构造函数,子类定义找不到无参构造函数会出错,抽象类无法实例化调用对象(new对象),但是有构造函数可以给子类使用

abstract  class a{}class b extends a{public b(){super();}
}

抽象方法表示没有实现的方法,没有方法体的方法

  • 没有方法体,以分号结尾
  • 前面的修饰符列表中有abstract关键字
    比如public abstract void dosome();

但是java语言中凡是没有方法体的方法都是抽象方法(×)
因为Object类中就有很多方法都没有方法体,都是以“;”结尾的,但他们都不是抽象方法

public class AbstractTest03 {public static void main(String[] args) {//此时不能再 new Employee 了Person p = new Employee();} }
abstract class Person {private String name;public void setName(String name) {this.name = name;}public String getName() {return name;}//此方法各个子类都可以使用public void commonMethod1() {System.out.println("---------commonMethod1-------");}//采用 abstract 定义抽象方法public abstract void printInfo();
}
abstract class Employee extends Person {//再次声明该方法为抽象的public abstract void printInfo();
}
class Student extends Person {//实现抽象的方法public void printInfo() {System.out.println("Student.printInfo()");} }

1.3 接口

接口是特殊的抽象类,类与类是继承extends,类与接口是实现implements,其实都是继承

  • 接口是一种“引用数据类型”,完全抽象的,支持多继承,且一个接口可以继承多个接口,只有常量+抽象方法
  • 所有的元素都是public修饰的,抽象方法的public abstract可以省略,常量的public static final可以省略,方法不能有方法体

定义的格式

[修饰符列表] interface 接口名{}

支持多继承,且一个接口可以继承多个接口
每一个interface 都会生成一个class后缀名的文件

public class text1 {public static void main(String[] args) {}
}interface a{}interface b extends a{}interface c extends a,b{}

接口中的方法是抽象,所以不能有方法体
定义抽象方法的时候可以省略修饰符public abstract

interface a{int sum(int a,intb);//可以省略,默认系统会给你加上void dosome();//可以执行通过void dosome(){};//不可以执行通过,不能有方法体
}

常量的定义结合final 是public static final可以省路

public class text1 {public static void main(String[] args) {//定义输出的时候 需要用类名加变量 也就是a.i//而且不允许在修改常量}
}
interface a{public static final int i=100;int z=1;//z也是一个常量而不是变量 不能修改
}

1.4 接口与抽象类结合

当一个非抽象的类实现接口的话,必须将接口中所有的抽象方法全部实现(覆盖、重写)

// 特殊的抽象类,完全抽象的,叫做接口。
interface MyMath{double PI = 3.1415926;int sum(int a, int b);int sub(int a, int b);
}// 这样没问题
abstract class MyMathImpl implements MyMath {}//编译出错
class MyMathImpl implements MyMath {}//添加方法重写实现
//修正
class MyMathImpl implements MyMath {//错误:正在尝试分配更低的访问权限; 以前为public/*int sum(int a, int b){return a + b;}*/// 重写/覆盖/实现 接口中的方法(通常叫做实现。)public int sum(int a, int b){return a + b;}public int sub(int a, int b){return a - b;}
}

接口中的方法必须是public :
如果删除执行不通过,因为接口是public,如果继承后,不写public执行后是分配更低的权限这是不行的,所以要添加public

结合多态面向接口编程

public class Test02{public static void main(String[] args){//错误: MyMath是抽象的; 无法实例化//new MyMath();// 父类型的引用指向子类型的对象MyMath mm = new MyMathImpl();// 调用接口里面的方法(面向接口编程。)int result1 = mm.sum(10, 20);System.out.println(result1);int result2 = mm.sub(20, 10);System.out.println(result2);}
}

接口与接口之间可以多继承,一个类也可以多个接口

interface X{}
interface Y{}
interface Z extends X,Y{ //接口和接口支持多继承。
}

一个类可以实现多个接口
java中类和类只支持单继承。实际上单继承是为了简单而出现的,java中的接口弥补了单继承带来的缺陷

interface A{void m1();
}interface B{void m2();
}interface C{void m3();
}// 实现多个接口,其实就类似于多继承。
class D implements A,B,C{// 实现A接口的m1()public void m1(){}// 实现B接口中的m2()public void m2(){System.out.println("m2 ....");}// 实现接口C中的m3()public void m3(){}
}

强转类型的时候,需要有继承关系才可

A a = new D();
//a.m2(); // 编译报错。A接口中没有m2()方法。
B b = new D();
C c = new D();// 这个编译没问题,运行也没问题。
// 调用其他接口中的方法,你需要转型(接口转型。)
B b2 = (B)a;
b2.m2();// 直接向下转型为D可以吗?可以
D d = (D)a;
d.m2();

但如果没有继承关系不能强转

M m = new E();// 经过测试:接口和接口之间在进行强制类型转换的时候,没有继承关系,也可以强转。
// 但是一定要注意,运行时可能会出现ClassCastException异常。
// 编译没问题,运行有问题。
K k = (K)m;if(m instanceof K){K k = (K)m;interface K{}interface M{}class E implements M{}

继承和实现同时存在
extends在前 implements在后

public class Test04{public static void main(String[] args){// 创建对象(表面看Animal类没起作用!)Flyable f = new Cat(); //多态。f.fly();}
}// 动物类:父类
class Animal{}interface Flyable{void fly();
}// 动物类子类:猫类
// Flyable是一个接口,是一对翅膀的接口,通过接口插到猫身上,让猫变的可以飞翔。
class Cat extends Animal implements Flyable{public void fly(){System.out.println("飞猫起飞,翱翔太空的一只猫,很神奇,我想做一只猫!!");}
}

抽象类和接口的区别:

  • 抽象类是半抽象的,有构造方法,只允许出现常量和抽象方法。类和类之间只能单继承,一个抽象类只能继承一个类(单继承)。

  • 接口是完全抽象的,没有构造方法,接口和接口之间支持多继承,一个类可以同时实现多个接口

1.5 接口的作用

接口在开发中的作用,类似于多态在开发中的作用

  • 多态:面向抽象编程,不要面向具体编程。降低程序的耦合度。提高程序的扩展力
class Fish implements Flyable{ //没写extends,也是有的,默认继承Object。public void fly(){System.out.println("我是六眼飞鱼(流言蜚语)!!!");}/*
public class Master{public void feed(Dog d){}public void feed(Cat c){}//假设又要养其它的宠物,那么这个时候需要再加1个方法。(需要修改代码了)//这样扩展力太差了,违背了OCP原则(对扩展开放,对修改关闭。)
}
*/public class Master{public void feed(Animal a){// 面向Animal父类编程,父类是比子类更抽象的。//所以我们叫做面向抽象编程,不要面向具体编程。//这样程序的扩展力就强。}
}
  • 接口的使用离不开多态机制。(接口+多态才可以达到降低耦合度。)
  • 接口可以解耦合

私有属性最好是get和set,放到外围访问不了,所以尽量调用get和set执行

类和类之间的关系有继承、关联、接口等。

1.6 包和import

psckage:

  • package出现在java源文件第一行
  • 编译:javac -d . xxx.java
  • 运行:java 完整类名
javac -d . HelloWorld.java// javac 负责编译的命令
//-d 带包编译
//  . 代表编译之后生成的东西放到当前目录下(点代表当前目录)
//  HelloWorld.java  被编译的java文件名。

import:
import什么时候使用:

  • A类中使用B类。
  • A和B类都在同一个包下。不需要import。
  • A和B类不在同一个包下。需要使用import
  • java.lang.*;这个包下的类不需要使用import导入(system输出语句也在这个包下)

import语句只能出现在package语句之下,class声明语句之上。

1.7 访问控制权限

访问控制权限有四个:

  • private 表示私有的,只能在本类中访问
  • public 表示公开的,在任何位置都可以访问
  • “默认”表示只能在本类,以及同包下访问
  • protected表示只能在本类、同包、子类中访问
访问控制修饰符 本类 同包 子类 任意位置
private 可以 不可以 不可以 不可以
public 可以 可以 可以 可以
protected 可以 可以 可以 不可以
默认 可以 可以 不可以 不可以

访问修饰符可以修饰的属性可以修饰

  • 属性(4个都能用)
  • 方法(4个都能用)
  • 类(public和默认能用,其它不行。)
  • 接口(public和默认能用,其它不行。)

1.8 Object类

主要讲解以下常用类:
protected Object clone() // 负责对象克隆的。
int hashCode() // 获取对象哈希值的一个方法。
boolean equals(Object obj) // 判断两个对象是否相等
String toString() // 将对象转换成字符串形式
protected void finalize() // 垃圾回收器负责调用的方法

1. toString()类
可查看我之前写的一篇文章
java中Arrays.toString()详细分析(全)

本身的源代码是

public String toString() {return this.getClass().getName() + "@" + Integer.toHexString(hashCode());
}

输出的结果是一个类名+16进制。
所以我们要改写此方法输出
完整代码如下:
可以改写toString()后 直接调用这个即可,不用在重新赋值给字符串在输出

public class Test01{public static void main(String[] args){MyTime t1 = new MyTime(1970, 1, 1);// 一个日期对象转换成字符串形式的话String s1 = t1.toString();//MyTime类重写toString()方法之前//System.out.println(s1); // MyTime@28a418fc//MyTime类重写toString()方法之后System.out.println(s1); // 1970年1月1日//System.out.println(t1.toString()); //1970年1月1日// 注意:输出引用的时候,会自动调用该引用的toString()方法。System.out.println(t1);}
}
class MyTime{int year;int month;int day;public MyTime(){}public MyTime(int year, int month, int day){this.year = year;this.month = month;this.day = day;}// 重写toString()方法public String toString(){//return this.year + "年" + this.month + "月" + this.day + "日";return this.year + "/" + this.month + "/" + this.day;}
}

2. equals()类
通过equals方法来判断两个对象是否相等
在Object类中的equals方法当中,默认采用的是==判断两个java对象是否相等
而判断的是两个java对象的内存地址,我们应该判断两个java对象的内容是否相等。需要子类重写equals

源码如下:

equals方法的源代码
public boolean equals(Object obj) {return (this == obj);
}

判断两个java对象是否相等,不能使用“==”,因为比较的是两个对象的内存地址
所以需要比较内容,需要改写equals

public boolean equals(Object obj) {// 当年相同,月相同,并且日也相同的时候,表示两个日期相同。两个对象相等。// 获取第一个日期的年月日int year1 = this.year;int month1 = this.month;int day1 = this.day;// 获取第二个日期的年月日//int year2 = obj.year;//int month2 = obj.month;//int day2 = obj.day;if(obj instanceof MyTime){MyTime t = (MyTime)obj;int year2 = t.year;int month2 = t.month;int day2 = t.day;if(year1 == year2 && month1 == month2 && day1 == day2){return true;}}// 程序能够执行到此处表示日期不相等。return false;
}

但如果传进了null指针,可以运行,只不过效率会低

MyTime t4 = null;
System.out.println(t1.equals(t4)); //false

改进后的equals方法,一开始增加一些判断条件
obj是空指针,obj不是比较对象,地址是否一样(不是null,是比较对象类型)

// 改良equals方法
public boolean equals(Object obj) {// 如果obj是空,直接返回falseif(obj == null){return false;}// 如果obj不是一个MyTime,没必要比较了 ,直接返回falseif(!(obj instanceof MyTime)){return false;}// 如果this和obj保存的内存地址相同,没必要比较了,直接返回true。// 内存地址相同的时候指向的堆内存的对象肯定是同一个。if(this == obj){return true;}// 程序能够执行到此处说明什么?// 说明obj不是null,obj是MyTime类型。MyTime t = (MyTime)obj;if(this.year == t.year && this.month == t.month && this.day == t.day){return true;}// 程序能到这里返回falsereturn false;
}

整理以上代码并合并
改进后的代码如下

public boolean equals(Object obj) {if(obj == null || !(obj instanceof MyTime)){return false;}if(this == obj){return true;}MyTime t = (MyTime)obj;return this.year == t.year && this.month == t.month && this.day == t.day ;
}

或者直接通过alt+insert生成idea的equals和toString快捷键


以上都是判断对象是否相等,那String类的字符串是否也可以用==号判断?

public class Test03{public static void main(String[] args){// 大部分情况下,采用这样的方式创建字符串对象String s1 = "hello";String s2 = "abc";// 实际上String也是一个类。不属于基本数据类型。// 既然String是一个类,那么一定存在构造方法。String s3 = new String("Test1");String s4 = new String("Test1");// new两次,两个对象内存地址,s3保存的内存地址和s4保存的内存地址不同。// == 判断的是内存地址。不是内容。System.out.println(s3 == s4); // false// 比较两个字符串能不能使用双等号?// 不能,必须调用equals方法。// String类已经重写equals方法了。System.out.println(s3.equals(s4)); // true// String类有没有重写toString方法呢?String x = new String("码农研究僧");// 如果String没有重写toString()方法,输出结果:java.lang.String@十六进制的地址// 经过测试:String类已经重写了toString()方法。System.out.println(x.toString()); //码农研究僧System.out.println(x); //码农研究僧}
}

故String对象比较的时候必须用equals方法

int no; //基本数据类型,比较时使用:==// 所在学校
String school; //引用数据类型,比较时使用:equals方法。public boolean equals(Object obj){if(obj == null || !(obj instanceof Student)) return false;if(this == obj) return true;Student s = (Student)obj;return this.no == s.no && this.school.equals(s.school);
}

完整代码演示:

// equals方法重写的时候要彻底。public class Test05{public static void main(String[] args){// 多态(自动类型转换。)Object o1 = new String("hello world!");Object o2 = new User();Object o3 = new Address();User u1 = new User("zhangsan", new Address("北京","大兴区","11111"));User u2 = new User("zhangsan", new Address("北京","大兴区","11111"));System.out.println(u1.equals(u2)); // trueUser u3 = new User("zhangsan", new Address("北京","朝阳区","11112"));System.out.println(u1.equals(u3)); // false}
}class User{// 用户名String name; // 用户的住址Address addr;public User(){}public User(String name, Address addr){this.name = name;this.addr = addr;}// 重写equals方法// 重写规则:当一个用户的用户名和家庭住址都相同,表示同一个用户。// 这个equals判断的是User对象和User对象是否相等。public boolean equals(Object obj){// 用户名和用户名相同,住址和住址相同的时候,认定是同一个用户。if(obj == null || !(obj instanceof User)) return false;if(this == obj) return true;User u = (User)obj;if(this.name.equals(u.name) && this.addr.equals(u.addr)){return true;}return false;}
}class Address{String city;String street;String zipcode;public Address(){}public Address(String city,String street,String zipcode){this.city = city;this.street = street;this.zipcode = zipcode;}// 注意:这里并没有重写equals方法。// 这里的equals方法判断的是:Address对象和Address对象是否相等。public boolean equals(Object obj){if(obj == null || !(obj instanceof Address)) return false;if(this == obj) return true;// 怎么算是家庭住址相同呢?// 城市相同,街道相同,邮编相同,表示相同。Address a = (Address)obj;if(this.city.equals(a.city) && this.street.equals(a.street) && this.zipcode.equals(a.zipcode)){return true;}return false;}
}

3. finalize方法
jdk13后已经过时了

  • 这个方法是protected修饰的
  • 源代码是protected void finalize() throws Throwable { }
  • 手动调用,JVM的垃圾回收器负责调用这个方法
  • finalize()只需要重写,重写完将来自动会有程序来调用
  • 垃圾回收器不是轻易启动,可能垃圾太少或者时间不对
public class Test06{public static void main(String[] args){// 创建对象Person p = new Person();p = null;}
}class Person{// 重写finalize()方法// Person类型的对象被垃圾回收器回收的时候,垃圾回收器负责调用:p.finalize();protected void finalize() throws Throwable {// this代表当前对象System.out.println(this + "即将被销毁!");}}

4. finalize方法

hashCode()方法返回的是哈希码,实际上就是一个java对象的内存地址,经过哈希算法,得出的一个值public native int hashCode();

public class Test07{public static void main(String[] args){Object o = new Object();int hashCodeValue = o.hashCode();// 对象内存地址经过哈希算法转换的一个数字。可以等同看做内存地址。System.out.println(hashCodeValue); //798154996}
}

1.9 匿名内部类

  • 内部类:在类的内部又定义了一个新的类。被称为内部类
  • 可读性差,复杂,乱
  • 内部类的分类:
    静态内部类:类似于静态变量
    实例内部类:类似于实例变量
    局部内部类:类似于局部变量
class Test01{// 该类在类的内部,所以称为内部类// 由于前面有static,所以称为“静态内部类”static class Inner1{}
}
  • 匿名:没有名字的类
// main方法,入口
public static void main(String[] args){// 调用MyMath中的mySum方法。MyMath mm = new MyMath();/*Compute c = new ComputeImpl();mm.mySum(c, 100, 200);*///合并(这样写代码,表示这个类名是有的。类名是:ComputeImpl)//mm.mySum(new ComputeImpl(), 100, 200);mm.mySum(new Compute(){public int sum(int a, int b){return a + b;}, 200, 300);}}// 负责计算的接口
interface Compute{ // 抽象方法int sum(int a, int b);
}// 你自动会在这里编写一个Compute接口的实现类class ComputeImpl implements Compute{// 对方法的实现public int sum(int a, int b){return a + b;}
}// 数学类
class MyMath{// 数学求和方法public void mySum(Compute c, int x, int y){int retValue = c.sum(x, y);System.out.println(x + "+" + y + "=" + retValue);}
}


2. 数组

可结合我之前写的一篇文章,比较全,这一数组章节为综合阐述梗概
java数组的定义与使用

  • 数组是一种引用数据类型,数组对象是堆内存当中
  • 数组元素的类型可以是基本类型,也可以是引用类型,但同一个数组只能是同一种类型,数组中存储的元素类型统一
  • 数组的长度在数组对象创建后就确定无法修改
  • 外数组还包括一个成员属性 length表示长度
  • 数组中的元素内存地址(存储的每一个元素都是有规则的挨着排列的)是连续的。内存地址连续

优点
查询/查找/检索某个下标上的元素时效率极高。
第一:每一个元素的内存地址在空间存储上是连续的。
第二:每一个元素类型相同,所以占用空间大小一样。
第三:知道第一个元素内存地址,知道每一个元素占用空间的大小,又知道下标,所以通过一个数学表达式就可以计算出某个下标上元素的内存地址。直接通过内存地址定位元素,所以数组的检索效率是最高的。
缺点:
第一:由于为了保证数组中每个元素的内存地址连续,所以在数组上随机删除或者增加元素的时候,效率较低,因为随机增删元素会涉及到后面元素统一向前或者向后位移的操作。
第二:很难在内存空间上找到一块特别大的连续的内存空间。

2.1 一维数组的声明和使用

数组声明的具体格式

  • 数组元素的类型[] 变量名称(比较常用这一种)
  • 数组元素的类型 变量名称[]
int [] a;
Student[] stu;//在一行中也可以声明多个数组
int[] a, b, c;

基本常量数组创建的具体格式

  • 使用 new 操作符来创建数组,new 数组元素的数据类型[数组元素的个数]
  • 楚数组为引用类型,它在堆中分配

数组下标不可越界,可用遍历数组进行输出以及赋值

int[] data = new int[5];for (int i=0; i<data.length; i++) {System.out.println(data[i]);
}

引用类型数组创建的具体格式
采用引用类型的数组,

public class ArrayTest02 {public static void main(String[] args) {//声明引用类型的数组Student[] student = new Student[2];
//出现空指针
//因为引用类型的数组,它采用 null 作为默认的初始化值student[0].id = 1001;student[0].name = "张三";student[1].id = 1002;student[1].name = "李四";} }
class Student {int id;String name;
}    

静态数组与动态数组如何选择定义

  • 确定数组中存储哪些具体的元素时,采用静态初始化方式。
  • 不确定将来数组中存储哪些数据,你可以采用动态初始化的方式,预先分配内存空间。
//静态初始化语法格式:int[] array = {100, 2100, 300, 55};
//动态初始化语法格式:int[] array = new int[5]; // 这里的5表示数组的元素个数。// 初始化一个5个长度的int类型数组,每个元素默认值0String[] names = new String[6]; // 初始化6个长度的String类型数组,每个元素默认值null。

匿名数组直接输出即可new int[3] ,默认是系统初始化值

// 如果直接传递一个静态数组的话,语法必须这样写。
printArray(new int[]{1,2,3});

静态初始化数组默认是0而不是null,留给用户操作初始化
结合多态以及接口的使用,在静态数组和动态数组的调用

public class  ArrayTest07 {public static void main(String[] args) {// a是一个数组// a[0] 是数组中的一个元素。// a[1] 是数组中的一个元素。int[] a = {100, 200, 300};System.out.println(a[1]);//a[2] = 400;int[] array = {1,2,3};for (int i = 0; i < array.length; i++) {/*int temp = array[i];System.out.println(temp);*/System.out.println(array[i]);}// 创建一个Animal类型的数组Animal a1 = new Animal();Animal a2 = new Animal();Animal[] animals = {a1, a2};// 对Animal数组进行遍历for (int i = 0; i < animals.length; i++) {/*Animal a = animals[i];a.move();*/// 代码合并animals[i].move(); // 这个move()方法不是数组的。是数组当中Animal对象的move()方法。}// 动态初始化一个长度为2的Animal类型数组。Animal[] ans = new Animal[2];// 创建一个Animal对象,放到数组的第一个盒子中。ans[0] = new Animal();// Animal数组中只能存放Animal类型,不能存放Product类型。//ans[1] = new Product();// Animal数组中可以存放Cat类型的数据,因为Cat是一个Animal。// Cat是Animal的子类。ans[1] = new Cat();// 创建一个Animal类型的数组,数组当中存储Cat和BirdCat c = new Cat();Bird b = new Bird();Animal[] anis = {c, b};//Animal[] anis = {new Cat(), new Bird()}; // 该数组中存储了两个对象的内存地址。for (int i = 0; i < anis.length; i++){// 这个取出来的可能是Cat,也可能是Bird,不过肯定是一个Animal// 如果调用的方法是父类中存在的方法不需要向下转型。直接使用父类型引用调用即可。//anis[i]//Animal an = anis[i];//an.move();//Animal中没有sing()方法。//anis[i].sing();// 调用子对象特有方法的话,需要向下转型!!!if(anis[i] instanceof Cat){Cat cat = (Cat)anis[i];cat.catchMouse();}else if(anis[i] instanceof Bird){Bird bird = (Bird)anis[i];bird.sing();}}}
}class Animal{public void move(){System.out.println("Animal move...");}
}// 商品类
class Product{}// Cat是子类
class Cat extends Animal {public void move(){System.out.println("猫在走猫步!");}// 特有方法public void catchMouse(){System.out.println("猫抓老鼠!");}
}// Bird子类
class Bird extends Animal {public void move(){System.out.println("Bird Fly!!!");}// 特有的方法public void sing(){System.out.println("鸟儿在歌唱!!!");}
}

数组扩容拷贝
扩容是先新建一个大容量的数组,然后将小容量数组中的数据一个一个拷贝到大数组当中,数组扩容效率较低。因为涉及到拷贝的问题。所以在以后的开发中请注意:尽可能少的进行数组的拷贝
1.拷贝基本类型变量

// 拷贝源(从这个数组中拷贝)
int[] src = {1, 11, 22, 3, 4};// 拷贝目标(拷贝到这个目标数组上)
int[] dest = new int[20]; // 动态初始化一个长度为20的数组,每一个元素默认值0System.arraycopy(src, 0, dest, 0, src.length);
for (int i = 0; i < dest.length; i++) {System.out.println(dest[i]);
}

输出的结果为src的结果并且之后的空位置是系统默认赋值0
2.拷贝引用类型变量
如果是引用类型数组同样也可以拷贝,系统默认赋值为null

// 数组中如果存储的元素是引用,可以拷贝吗?当然可以。
String[] strs = {"hello", "world!", "study", "java", "oracle", "mysql", "jdbc"};
String[] newStrs = new String[20];
System.arraycopy(strs, 0, newStrs, 0, strs.length);
for (int i = 0; i < newStrs.length; i++) {System.out.println(newStrs[i]);
}

数组是objects同样也可以拷贝
具体拷贝的是一个数组的地址

Object[] objs = {new Object(), new Object(), new Object()};
Object[] newObjs = new Object[5];
// 思考一下:这里拷贝的时候是拷贝对象,还是拷贝对象的地址。(地址。)
System.arraycopy(objs, 0, newObjs, 0, objs.length);
for (int i = 0; i < newObjs.length; i++) {System.out.println(newObjs[i]);
}

截图如下

2.2 二维数组的声明和使用

  • 二维数组其实是一个特殊的一维数组,特殊在这个一维数组当中的每一个元素是一个一维数组
  • 维数组是一个特殊的二维数组,特殊在这个二维数组中每一个元素是一个一维数组
  • 以此类推同理
int[][] a = {{100, 200, 300},{30, 20, 40, 50, 60},{6, 7, 9, 1},{0}
};
System.out.println(a.length); // 4
System.out.println(a[0].length); // 3
System.out.println(a[1].length); // 5
System.out.println(a[2].length); // 4
System.out.println(a[3].length); // 1

二维数组的遍历
伪代码如下

// 遍历二维数组
for(int i = 0; i < array.length; i++){ // 外层循环3次。(负责纵向。)String[] 一维数组 = array[i];for(int j = 0; j < 一维数组.length; j++){System.out.print(一维数组[j] + " ");}System.out.println();
}

结合完整代码实例的演示

public class ArrayTest11 {public static void main(String[] args) {// 二维数组String[][] array = {{"java", "oracle", "c++", "python", "c#"},{"张三", "李四", "王五"},{"lucy", "jack", "rose"}};for(int i = 0; i < array.length; i++){ // 外层循环3次。(负责纵向。)for(int j = 0; j < array[i].length; j++){// 负责遍历一维数组System.out.print(array[i][j] + " ");}System.out.println(); // 输出换行符}}
}

动态初始化二维数组

// 3行4列。
// 3个一维数组,每一个一维数组当中4个元素。
int[][] array = new int[3][4];

结合静态初始化二维数组进行比较

// 静态初始化
int[][] a = {{1,2,3,4},{4,5,6,76},{1,23,4}};
// 没有这种语法
//printArray({{1,2,3,4},{4,5,6,76},{1,23,4}});// 可以这样写。
printArray(new int[][]{{1,2,3,4},{4,5,6,76},{1,23,4}});public static void printArray(int[][] array){// 遍历二维数组。for (int i = 0; i < array.length; i++) {for (int j = 0; j < array[i].length; j++) {System.out.print(array[i][j] + " ");}System.out.println();}}

2.3 排序查找算法

1.冒泡排序算法
每一次循环结束之后,都要找出最大的数据,放到参与比较的这堆数据的最右边。(冒出最大的那个气泡。),左边的数字和右边的数字比对,当左边 > 右边的时候,交换位置。

public class BubbleSort {public static void main(String[] args) {// 这是int类型的数组对象//int[] arr = {3, 2, 7, 6, 8};int[] arr = {9, 8, 10, 7, 6, 0, 11};int count = 0;for(int i = arr.length-1; i > 0; i--){for(int j = 0; j < i; j++){// 不管是否需要交换位置,总之是要比较一次的。//count++;if(arr[j] > arr[j+1]){// 交换位置。// arr[j] 和 arr[j+1] 交换int temp;temp = arr[j];arr[j] = arr[j+1];arr[j+1] = temp;count++;}}}//System.out.println("比较次数:" + count);System.out.println("交换位置的次数:" + count); //13// 输出结果for (int i = 0; i < arr.length; i++) {System.out.println(arr[i]);}}
}

2.选择排序算法
每一次从这堆“参与比较的数据当中”找出最小值,这个最小值和“参与比较的这堆最前面的元素”交换位置。选择排序比冒泡排序好在:每一次的交换位置都是有意义的。

public class SelectSort {public static void main(String[] args) {//int[] arr = {3, 1, 6, 2, 5};int[] arr = {9, 8, 10, 7, 6, 0, 11};int count = 0;int count2 = 0;for(int i = 0; i < arr.length - 1; i++){int min = i;for(int j = i+1; j < arr.length; j++){count++;if(arr[j] < arr[min]){min = j; //最小值的元素下标是j}}// 当i和min相等时,表示最初猜测是对的。// 当i和min不相等时,表示最初猜测是错的,有比这个元素更小的元素,// 需要拿着这个更小的元素和最左边的元素交换位置。if(min != i){// 表示存在更小的数据// arr[min] 最小的数据// arr[i] 最前面的数据int temp;temp = arr[min];arr[min] = arr[i];arr[i] = temp;count2++;}}// 冒泡排序和选择排序实际上比较的次数没变。// 交换位置的次数减少了。System.out.println("比较次数" + count); // 21System.out.println("交换次数:" + count2); // 5// 排序之后遍历for (int i = 0; i < arr.length; i++) {System.out.println(arr[i]);}}
}

3.二分查找算法
二分法查找算法是基于排序的基础之上。(没有排序的数据是无法查找的。)
可具体查看我上一篇文章
【leetcode】二分查找

public class ArrayUtil {public static void main(String[] args) {int[] arr = {100,200,230,235,600,1000,2000,9999};// 找出arr这个数组中200所在的下标。// 调用方法int index = binarySearch(arr, 230);System.out.println(index == -1 ? "该元素不存在!" : "该元素下标" + index);}/*** 从数组中查找目标元素的下标。* @param arr 被查找的数组(这个必须是已经排序的。)* @param dest 目标元素* @return -1表示该元素不存在,其它表示返回该元素的下标。*/public static int binarySearch(int[] arr, int dest) {// 开始下标int begin = 0;// 结束下标int end = arr.length - 1;// 开始元素的下标只要在结束元素下标的左边,就有机会继续循环。while(begin <= end) {// 中间元素下标int mid = (begin + end) / 2;if (arr[mid] == dest) {return mid;} else if (arr[mid] < dest) {// 目标在“中间”的右边// 开始元素下标需要发生变化(开始元素的下标需要重新赋值)begin = mid + 1; // 一直增} else {// arr[mid] > dest// 目标在“中间”的左边// 修改结束元素的下标end = mid - 1; // 一直减}}return -1;}}

3. 常用类

本章节的类中阐述String、StringBuffer、基础类型对应的 8 个包装类、日期相关类、数字相关类、 Random、 Enum

3.1 String类

  • String表示字符串类型,属于引用数据类型,不属于基本数据类型
  • 在java中随便使用双引号括起来的都是String对象。如:“abc”,“def”,“hello world!”,这是3个String对象
  • 双引号括起来的字符串,是不可变的
  • 双引号括起来的字符串都是直接存储在“方法区”的“字符串常量池”当中的
String s1 = "abcdef";
String s2 = "abcdef" + "xy";
String s3 = new String("xy");// i变量中保存的是100这个值。
int i = 100;
// s变量中保存的是字符串对象的内存地址。
// s引用中保存的不是"abc",是0x1111
// 而0x1111是"abc"字符串对象在“字符串常量池”当中的内存地址。
String s = "abc";

内存地址如图所示

强化new对象调用的时候地址显示

User user = new User(110, "张三");


String类的字符串在双引号和新建对象的地址判断

public class StringTest02 {public static void main(String[] args) {String s1 = "hello";// "hello"是存储在方法区的字符串常量池当中// 所以这个"hello"不会新建。(因为这个对象已经存在了!)String s2 = "hello";// 分析结果是true还是false?// == 双等号比较的是不是变量中保存的内存地址?是的。System.out.println(s1 == s2); // trueString x = new String("xyz");String y = new String("xyz");// 分析结果是true还是false?// == 双等号比较的是不是变量中保存的内存地址?是的。System.out.println(x == y); //false// 通过这个案例的学习,我们知道了,字符串对象之间的比较不能使用“==”// "=="不保险。应该调用String类的equals方法。// String类已经重写了equals方法,以下的equals方法调用的是String重写之后的equals方法。System.out.println(x.equals(y)); // trueString k = new String("testString");//String k = null;// "testString"这个字符串可以后面加"."呢?// 因为"testString"是一个String字符串对象。只要是对象都能调用方法。System.out.println("testString".equals(k)); // 建议使用这种方式,因为这个可以避免空指针异常。System.out.println(k.equals("testString")); // 存在空指针异常的风险。不建议这样写。}
}

System.out.println(x);
System.out.println(y);
以上两个变量输出的字符串是值,是因为改写了toString类
但是如果比较对象即两个变量,比较的是地址值而不是值

通过比较可以得出字符串对象之间的比较不能使用“==”


补充:面试题
一共新建了多少个对象?

/*
String s1 = new String("hello");
String s2 = new String("hello");

方法区字符串常量池中有1个:“hello”,堆内存当中有两个String对象。一共3个


关于String类中的构造方法:

  • String s = new String("");
  • 第二个:String s = “”; 最常用
  • 第三个:String s = new String(char数组);
  • 第四个:String s = new String(char数组,起始下标,长度);
  • 第五个:String s = new String(byte数组);
  • 第六个:String s = new String(byte数组,起始下标,长度)
String s1 =  "hello world!";
String s2 = new String(bytes);
String s3 = new String(bytes, 1, 2);
char[] chars = {'我','是','中','国','人'};
String s5 = new String(chars, 2, 3);
String s6 = new String("helloworld!");

额外补充

// s1这个变量中保存的是一个内存地址。
// 按说以下应该输出一个地址。
// 但是输出一个字符串,说明String类已经重写了toString()方法。
System.out.println(s1);//hello world!
System.out.println(s1.toString()); //hello world!// 前面说过:输出一个引用的时候,会自动调用toString()方法,默认Object的话,会自动输出对象的内存地址。
// 通过输出结果我们得出一个结论:String类已经重写了toString()方法。
// 输出字符串对象的话,输出的不是对象的内存地址,而是字符串本身。
System.out.println(s2.toString()); //abc
System.out.println(s2); //abc

3.11 charAt方法

具体用法可看我上一篇文章
java中charAt用法详细分析(全)
返回指定索引值

//1(掌握).char charAt(int index)
char c = "中国人".charAt(1); // "中国人"是一个字符串String对象。只要是对象就能“点.”
System.out.println(c); // 国

3.12 compare To方法

返回的是一个int值,按照字典顺序的比较值
返回值等于,代表相等
返回值小于0,前小后大
返回值大于0,前大后小
每一个字母一个字母比较,如果有结果了之后,后面的内容就不比较了,类似英语的字典。

// 字符串之间比较大小不能直接使用 > < ,需要使用compareTo方法。
int result = "abc".compareTo("abc");
System.out.println(result); //0(等于0) 前后一致  10 - 10 = 0int result2 = "abcd".compareTo("abce");
System.out.println(result2); //-1(小于0) 前小后大 8 - 9 = -1int result3 = "abce".compareTo("abcd");
System.out.println(result3); // 1(大于0) 前大后小 9 - 8 = 1// 拿着字符串第一个字母和后面字符串的第一个字母比较。能分胜负就不再比较了。
System.out.println("xyz".compareTo("yxz")); // -1

3.13 contains方法

判断前面的字符串中是否有包含后面的字符串.boolean contains(CharSequence s)

 // 判断前面的字符串中是否包含后面的子字符串。
System.out.println("HelloWorld.java".contains(".java")); // trueSystem.out.println("http://www.baidu.com".contains("https://")); // false

3.14 endsWith方法

判断当前字符串是否以某个子字符串结尾. boolean endsWith(String suffix)

System.out.println("test.txt".endsWith(".java")); // false
System.out.println("test.txt".endsWith(".txt")); // true
System.out.println("fdsajklfhdkjlsahfjkdsahjklfdss".endsWith("ss")); // true

3.15 equals方法

比较两个字符串必须使用equals方法,不能使用“==”,.boolean equals(Object anObject)

// equals只能看出相等不相等。
// compareTo方法可以看出是否相等,并且同时还可以看出谁大谁小。
System.out.println("abc".equals("abc")); // true

3.16 equalsIgnoreCase方法

判断两个字符串是否相等,并且同时忽略大小写。.boolean equalsIgnoreCase(String anotherString)

System.out.println("ABc".equalsIgnoreCase("abC")); // true

3.17 getBytes方法

将字符串对象转换成字节数组.byte[] getBytes()

byte[] bytes = "abcdef".getBytes();
for(int i = 0; i < bytes.length; i++){System.out.println(bytes[i]); //97 98 99 100 101 102
}

3.18 indexOf与lastIndexOf方法

判断某个子字符串在当前字符串中第一次出现处的索引(下标).int indexOf(String str)

System.out.println("oraclejavac++.netc#phppythonjavaoraclec++".indexOf("java")); // 6

判断某个子字符串在当前字符串中最后一次出现的索引(下标).int lastIndexOf(String str)

System.out.println("oraclejavac++javac#phpjavapython".lastIndexOf("java")); //22

3.19 isEmpty()方法

判断某个字符串是否为“空字符串”。底层源代码调用的应该是字符串的length()方法。.boolean isEmpty()
如果为null会出现空指针异常

//String s = ""; //true
String s = "a"; //false
System.out.println(s.isEmpty());

补充判断数组长度是length属性,判断字符串长度是length()方法。

// 10(掌握). int length()
// 面试题:判断数组长度和判断字符串长度不一样System.out.println("abc".length()); // 3System.out.println("".length()); // 0

3.20 replace方法

替换某一部分. String replace(CharSequence target, CharSequence replacement)
String的父接口就是:CharSequence


String newString = "http://www.baidu.com".replace("http://", "https://");
System.out.println(newString); //https://www.baidu.com
// 把以下字符串中的“=”替换成“:”
String newString2 = "name=zhangsan&password=123&age=20".replace("=", ":");
System.out.println(newString2); //name:zhangsan&password:123&age:20

3.21 split方法

拆分字符串.String[] split(String regex)

String[] ymd = "1980-10-11".split("-"); //"1980-10-11"以"-"分隔符进行拆分。
for(int i = 0; i < ymd.length; i++){System.out.println(ymd[i]);
}
String param = "name=zhangsan&password=123&age=20";
String[] params = param.split("&");
for(int i = 0; i <params.length; i++){System.out.println(params[i]);// 可以继续向下拆分,可以通过“=”拆分。
}

3.22 startsWith方法

判断某个字符串是否以某个子字符串开始。、boolean startsWith(String prefix)

System.out.println("http://www.baidu.com".startsWith("http")); // true
System.out.println("http://www.baidu.com".startsWith("https")); // false

3.23 substring方法

截取字符串 .String substring(int beginIndex) 参数是起始下标

System.out.println("http://www.baidu.com".substring(7)); //www.baidu.com

多一个起始位置和终止位置.String substring(int beginIndex, int endIndex)

// beginIndex起始位置(包括)
// endIndex结束位置(不包括)
System.out.println("http://www.baidu.com".substring(7, 10)); //www

3.24 toCharArray方法

具体可看我上一篇文章
java中toCharArray用法详细分析(全)
将字符串转换成char数组.char[] toCharArray()

char[] chars = "我是中国人".toCharArray();
for(int i = 0; i < chars.length; i++){System.out.println(chars[i]);
}

3.25 toLowerCase与toUpperCase方法

大小写字母的转换如下


// 转换为小写。
System.out.println("ABCDefKXyz".toLowerCase());
//转换为大写
System.out.println("ABCDefKXyz".toUpperCase());

3.26 trim方法

去除字符串前后空白.String trim();

System.out.println("           hello      world             ".trim());

3.27 valueOf方法

将“非字符串”转换成“字符串”
String中只有一个方法是静态的,不需要new对象

//String s1 = String.valueOf(true);
//String s1 = String.valueOf(100);
//String s1 = String.valueOf(3.14);

如果没有重写对象的toString方法,输出对象@地址

String s1 = String.valueOf(new Customer());
//System.out.println(s1); // 没有重写toString()方法之前是对象内存地址 对象@地址System.out.println(s1); //码农研究僧!!!
class Customer {// 重写toString()方法@Overridepublic String toString() {return "码农研究僧!!!!";}
}

3.2 stringbuffer类

java中的字符串是不可变的,由于频繁造成字符串的拼接,每一次拼接都会产生新字符串,这样会占用大量的方法区内存。造成内存空间的浪费String s = "abc"; s += "hello";就单纯这两行代码就会产生三个代码,abc,abchello,hello
大量的拼接可以使用两个类

  • java.lang.StringBuffer
  • java.lang.StringBuilder

StringBuffer()无参构造,构造一个其中不带字符的缓冲区,初始化容量为16个字符
StringBuffer(int capacity)构造一个不带字符,但具有指定初始化容量的字符串缓冲区

public class StringBufferTest02 {public static void main(String[] args) {// 创建一个初始化容量为16个byte[] 数组。(字符串缓冲区对象)StringBuffer stringBuffer = new StringBuffer();// 拼接字符串,以后拼接字符串统一调用 append()方法。// append是追加的意思。stringBuffer.append("a");stringBuffer.append("b");stringBuffer.append("d");stringBuffer.append(3.14);stringBuffer.append(true);// append方法底层在进行追加的时候,如果byte数组满了,会自动扩容。stringBuffer.append(100L);System.out.println(stringBuffer.toString());// 指定初始化容量的StringBuffer对象(字符串缓冲区对象)StringBuffer sb = new StringBuffer(100);sb.append("hello");sb.append("world");sb.append("hello");sb.append("kitty");System.out.println(sb);}
}

StringBuffer和StringBuilder的区别?

  • StringBuffer中的方法都有:synchronized关键字修饰。表示StringBuffer在多线程环境下运行是安全的。
  • StringBuilder中的方法都没有:synchronized关键字修饰,表StringBuilder在多线程环境下运行是不安全的。
public class StringBuilderTest01 {public static void main(String[] args) {// 使用StringBuilder也是可以完成字符串的拼接。StringBuilder sb = new StringBuilder();sb.append(100);sb.append(true);sb.append("hello");sb.append("kitty");System.out.println(sb);}
}

补充:String为什么是不可变的?StringBuilder/StringBuffer为什么是可变的呢?

源代码String类中有一个byte[]数组,这个byte[]数组采用了final修饰,因为数组一旦创建长度不可变。并且被final修饰的引用一旦指向某个对象之后,不可再指向其它对象,所以String是不可变的!“abc” 无法变成 “abcd”

源代码StringBuffer/StringBuilder内部实际上是一个byte[]数组, 这个byte[]数组没有被final修饰,StringBuffer/StringBuilder的初始化容量我记得应该是16,当存满之后会进行扩容,底层调用了数组拷贝的方System.arraycopy()…是这样扩容的。所以StringBuilder/StringBuffer适合于使用字符串的频繁拼接操作。

public class StringBufferTest04 {public static void main(String[] args) {// 字符串不可变是什么意思?// 是说双引号里面的字符串对象一旦创建不可变。String s = "abc"; //"abc"放到了字符串常量池当中。"abc"不可变。// s变量是可以指向其它对象的。// 字符串不可变不是说以上变量s不可变。说的是"abc"这个对象不可变。s = "xyz";//"xyz"放到了字符串常量池当中。"xyz"不可变。}
}

3.3 基础类型对应的 8 个包装类

入门小程序:

数字属于基本数据类型,而doSome()方法参数的类型是Object,可以传一个数字对应的包装类进去

public class IntegerTest01 {//入口public static void main(String[] args) {// 把100这个数字经过构造方法包装成对象。MyInt myInt = new MyInt(100);// doSome()方法虽然不能直接传100,但是可以传一个100对应的包装类型。doSome(myInt);}public static void doSome(Object obj){//System.out.println(obj);System.out.println(obj.toString());}
}// 这种包装类目前是我自己写的。实际开发中我们不需要自己写。
// 8种基本数据类型对应的8种包装类,SUN公司已经写好了。我们直接用。
public class MyInt {int value;public MyInt() {}public MyInt(int value) {this.value = value;}@Overridepublic String toString() {return String.valueOf(value);}
}

8种基本数据类型不够用,而8种基本数据类型对应的包装类型名是

基本数据类型 包装类型
byte java.lang.Byte(父类Number)
short java.lang.Short(父类Number)
int java.lang.Integer(父类Number)
long java.lang.Long(父类Number)
float java.lang.Float(父类Number)
double java.lang.Double(父类Number)
boolean java.lang.Boolean(父类Object)
char java.lang.Character(父类Object)

只有 java.lang.Integer(父类Number)和java.lang.Character(父类Object)这两个与基本类型不一样有所区别。
装箱拆箱的操作。都是包装好的函数
Number类中有这样的方法

  • byte byteValue() 以 byte 形式返回指定的数值。
  • abstract double doubleValue()以 double 形式返回指定的数值。
  • abstract float floatValue()以 float 形式返回指定的数值。
  • abstract int intValue()以 int 形式返回指定的数值。
  • abstract long longValue()以 long 形式返回指定的数值。
  • short shortValue()以 short 形式返回指定的数值。

这些方法其实所有的数字包装类的子类都有,这些方法是负责拆箱的。

public class IntegerTest02 {public static void main(String[] args) {// 123这个基本数据类型,进行构造方法的包装达到了:基本数据类型向引用数据类型的转换。// 基本数据类型 -(转换为)->引用数据类型(装箱)Integer i = new Integer(123);// 将引用数据类型--(转换为)-> 基本数据类型float f = i.floatValue();System.out.println(f); //123.0// 将引用数据类型--(转换为)-> 基本数据类型(拆箱)int retValue = i.intValue();System.out.println(retValue); //123}
}

3.31 自动装箱和自动拆箱

  • 自动装箱:基本数据类型自动转换成包装类。
  • 自动拆箱:包装类自动转换成基本数据类型。

有了自动拆箱之后,Number类中的方法就用不着了!而且更容易编程
只有+ - * /等运算的时候才会触发装箱和拆箱的操作,详情可看下面的代码

public class IntegerTest05 {public static void main(String[] args) {// 900是基本数据类型// x是包装类型// 基本数据类型 --(自动转换)--> 包装类型:自动装箱Integer x = 900;System.out.println(x);// x是包装类型// y是基本数据类型// 包装类型 --(自动转换)--> 基本数据类型:自动拆箱int y = x;System.out.println(y);// z是一个引用,z是一个变量,z还是保存了一个对象的内存地址。Integer z = 1000; // 等同于:Integer z = new Integer(1000);// 分析为什么这个没有报错呢?// +两边要求是基本数据类型的数字,z是包装类,不属于基本数据类型,这里会进行自动拆箱。将z转换成基本数据类型// 在java5之前你这样写肯定编译器报错。System.out.println(z + 1);Integer a = 1000; // Integer a = new Integer(1000); a是个引用,保存内存地址指向对象。Integer b = 1000; // Integer b = new Integer(1000); b是个引用,保存内存地址指向对象。// == 比较的是对象的内存地址,a和b两个引用中保存的对象内存地址不同。// == 这个运算符不会触发自动拆箱机制。System.out.println(a == b); //false}
}

java中为了提高程序的执行效率,将[-128到127]之间所有的包装对象提前创建好,放到了一个方法区的“整数型常量池”当中了,目的是只要用这个区间的数据不需要再new了,直接从整数型常量池当中取出来。

原理:x变量中保存的对象的内存地址和y变量中保存的对象的内存地址是一样的

Integer a = 128;
Integer b = 128;
System.out.println(a == b); //falseInteger x = 127;
Integer y = 127;
// == 永远判断的都是两个对象的内存地址是否相同。
System.out.println(x == y); //true
// 手动装箱
Integer x = new Integer(1000);// 手动拆箱。
int y = x.intValue();
System.out.println(y);Integer a = new Integer("中文");//出现异常

补充异常的类型:

  • 空指针异常:NullPointerException
  • 类型转换异常:ClassCastException
  • 数组下标越界异常:ArrayIndexOutOfBoundsException
  • 数字格式化异常:NumberFormatException

3.32 Integer常用方法

Integer类的构造方法,有两个:

  • Integer(int)
  • Integer(String)
// Java9之后不建议使用这个构造方法了。出现横线表示已过时。
// 将数字100转换成Integer包装类型(int --> Integer)
Integer x = new Integer(100);// 将String类型的数字,转换成Integer包装类型。(String --> Integer)
Integer y = new Integer("123");// double -->Double
Double d = new Double(1.23);// String --> Double
Double e = new Double("3.14");

获取最大值最小值
最大值Integer.MAX_VALUE
最小值Integer.MIN_VALUE
其他类型也同理

public class IntegerTest04 {public static void main(String[] args) {// 通过访问包装类的常量,来获取最大值和最小值System.out.println("int的最大值:" + Integer.MAX_VALUE);System.out.println("int的最小值:" + Integer.MIN_VALUE);System.out.println("byte的最大值:" + Byte.MAX_VALUE);System.out.println("byte的最小值:" + Byte.MIN_VALUE);}
}

string转换成数字static int parseInt(String s)

// 静态方法,传参String,返回int
//网页上文本框中输入的100实际上是"100"字符串。后台数据库中要求存储100数字,此时java程序需要将"100"转换成100数字。
int retValue = Integer.parseInt("123"); // String -转换-> int

同样其他类型参数也有类似的方法

// 照葫芦画瓢
double retValue2 = Double.parseDouble("3.14");
System.out.println(retValue2 + 1); //4.140000000000001(精度问题)float retValue3 = Float.parseFloat("1.0");
System.out.println(retValue3 + 1); //2.0

public class IntegerTest08 {public static void main(String[] args) {// String --> intint i1 = Integer.parseInt("100"); // i1是100数字System.out.println(i1 + 1); // 101// int --> StringString s2 = i1 + ""; // "100"字符串System.out.println(s2 + 1); // "1001"// int --> Integer// 自动装箱Integer x = 1000;// Integer --> int// 自动拆箱int y = x;// String --> IntegerInteger k = Integer.valueOf("123");// Integer --> StringString e = String.valueOf(k);}
}

3.4 日期相关类

  • 获取当前日期
  • String转换成日期格式
  • 日期格式转换成String

获取对象之后,通过对象调取format格式的方法

// 获取系统当前时间(精确到毫秒的系统当前时间)
// 直接调用无参数构造方法就行。
Date nowTime = new Date();System.out.println(nowTime); //Thu Mar 05 10:51:06 CST 2020

SimpleDateFormat是java.text包下的,专门负责日期格式化的
具体参数

代码参数 代表含义
yyyy 年(年是4位)
MM 月(月是2位)
dd
HH
mm
ss
SSS 毫秒(毫秒3位,最高999。1000毫秒代表1秒)

在日期格式中,除了y M d H m s S这些字符不能随便写之外,剩下的符号格式自己随意组织。

Date nowTime = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String nowTimeStr = sdf.format(nowTime);
System.out.println(nowTimeStr);

代码截图如下

以上是日期转换string字符串
下面是字符串转换成日期格式

// String --> Date
String time = "2008-08-08 08:08:08 888";
//SimpleDateFormat sdf2 = new SimpleDateFormat("格式不能随便写,要和日期字符串格式相同");
// 注意:字符串的日期格式和SimpleDateFormat对象指定的日期格式要一致。不然会出现异常:java.text.ParseException
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
Date dateTime = sdf2.parse(time);
System.out.println(dateTime); //Fri Aug 08 08:08:08 CST 2008

获取1970到现在的毫秒数System.currentTimeMillis();

// 获取自1970年1月1日 00:00:00 000到当前系统时间的总毫秒数。
long nowTimeMillis = System.currentTimeMillis();
System.out.println(nowTimeMillis);

可以统计一个方法所执行耗费的时长

// 在调用目标方法之前记录一个毫秒数
long begin = System.currentTimeMillis();
//方法;
// 在执行完目标方法之后记录一个毫秒数
long end = System.currentTimeMillis();
System.out.println("耗费时长"+(end - begin)+"毫秒");

补充System的方法

  • System.gc() 建议启动垃圾回收器
  • System.exit(0) 退出JVM

讲完Date无参构造函数,下文讲有参构造函数

// 1970-01-01 00:00:00 001
Date time = new Date(1); // 注意:参数是一个毫秒SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String strTime = sdf.format(time);
// 北京是东8区。差8个小时。
System.out.println(strTime); // 1970-01-01 08:00:00 001// 获取昨天的此时的时间。
Date time2 = new Date(System.currentTimeMillis() - 1000 * 60 * 60 * 24);
String strTime2 = sdf.format(time2);
System.out.println(strTime2); //2020-03-04 11:44:14 829

3.5 数字相关类

java.text.DecimalFormat专门负责数字格式化的。DecimalFormat df = new DecimalFormat("数字格式");
获取对象之后,通过对象调取format格式的方法
数字格式有:

  • #代表任意数字
  • , 代表千分位
  • . 代表小数点
  • 0 代表不够时补0

###,###.## 表示:加入千分位,保留2个小数

DecimalFormat df = new DecimalFormat("###,###.##");
//String s = df.format(1234.56); //"1,234.56"
String s = df.format(1234.561232);
System.out.println(s); // "1,234.56"DecimalFormat df2 = new DecimalFormat("###,###.0000"); //保留4个小数位,不够补上0
String s2 = df2.format(1234.56);
System.out.println(s2); // "1,234.5600"

BigDecimal 属于大数据,精度极高。不属于基本数据类型,属于java对象(引用数据类型)

// 这个100不是普通的100,是精度极高的100
BigDecimal v1 = new BigDecimal(100);
// 精度极高的200
BigDecimal v2 = new BigDecimal(200);
// 求和
// v1 + v2; // 这样不行,v1和v2都是引用,不能直接使用+求和。
BigDecimal v3 = v1.add(v2); // 调用方法求和。
System.out.println(v3); //300BigDecimal v4 = v2.divide(v1);
System.out.println(v4); // 2

3.6 Random类

产生随机数
先获取随机数的对象
通过对象获取随机取值的范围

// 创建随机数对象
Random random = new Random();// 随机产生一个int类型取值范围内的数字。
int num1 = random.nextInt();System.out.println(num1);

nextInt翻译为:下一个int类型的数据是101,表示只能取到100

// 产生[0~100]之间的随机数。不能产生101。
int num2 = random.nextInt(101); //不包括101
System.out.println(num2);

程序:生成随机5个数字不重复

public class RandomTest02 {public static void main(String[] args) {// 创建Random对象Random random = new Random();// 准备一个长度为5的一维数组。int[] arr = new int[5]; // 默认值都是0for(int i = 0; i < arr.length; i++){arr[i] = -1;}// 循环,生成随机数int index = 0;while(index < arr.length){// 生成随机数//int num = random.nextInt(101);//int num = random.nextInt(6); // 只能生成[0-5]的随机数!int num = random.nextInt(4); // 只能生成[0-3]的随机数!永远都有重复的,永远都凑不够5个。System.out.println("生成的随机数:" + num);// 判断arr数组中有没有这个num// 如果没有这个num,就放进去。if(!contains(arr, num)){arr[index++] = num;}}// 遍历以上的数组for(int i = 0; i < arr.length; i++){System.out.println(arr[i]);}}/*** 单独编写一个方法,这个方法专门用来判断数组中是否包含某个元素* @param arr 数组* @param key 元素* @return true表示包含,false表示不包含。*/public static boolean contains(int[] arr, int key){/*// 这个方案bug。(排序出问题了。)// 对数组进行升序//Arrays.sort(arr);// 进行二分法查找// 二分法查找的结果 >= 0说明,这个元素找到了,找到了表示存在!//return Arrays.binarySearch(arr, key) >= 0;*/for(int i = 0; i < arr.length; i++){if(arr[i] == key){// 条件成立了表示包含,返回truereturn true;}}// 这个就表示不包含!return false;}}

3.7 enum类

  • 枚举:一枚一枚可以列举出来的,才建议使用枚举类型。
  • 编译之后也是生成class文件。
  • 一种引用数据类型。
  • 每一个值可以看做是常量
enum 枚举类型名{枚举值1,枚举值2
}

结果只有两种情况的,建议使用布尔类型
结果超过两种并且还是可以一枚一枚列举出来的,建议使用枚举类型
例如

/*** 四季枚举类型*/public enum Season {/*春夏秋冬*/SPRING, SUMMER, AUTUMN, WINTER
}

具体结合的用法如下
高版本的JDK,switch支持int、String、枚举

public class SwitchTest {public static void main(String[] args) {// switch语句支持枚举类型// switch也支持String、int// 低版本的JDK,只支持int// 高版本的JDK,支持int、String、枚举。// byte short char也可以,因为存在自动类型转换。switch (Season.SPRING) {// 必须省略Season.case SPRING:System.out.println("春天");break;case SUMMER:System.out.println("夏天");break;case AUTUMN:System.out.println("秋天");break;case WINTER:System.out.println("冬天");break;}}
}

补充另外一个程序

enum Result{// SUCCESS 是枚举Result类型中的一个值// FAIL 是枚举Result类型中的一个值// 枚举中的每一个值,可以看做是“常量”SUCCESS, FAIL
}

通过这个可以设计函数
注意下面函数的区分
通过类调用枚举的属性

public class EnumTest02 {public static void main(String[] args) {Result r = divide(10, 2);System.out.println(r == Result.SUCCESS ? "计算成功" : "计算失败");}/*** 计算两个int类型数据的商。* @param a int数据* @param b int数据* @return Result.SUCCESS表示成功,Result.FAIL表示失败!*/public static Result divide(int a, int b){try {int c = a / b;return Result.SUCCESS;} catch (Exception e){return Result.FAIL;}}
}

4.异常处理

异常的处理机制可结合我上一篇的文章
java异常处理机制

本文阐述的异常处理也比较全,两者选其一也可

异常的作用:增强程序的健壮性

通过异常类创建对象
因为异常在java中以类的形式存在,每一个异常类都可以创建异常对象

// 通过“异常类”实例化“异常对象”
NumberFormatException nfe = new NumberFormatException("数字格式化异常!");// java.lang.NumberFormatException: 数字格式化异常!
System.out.println(nfe);// 通过“异常类”创建“异常对象”
NullPointerException npe = new NullPointerException("空指针异常发生了!");//java.lang.NullPointerException: 空指针异常发生了!
System.out.println(npe);

编译时异常和运行时异常

  • 都是发生在运行阶段。编译阶段异常是不会发生的
  • 只有程序运行阶段才可以new对象。因为异常的发生就是new异常对象
#mermaid-svg-OIZKCMyhGDlJap1S .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-OIZKCMyhGDlJap1S .label text{fill:#333}#mermaid-svg-OIZKCMyhGDlJap1S .node rect,#mermaid-svg-OIZKCMyhGDlJap1S .node circle,#mermaid-svg-OIZKCMyhGDlJap1S .node ellipse,#mermaid-svg-OIZKCMyhGDlJap1S .node polygon,#mermaid-svg-OIZKCMyhGDlJap1S .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-OIZKCMyhGDlJap1S .node .label{text-align:center;fill:#333}#mermaid-svg-OIZKCMyhGDlJap1S .node.clickable{cursor:pointer}#mermaid-svg-OIZKCMyhGDlJap1S .arrowheadPath{fill:#333}#mermaid-svg-OIZKCMyhGDlJap1S .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-OIZKCMyhGDlJap1S .flowchart-link{stroke:#333;fill:none}#mermaid-svg-OIZKCMyhGDlJap1S .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-OIZKCMyhGDlJap1S .edgeLabel rect{opacity:0.9}#mermaid-svg-OIZKCMyhGDlJap1S .edgeLabel span{color:#333}#mermaid-svg-OIZKCMyhGDlJap1S .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-OIZKCMyhGDlJap1S .cluster text{fill:#333}#mermaid-svg-OIZKCMyhGDlJap1S div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-OIZKCMyhGDlJap1S .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-OIZKCMyhGDlJap1S text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-OIZKCMyhGDlJap1S .actor-line{stroke:grey}#mermaid-svg-OIZKCMyhGDlJap1S .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-OIZKCMyhGDlJap1S .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-OIZKCMyhGDlJap1S #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-OIZKCMyhGDlJap1S .sequenceNumber{fill:#fff}#mermaid-svg-OIZKCMyhGDlJap1S #sequencenumber{fill:#333}#mermaid-svg-OIZKCMyhGDlJap1S #crosshead path{fill:#333;stroke:#333}#mermaid-svg-OIZKCMyhGDlJap1S .messageText{fill:#333;stroke:#333}#mermaid-svg-OIZKCMyhGDlJap1S .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-OIZKCMyhGDlJap1S .labelText,#mermaid-svg-OIZKCMyhGDlJap1S .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-OIZKCMyhGDlJap1S .loopText,#mermaid-svg-OIZKCMyhGDlJap1S .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-OIZKCMyhGDlJap1S .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-OIZKCMyhGDlJap1S .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-OIZKCMyhGDlJap1S .noteText,#mermaid-svg-OIZKCMyhGDlJap1S .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-OIZKCMyhGDlJap1S .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-OIZKCMyhGDlJap1S .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-OIZKCMyhGDlJap1S .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-OIZKCMyhGDlJap1S .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-OIZKCMyhGDlJap1S .section{stroke:none;opacity:0.2}#mermaid-svg-OIZKCMyhGDlJap1S .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-OIZKCMyhGDlJap1S .section2{fill:#fff400}#mermaid-svg-OIZKCMyhGDlJap1S .section1,#mermaid-svg-OIZKCMyhGDlJap1S .section3{fill:#fff;opacity:0.2}#mermaid-svg-OIZKCMyhGDlJap1S .sectionTitle0{fill:#333}#mermaid-svg-OIZKCMyhGDlJap1S .sectionTitle1{fill:#333}#mermaid-svg-OIZKCMyhGDlJap1S .sectionTitle2{fill:#333}#mermaid-svg-OIZKCMyhGDlJap1S .sectionTitle3{fill:#333}#mermaid-svg-OIZKCMyhGDlJap1S .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-OIZKCMyhGDlJap1S .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-OIZKCMyhGDlJap1S .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-OIZKCMyhGDlJap1S .grid path{stroke-width:0}#mermaid-svg-OIZKCMyhGDlJap1S .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-OIZKCMyhGDlJap1S .task{stroke-width:2}#mermaid-svg-OIZKCMyhGDlJap1S .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-OIZKCMyhGDlJap1S .taskText:not([font-size]){font-size:11px}#mermaid-svg-OIZKCMyhGDlJap1S .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-OIZKCMyhGDlJap1S .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-OIZKCMyhGDlJap1S .task.clickable{cursor:pointer}#mermaid-svg-OIZKCMyhGDlJap1S .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-OIZKCMyhGDlJap1S .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-OIZKCMyhGDlJap1S .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-OIZKCMyhGDlJap1S .taskText0,#mermaid-svg-OIZKCMyhGDlJap1S .taskText1,#mermaid-svg-OIZKCMyhGDlJap1S .taskText2,#mermaid-svg-OIZKCMyhGDlJap1S .taskText3{fill:#fff}#mermaid-svg-OIZKCMyhGDlJap1S .task0,#mermaid-svg-OIZKCMyhGDlJap1S .task1,#mermaid-svg-OIZKCMyhGDlJap1S .task2,#mermaid-svg-OIZKCMyhGDlJap1S .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-OIZKCMyhGDlJap1S .taskTextOutside0,#mermaid-svg-OIZKCMyhGDlJap1S .taskTextOutside2{fill:#000}#mermaid-svg-OIZKCMyhGDlJap1S .taskTextOutside1,#mermaid-svg-OIZKCMyhGDlJap1S .taskTextOutside3{fill:#000}#mermaid-svg-OIZKCMyhGDlJap1S .active0,#mermaid-svg-OIZKCMyhGDlJap1S .active1,#mermaid-svg-OIZKCMyhGDlJap1S .active2,#mermaid-svg-OIZKCMyhGDlJap1S .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-OIZKCMyhGDlJap1S .activeText0,#mermaid-svg-OIZKCMyhGDlJap1S .activeText1,#mermaid-svg-OIZKCMyhGDlJap1S .activeText2,#mermaid-svg-OIZKCMyhGDlJap1S .activeText3{fill:#000 !important}#mermaid-svg-OIZKCMyhGDlJap1S .done0,#mermaid-svg-OIZKCMyhGDlJap1S .done1,#mermaid-svg-OIZKCMyhGDlJap1S .done2,#mermaid-svg-OIZKCMyhGDlJap1S .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-OIZKCMyhGDlJap1S .doneText0,#mermaid-svg-OIZKCMyhGDlJap1S .doneText1,#mermaid-svg-OIZKCMyhGDlJap1S .doneText2,#mermaid-svg-OIZKCMyhGDlJap1S .doneText3{fill:#000 !important}#mermaid-svg-OIZKCMyhGDlJap1S .crit0,#mermaid-svg-OIZKCMyhGDlJap1S .crit1,#mermaid-svg-OIZKCMyhGDlJap1S .crit2,#mermaid-svg-OIZKCMyhGDlJap1S .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-OIZKCMyhGDlJap1S .activeCrit0,#mermaid-svg-OIZKCMyhGDlJap1S .activeCrit1,#mermaid-svg-OIZKCMyhGDlJap1S .activeCrit2,#mermaid-svg-OIZKCMyhGDlJap1S .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-OIZKCMyhGDlJap1S .doneCrit0,#mermaid-svg-OIZKCMyhGDlJap1S .doneCrit1,#mermaid-svg-OIZKCMyhGDlJap1S .doneCrit2,#mermaid-svg-OIZKCMyhGDlJap1S .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-OIZKCMyhGDlJap1S .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-OIZKCMyhGDlJap1S .milestoneText{font-style:italic}#mermaid-svg-OIZKCMyhGDlJap1S .doneCritText0,#mermaid-svg-OIZKCMyhGDlJap1S .doneCritText1,#mermaid-svg-OIZKCMyhGDlJap1S .doneCritText2,#mermaid-svg-OIZKCMyhGDlJap1S .doneCritText3{fill:#000 !important}#mermaid-svg-OIZKCMyhGDlJap1S .activeCritText0,#mermaid-svg-OIZKCMyhGDlJap1S .activeCritText1,#mermaid-svg-OIZKCMyhGDlJap1S .activeCritText2,#mermaid-svg-OIZKCMyhGDlJap1S .activeCritText3{fill:#000 !important}#mermaid-svg-OIZKCMyhGDlJap1S .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-OIZKCMyhGDlJap1S g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-OIZKCMyhGDlJap1S g.classGroup text .title{font-weight:bolder}#mermaid-svg-OIZKCMyhGDlJap1S g.clickable{cursor:pointer}#mermaid-svg-OIZKCMyhGDlJap1S g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-OIZKCMyhGDlJap1S g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-OIZKCMyhGDlJap1S .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-OIZKCMyhGDlJap1S .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-OIZKCMyhGDlJap1S .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-OIZKCMyhGDlJap1S .dashed-line{stroke-dasharray:3}#mermaid-svg-OIZKCMyhGDlJap1S #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-OIZKCMyhGDlJap1S #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-OIZKCMyhGDlJap1S #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-OIZKCMyhGDlJap1S #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-OIZKCMyhGDlJap1S #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-OIZKCMyhGDlJap1S #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-OIZKCMyhGDlJap1S #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-OIZKCMyhGDlJap1S #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-OIZKCMyhGDlJap1S .commit-id,#mermaid-svg-OIZKCMyhGDlJap1S .commit-msg,#mermaid-svg-OIZKCMyhGDlJap1S .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-OIZKCMyhGDlJap1S .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-OIZKCMyhGDlJap1S .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-OIZKCMyhGDlJap1S g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-OIZKCMyhGDlJap1S g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-OIZKCMyhGDlJap1S g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-OIZKCMyhGDlJap1S g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-OIZKCMyhGDlJap1S g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-OIZKCMyhGDlJap1S g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-OIZKCMyhGDlJap1S .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-OIZKCMyhGDlJap1S .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-OIZKCMyhGDlJap1S .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-OIZKCMyhGDlJap1S .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-OIZKCMyhGDlJap1S .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-OIZKCMyhGDlJap1S .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-OIZKCMyhGDlJap1S .edgeLabel text{fill:#333}#mermaid-svg-OIZKCMyhGDlJap1S .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-OIZKCMyhGDlJap1S .node circle.state-start{fill:black;stroke:black}#mermaid-svg-OIZKCMyhGDlJap1S .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-OIZKCMyhGDlJap1S #statediagram-barbEnd{fill:#9370db}#mermaid-svg-OIZKCMyhGDlJap1S .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-OIZKCMyhGDlJap1S .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-OIZKCMyhGDlJap1S .statediagram-state .divider{stroke:#9370db}#mermaid-svg-OIZKCMyhGDlJap1S .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-OIZKCMyhGDlJap1S .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-OIZKCMyhGDlJap1S .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-OIZKCMyhGDlJap1S .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-OIZKCMyhGDlJap1S .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-OIZKCMyhGDlJap1S .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-OIZKCMyhGDlJap1S .note-edge{stroke-dasharray:5}#mermaid-svg-OIZKCMyhGDlJap1S .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-OIZKCMyhGDlJap1S .error-icon{fill:#522}#mermaid-svg-OIZKCMyhGDlJap1S .error-text{fill:#522;stroke:#522}#mermaid-svg-OIZKCMyhGDlJap1S .edge-thickness-normal{stroke-width:2px}#mermaid-svg-OIZKCMyhGDlJap1S .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-OIZKCMyhGDlJap1S .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-OIZKCMyhGDlJap1S .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-OIZKCMyhGDlJap1S .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-OIZKCMyhGDlJap1S .marker{fill:#333}#mermaid-svg-OIZKCMyhGDlJap1S .marker.cross{stroke:#333}:root { --mermaid-font-family: "trebuchet ms", verdana, arial;}#mermaid-svg-OIZKCMyhGDlJap1S {color: rgba(0, 0, 0, 0.75);font: ;}ObjectThrowableErrorExceptionVitualMachineErrorIOErrorStackOverflowErrorExceptionSubclassRuntimeExceptionNullPointerExceptionClassCastExceptionillegalArgumentExceptionNumberFormatExceptionException的直接子类
  • 不管是错误还是异常,都是可以抛出的
  • 所有错误只要发生,java程序终止程序执行,退出jvm,错误是不能处理的
  • 编译时异常:所有Exception的直接子类,都可以叫做是编译时的异常,表示必须在编写程序时候预先对这种异常进行处理,如果不处理编译器会出错。发生概率比较高。也叫受检异常(CheckedException)或者受控异常
  • 运行时异常:所有RuntimeException及子类都属于运行时异常,运行时异常可处理也可不处理,发生概率比较低。叫未受检异常(UnCheckedException)或者非受控异常

4.1 异常处理方式

  • 在方法声明的位置上,使用throws关键字
  • 使用try…catch语句进行异常的捕捉

注意:Java中异常发生之后如果一直上抛,最终抛给了main方法,main方法继续向上抛,抛给了调用者JVM,JVM知道这个异常发生,只有一个结果。终止java程序的执行

public class ExceptionTest03 {public static void main(String[] args) {/*程序执行到此处发生了ArithmeticException异常,底层new了一个ArithmeticException异常对象,然后抛出了,由于是main方法调用了100 / 0,所以这个异常ArithmeticException抛给了main方法,main方法没有处理,将这个异常自动抛给了JVM。JVM最终终止程序的执行。ArithmeticException 继承 RuntimeException,属于运行时异常。在编写程序阶段不需要对这种异常进行预先的处理。*/System.out.println(100 / 0);// 这里的HelloWorld没有输出,没有执行。System.out.println("Hello World!");}
}

异常处理的原理:
通过抛出异常往上捕捉

public class ExceptionTest04 {public static void main(String[] args) {//doSome();//如果不加dosome函数编译通过,如果加上就会出错,必须对方法异常进行预处理}public static void doSome() throws ClassNotFoundException{System.out.println("doSome!!!!");}}

第一种方法:
在方法声明的位置上继续使用:throws,来完成异常的继续上抛。抛给调用者。上抛类似推卸责任,将其抛给上一级完成

public static void main(String[] args) throws ClassNotFoundException {doSome();
}

第二种方法:
try…catch进行捕捉
下抛是进行捕捉完成

public static void main(String[] args) {try {doSome();} catch (ClassNotFoundException e) {e.printStackTrace();}
}

4.2 异常捕捉和上报联合使用

  • 只要异常没有捕捉,采用上报的方式,此方法的后续代码不会执行。另外需要注意,try语句块中的某一行出现异常,该行后面的代码不会执行,try…catch捕捉异常之后,后续代码可以执行
  • 如果希望调用者来处理,选择throws上报。其它情况使用捕捉的方式
  • 一般不建议在main方法上使用throws,因为这个异常如果真正的发生了,一定会抛给JVM。JVM只有终止。一般main方法中的异常建议使用try…catch进行捕捉。main就不要继续上抛了
  • 抛出异常可以多个种类或者是其中一个子类也可以
FileInputStream fis = new FileInputStream("文件名");

通常调用这个类会执行错误,本身这个类加油thros抛出异常,需要进一步上抛出或者截获使用try catch语句
此处主要讲解上抛的结构

public class ExceptionTest06 {/*public static void main(String[] args) throws FileNotFoundException {System.out.println("main begin");m1();System.out.println("main over");}*/public static void main(String[] args) {System.out.println("main begin");try {// try尝试m1();// 以上代码出现异常,直接进入catch语句块中执行。System.out.println("hello world!");} catch (FileNotFoundException e){ // catch后面的好像一个方法的形参。// 这个分支中可以使用e引用,e引用保存的内存地址是那个new出来异常对象的内存地址。// catch是捕捉异常之后走的分支。// 在catch分支中干什么?处理异常。System.out.println("文件不存在,可能路径错误,也可能该文件被删除了!");System.out.println(e); //java.io.FileNotFoundException: D:\course\01-课\学习方法.txt (系统找不到指定的路径。)}// try..catch把异常抓住之后,这里的代码会继续执行。System.out.println("main over");}private static void m1() throws FileNotFoundException {System.out.println("m1 begin");m2();// 以上代码出异常,这里是无法执行的。System.out.println("m1 over");}// 抛别的不行,抛ClassCastException说明你还是没有对FileNotFoundException进行处理//private static void m2() throws ClassCastException{// 抛FileNotFoundException的父对象IOException,这样是可以的。因为IOException包括FileNotFoundException//private static void m2() throws IOException {// 这样也可以,因为Exception包括所有的异常。//private static void m2() throws Exception{// throws后面也可以写多个异常,可以使用逗号隔开。//private static void m2() throws ClassCastException, FileNotFoundException{private static void m2() throws FileNotFoundException {System.out.println("m2 begin");// 编译器报错原因是:m3()方法声明位置上有:throws FileNotFoundException// 我们在这里调用m3()没有对异常进行预处理,所以编译报错。// m3();m3();// 以上如果出现异常,这里是无法执行的!System.out.println("m2 over");}private static void m3() throws FileNotFoundException {// 调用SUN jdk中某个类的构造方法。// 这个类还没有接触过,后期IO流的时候就知道了。// 我们只是借助这个类学习一下异常处理机制。// 创建一个输入流对象,该流指向一个文件。/*编译报错的原因是什么?第一:这里调用了一个构造方法:FileInputStream(String name)第二:这个构造方法的声明位置上有:throws FileNotFoundException第三:通过类的继承结构看到:FileNotFoundException父类是IOException,IOException的父类是Exception,最终得知,FileNotFoundException是编译时异常。错误原因?编译时异常要求程序员编写程序阶段必须对它进行处理,不处理编译器就报错。*///new FileInputStream("D:\\course\\01-开课\\学习方法.txt");// 我们采用第一种处理方式:在方法声明的位置上使用throws继续上抛。// 一个方法体当中的代码出现异常之后,如果上报的话,此方法结束。new FileInputStream("D:\\course\\01-课\\学习方法.txt");System.out.println("如果以上代码出异常,这里会执行吗??????????????????不会!!!");}
}

4.3 try catch深入

上面的文章中进行上抛的异常
此处讲解截获使用try catch结构

FileInputStream fis = new FileInputStream("文件名");

catch结构截获解决异常
例如

try {FileInputStream fis = new FileInputStream("D:\\curse\\02-JavaSE\\document\\JavaSE进阶讲义\\JavaSE进阶-01-面向对象.pdf");System.out.println("以上出现异常,这里无法执行!");
} catch(FileNotFoundException e) {System.out.println("文件不存在!");
}

catch后面的小括号中的类型可以是具体的异常类型,也可以是该异常类型的父类型
此处可以用FileNotFoundException e

  • 也可以用IOException e,因为多态:IOException e = new FileNotFoundException();
  • 也可以用Exception e,因为多态:Exception e = new FileNotFoundException();

catch可以写多个。建议catch的时候,精确的一个一个处理。这样有利于程序的调试

try {//创建输入流FileInputStream fis = new FileInputStream("D:\\curse\\02-JavaSE\\document\\JavaSE进阶讲义\\JavaSE进阶-01-面向对象.pdf");//读文件fis.read();
} catch(FileNotFoundException e) {System.out.println("文件不存在!");
} catch(IOException e){System.out.println("读文件报错了!");
}

jdk8以上之后可以写或语句

// JDK8的新特性!
try {//创建输入流FileInputStream fis = new FileInputStream("D:\\curse\\02-JavaSE\\document\\JavaSE进阶讲义\\JavaSE进阶-01-面向对象.pdf");// 进行数学运算System.out.println(100 / 0); // 这个异常是运行时异常,编写程序时可以处理,也可以不处理。
} catch(FileNotFoundException | ArithmeticException | NullPointerException e) {System.out.println("文件不存在?数学异常?空指针异常?都有可能!");
}

catch写多个的时候,从上到下,必须遵守从小到大

// 编译报错。
try {//创建输入流FileInputStream fis = new FileInputStream("D:\\curse\\02-JavaSE\\document\\JavaSE进阶讲义\\JavaSE进阶-01-面向对象.pdf");//读文件fis.read();
} catch(IOException e){System.out.println("读文件报错了!");
} catch(FileNotFoundException e) {System.out.println("文件不存在!");
}

4.4 异常对象常用方法

  • 获取异常简单的描述信息:String msg = exception.getMessage();

  • 打印异常追踪的堆栈信息:exception.printStackTrace();java后台打印异常堆栈追踪信息的时候,采用了异步线程的方式打印的

public class ExceptionTest08 {public static void main(String[] args) {// 这里只是new了异常对象,但是没有将异常对象抛出。JVM会认为这是一个普通的java对象。因为此处的对象源代码没有throws上抛NullPointerException e = new NullPointerException("空指针异常fdsafdsafdsafds");// 获取异常简单描述信息:这个信息实际上就是构造方法上面String参数。String msg = e.getMessage(); //空指针异常fdsafdsafdsafdsSystem.out.println(msg);// 打印异常堆栈信息e.printStackTrace();for(int i = 0; i < 1000; i++){System.out.println("i = " + i);}System.out.println("Hello World!");}
}

所谓异步线程,是控制台如果有其他输出也会跟着一起输出

try {m1();
} catch (FileNotFoundException e) {// 获取异常的简单描述信息String msg = e.getMessage();System.out.println(msg); //C:\jetns-agent.jar (系统找不到指定的文件。)//打印异常堆栈追踪信息!!!//在实际的开发中,建议使用这个。养成好习惯!// 这行代码要写上,不然出问题你也不知道!e.printStackTrace();
}

异常追踪信息如图所示

只需要看本身写的文件bug出错即可
59行问题导致在55行,依次类推
追踪的信息最终在19行,只需要看19行即可

4.5 finally子句

  • 在finally子句中的代码是最后执行的,并且是一定会执行的,即使try语句块中的代码出现了异常,finally子句必须和try一起出现,不能单独编写
  • 通常用的情况:通常在finally语句块中完成资源的释放/关闭,因为finally中的代码比较有保障。即使try语句块中的代码出现异常,finally中代码也会正常执行

在try中定义的对象不能在finally使用,所以要想使用必须设置为全局变量

public class ExceptionTest10 {public static void main(String[] args) {FileInputStream fis = null; // 声明位置放到try外面。这样在finally中才能用。try {// 创建输入流对象fis = new FileInputStream("D:\\course\\02-JavaSE\\document\\JavaSE进阶讲义\\JavaSE进阶-01-面向对象.pdf");// 开始读文件....String s = null;// 这里一定会出现空指针异常!s.toString();System.out.println("hello world!");// 流使用完需要关闭,因为流是占用资源的。// 即使以上程序出现异常,流也必须要关闭!// 放在这里有可能流关不了。//fis.close();} catch (FileNotFoundException e) {e.printStackTrace();} catch(IOException e){e.printStackTrace();} catch(NullPointerException e) {e.printStackTrace();} finally {System.out.println("hello 浩克!");// 流的关闭放在这里比较保险。// finally中的代码是一定会执行的。// 即使try中出现了异常!if (fis != null) { // 避免空指针异常!try {// close()方法有异常,采用捕捉的方式。fis.close();} catch (IOException e) {e.printStackTrace();}}}System.out.println("hello kitty!");}
}

子句中注意事项

  • try不能单独使用
  • try finally可以联合使用,可以没有catch
  • 代码执行顺序,先执行try,在finally,在执行try中涉及的return
try {System.out.println("try...");return;
} finally {// finally中的语句会执行。能执行到。System.out.println("finally...");
}// 这里不能写语句,因为这个代码是无法执行到的。
//System.out.println("Hello World!");
  • 在try中有exit,退出JVM之后,finally语句中的代码就不执行了
try {System.out.println("try...");// 退出JVMSystem.exit(0); // 退出JVM之后,finally语句中的代码就不执行了!
} finally {System.out.println("finally...");
}

4.6 final、finally与finalize区别

  • final是一个关键字。表示最终的。不变的
  • finally也是一个关键字,和try联合使用,使用在异常处理机制中
  • finalize()是Object类中的一个方法。作为方法名出现,finalize是标识符。这个方法是由垃圾回收器GC负责调用的

4.7 自定义异常

  • 编写一个类继承Exception或者RuntimeException
  • 提供两个构造方法,一个无参数的,一个带有String参数的
    (具体灵感来源源码)
public class MyException extends Exception{ // 编译时异常public MyException(){}public MyException(String s){super(s);}
}/*
public class MyException extends RuntimeException{ // 运行时异常}*/

正式调用异常的类以及使用异常常用方法

public class ExceptionTest15 {public static void main(String[] args) {// 创建异常对象(只new了异常对象,并没有手动抛出)MyException e = new MyException("用户名不能为空!");// 打印异常堆栈信息e.printStackTrace();// 获取异常简单描述信息String msg = e.getMessage();System.out.println(msg);}
}

4.8 面试题

补充:

  • throws 在方法声明位置上使用,表示上报异常信息给调用者
  • throw 手动抛出异常

代码自上而下执行,与上面的结构有些违和,最后的输出是100而不是101

public static void main(String[] args) {int result = m();System.out.println(result); //100
}/*
java语法规则(有一些规则是不能破坏的,一旦这么说了,就必须这么做!):java中有一条这样的规则:方法体中的代码必须遵循自上而下顺序依次逐行执行(亘古不变的语法!)java中海油一条语法规则:return语句一旦执行,整个方法必须结束(亘古不变的语法!)*/
public static int m(){int i = 100;try {// 这行代码出现在int i = 100;的下面,所以最终结果必须是返回100// return语句还必须保证是最后执行的。一旦执行,整个方法结束。return i;} finally {i++;}
}

反编译之后的语句是,而且也满足从上往下的执行代码顺序

public static int m(){int i = 100;int j = i;i++;return j;
}

重写之后的方法不能比重写之前的方法抛出更多(更宽泛)的异常,可以更少
父类抛出异常,子类可以抛出也可以不抛出或者抛出同类似类即可
父类没抛出异常,子类不可以抛出异常但可以重写覆盖RuntimeException的运行时异常

class Animal {public void doSome(){}public void doOther() throws Exception{}
}class Cat extends Animal {// 编译正常。public void doSome() throws RuntimeException{}// 编译报错。/*public void doSome() throws Exception{}*/// 编译正常。/*public void doOther() {}*/// 编译正常。/*public void doOther() throws Exception{}*/// 编译正常。public void doOther() throws NullPointerException{}
}

4.9 实际开发场景

用户业务类,处理用户相关的业务:例如登录、注册等功能
实现的用户类

public class UserService {/*** 用户注册* @param username 用户名* @param password 密码* @throws IllegalNameException 当用户名为null,或者用户名长度小于6,或者长度大于14,会出现该异常!*/public void register(String username, String password) throws IllegalNameException {/*引用等于null的这个判断最好放到所有条件的最前面。*//*if(username == null || username.length() < 6 || username.length() > 14){}*//*再分享一个经验:username == null 不如写成 null == username"abc".equals(username) 比 username.equals("abc") 好。*//*if(null == username || username.length() < 6 || username.length() > 14){}*/if(null == username || username.length() < 6 || username.length() > 14) {/*System.out.println("用户名不合法,长度必须在[6-14]之间");return;*/throw new IllegalNameException("用户名不合法,长度必须在[6-14]之间");}// 程序能够执行到此处说明,用户名合法System.out.println("注册成功,欢迎["+username+"]");}
}

自定义异常

public class IllegalNameException extends Exception {public IllegalNameException(){}public IllegalNameException(String s){super(s);}
}

通过测试类检测

public class Test {public static void main(String[] args) {// 创建UserService对象UserService userService = new UserService();// 用户名和密码就不再从控制台接收了try {userService.register("jack", "123");} catch (IllegalNameException e) {//System.out.println(e.getMessage());e.printStackTrace();}}
}

在此强调

这部分知识一共有3个文档
第二个是当前这个文档

  1. java零基础从入门到精通(全)
  2. javaSE从入门到精通的十万字总结(一)
  3. javaSE从入门到精通的十万字总结(完结)

javaSE从入门到精通的二十万字总结(一)相关推荐

  1. javaSE从入门到精通的二十万字总结(二)

    目录 前言 5. 集合 5.1 集合两大类 5.2 Collection 5.2.1 常用方法 5.2.2 迭代器 5.2.3 contains方法 5.2.4 remove方法 5.3 List 5 ...

  2. MySQL数据库,从入门到精通:第十二篇——MySQL数据类型详解

    MySQL数据库,从入门到精通:第十二篇--MySQL数据类型详解 第 12 章_MySQL数据类型精讲 1. MySQL中的数据类型 2. 整数类型 2. 1 类型介绍 2. 2 可选属性 2. 2 ...

  3. MySQL数据库,从入门到精通:第十四篇——MySQL视图详解

    MySQL数据库,从入门到精通:第十四篇--MySQL视图详解 第 14 篇_视图 1. 常见的数据库对象 2. 视图概述 2. 1 为什么使用视图? 2. 2 视图的理解 3. 创建视图 3. 1 ...

  4. Oracle数据库从入门到精通系列之十八:Oracle进程

    Oracle数据库从入门到精通系列之十八:Oracle进程 一.Oracle进程 二.服务器进程server process 三.后台进程background process 四.从属进程(slave ...

  5. GPU 编程入门到精通(二)之 运行第一个程序

    博主由于工作当中的需要,开始学习 GPU 上面的编程,主要涉及到的是基于 GPU 的深度学习方面的知识,鉴于之前没有接触过 GPU 编程,因此在这里特地学习一下 GPU 上面的编程.有志同道合的小伙伴 ...

  6. GPS从入门到放弃(二十六) --- RTKLIB函数解析

    GPS从入门到放弃(二十六) - RTKLIB函数解析 为了贴合这个系列的标题"从入门到放弃",在入门之后现在就要放弃此方向了.虽然感觉遗憾,暂时也没有办法.在此附上此系列最后一篇 ...

  7. 【Freeswitch从入门到精通】二、初识Freeswitch

    [Freeswitch从入门到精通]二.初识Freeswitch 1.入门术语 1.1 常见短语 1.2 Call Legs 2.历史 3.启动 4.dialplan 路由表 4.1 测试Demo路由 ...

  8. GPS从入门到放弃(二十五) --- 卡尔曼滤波

    GPS从入门到放弃(二十五) - 卡尔曼滤波 概述 单点定位的结果因为是单独一个点一个点进行的,所以连续起来看数据可能出现上串下跳的情况,事实上并不符合实际情况.为了解决这个问题,考虑到物体运动的连续 ...

  9. Excel数据分析从入门到精通(二)软件操作快捷键

    Excel数据分析从入门到精通(二)软件操作快捷键 1.智能拆分--Ctrl+E 2.自动生成下拉列表--Alt+↓ 3.插入批注--shift+F2 4.快速查找--Ctrl+F 5.快速定位--C ...

最新文章

  1. 白皮书下载 |《产品用户体验的数据化评估》
  2. Source Insight 常用设置和快捷键大全
  3. VTK:可视化之AnnotatedCubeActor
  4. 重量级锁的加锁的基本流程
  5. 线性一致性理解Linearizability
  6. Centos7,配置防火墙,开启端口
  7. python实现的json数据以HTTP GET,POST,PUT,DELETE方式页面请求
  8. 破环计算机系统的案件量刑,破坏计算机信息系统罪如何定罪量刑
  9. 企业级实际性能测试案例与经验分享
  10. 【LA3942】Remember the World(初识前缀树Trie----模版题 + dp)
  11. Mcmod模组下载脚本
  12. 有趣的计算机黑科技,6个让你欲罢不能的电脑黑科技软件,我一定要分享出来!...
  13. 17、【易混淆概念集】第十一章1 项目风险 风险临界值 VS 风险承受力 风险管理流程 风险管理及变更流程 规划风险管理 识别风险
  14. DTcms二次开发心得
  15. 常用财务软件有哪些功能模块
  16. NULL, '\0',0 '0'的区别
  17. 15.系统安全分析与设计
  18. oracle 判断标签,判断text标签
  19. 2021天梯赛选拔随缘补题.jpg
  20. URAL 1998 The old Padawan

热门文章

  1. 计算机毕业设计Node.js+Vue驾校预约考试管理系统(程序+源码+LW+部署)
  2. React基础 - refs的详解与应用
  3. 集成电路芯片缺陷检测
  4. JAVA vim 开发环境配置
  5. PostgreSQL数据库专家唐成:用最专业的服务解决用户最大的痛点
  6. QMUI Android
  7. Mac版本的After Effects 2023中英文切换方法
  8. Badly placed ()‘s 问题
  9. 王者服务器维护8.25,上单位移全部削弱射手春天到来!王者荣耀体验服8.25英雄调整详解...
  10. android 电源管理(PowerManager)