面向对象编程
Java的核心思想就是OOP(面对对象编程的英文缩写)
面向对象编程的本质就是: 以类的方式组织代码,以对象的组织(封装)数据!!
三大特性: 封装、继承、多态
面向过程思想:
步骤清晰简单,第一步做什么,第二步做什么…
面对过程适合处理一些较为简单的问题
面向对象思想:
物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考。
最后,才对某个分类下的细节进行面向过程的思索。
面向对象适合处理复杂的问题,适合处理需要多人协作的问题!
对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路
来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。
创建与初始化对象
使用new关键字创建对象
使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用!
类中的构造器为称为构造方法,是在进行创建对象的时候必须要调用的。并且构造器有以下俩个特点:
- 必须和类的名字相同
- 必须没有返回类型,也不能写void
- 无参的构造方法在类生成的时候就默认生成了!
构造器(构造方法必须掌握!)
构造器的作用:
- 使用new关键字,本质是在调用构造器
- 用来初始化对象的值
注意点:当定义了有参构造方法之后,如果想使用无参的构造方法,必须要显示的定义一个无参的构造方法!
<在IDE中,生成构造方法的快捷键为:Alt + Insert + 点击Constructor 既可选择生成无参的或者有参的构造方法!>
this . 表示当前类
public class Application{
public static void main(String[] args){
// new 实例化一个对象
Person person1 = new Person();//本质使用了无参的构造方法
System.out.println(person.name);//输出结果为:null
//new 实例化一个对象
Person person = new Person("夏黄杰");//使用了定义的有参的构造方法!给person对象的name属性初始化了一个值
System.out.println(person.name);//输出结果为:夏黄杰
}
}
class Person{
String name;
//显示的定义无参构造方法
public Person(){
}
//有参构造方法:一旦定义了有参构造方法,无参构造方法就必须显示定义
public Person(String name){
this.name = name;
}
}
面向对象三大特性
封装
该露的露,该藏的藏:
我们程序设计要追求"高内聚,低耦合"。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用!
封装(数据的隐藏):
通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏。
记住这句话就够了:属性私有,get/set
eg:
package Demo.OOP;
public class FengZhuang {
/*
* 封装的意义:
* 1.提高程序的安全性,保护数据
* 2.隐藏代码的实现细节
* 3.统一接口(get()、set()),通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏。
* 4.提高了系统的可维护性
* */
public static void main(String[] args) {
Student s1 = new Student();
s1.setName("夏黄杰");
System.out.println(s1.getName());
s1.setAge(999);//不合法的年龄,在setAge()中做出了判断排除
System.out.println(s1.getAge());//setAge中对于不合法的年龄,默认赋值age = 3
}
}
class Student{
//属性私有 ------- private: 私有
//封装大多数时候是针对属性,对方法封装使用的比较少!!!
private String name;//名字
private int id;//学号
private char sex;//性别
private int age;
//提供一些可以操作这些私有属性的方法!
//提供一些public的get、set方法,IDE中同样有快捷键生成get、set方法------ alt + insert + 点击Getter and Setter 然后选择你想生成get、set方法的属性一键生成!!!
//get方法 ----- 获得这个数据
public String getName() {
return name;
}
//set方法 ------- 给这个数据设置值
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age > 120 || age < 0 ){//年龄不合法
this.age = 3;
}else {
this.name = name;
}
}
}
继承
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模!
extends的意思是"扩展"。子类是父类的扩展。
JAVA中只有单继承,没有多继承!
- 继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等。
- 继承关系的俩个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用extends来表示。
- 子类和父类之间,从意义上讲应该具有"is a"的关系
- 在JAVA中,所有的类,都默认直接或者间接继承Object
- 当一个类被final修饰后,则该类不允许被继承!!!
eg:
package Demo.OOP;
public class JiCheng {//测试类
public static void main(String[] args) {
Student1 student1 = new Student1();
System.out.println(student1.address);
student1.Study();
System.out.println(student1.getMenoy());
}
}
//在JAVA中,所有的类,都默认直接或者间接继承Object
//在IDE中,快捷键 Ctrl + H 在打开的窗口中,即可观察到父类和子类们之间的继承关系!!
class Person1{//父类
private int menoy = 10_0000_0000;//这里将父类的menoy属性定义成私有的
public String address = "江西省";
public void Study(){
System.out.println("好好学习!");
}
//为私有属性menoy提供get、set方法,供子类调用该属性
public int getMenoy() {
return menoy;
}
public void setMenoy(int menoy) {
this.menoy = menoy;
}
}
//定义一个子类Student1继承Person1 ------- 子类继承了父类,就会拥有父类的全部方法!(用private修饰定义为私有的父类的方法与属性不能被子类继承!!!)
class Student1 extends Person1{
}
IDE快捷键 Ctrl + H 打开后的效果图:
Super
super注意点:
- super调用父类的构造方法,必须在构造方法的第一行
- super必须只能出现在子类的方法或者构造方法中,不能出现在父类中
- super和this不能同时调用构造方法(因为super()和this()都必须在第一行从而冲突!)
- 在子类中的无参构造方法默认隐藏了super() ,既new子类对象的时候先调用父类的无参构造方法,再调用本类的无参构造方法,且super()必须位于子类无参构造方法的第一行!
VS this:
代表的对象不同:
- this: 本身调用者这个对象
- super: 代表父类对象的引用
前提:
- this: 没有继承前提也可以使用
- super: 只能在继承条件下才可以使用
构造方法:
- this(): 本类的构造方法
- super(): 父类的构造方法
eg:
package Demo.OOP;
public class Super {
public static void main(String[] args) {
Teacher teacher = new Teacher();
//输出结果是先输出了父类的无参构造方法然后再输出了子类的无参构造方法,
// 原因:在子类无参构造方法的第一行默认有super()既调用父类的无参构造方法
teacher.test("软件工程专业");
}
}
class School{//定义一个School父类
protected String name = "清华大学";
//protected(受保护的)
//它和private关键字的作用相当,差别仅在于继承的类可以访问protected成员但是不能访问private成员
public School() {
System.out.println("School无参执行了!");
}
}
class Teacher extends School{//定义一个子类Teacher继承School
private String name = "人工智能学院";
public Teacher() {
//隐藏代码: 调用了父类的无参构造方法!
//super(); 在子类的无参构造方法中,默认隐藏调用了父类的无参构造方法既super(),且super()必须要放在子类无参构造方法的第一行!!!
System.out.println("Teacher无参执行了!");
}
public void test(String name){
System.out.println(name);//传入方法参数的值
System.out.println(this.name);//本类中name的值
System.out.println(super.name);//父类中name的值
}
}
方法重写
重写: (前提) 需要有继承关系,子类重写父类的方法!
- 方法名必须相同
- 参数列表必须相同
- 修饰符:范围可以扩大但不能缩小: public > Protected > Default > private
- 抛出的异常:范围,可以被缩小,但不能扩大;eg: ClassNotFoundException – > Exception
- 方法的重写只和非静态方法有关,与静态方法无关!!!
重写,子类的方法必须要与父类的一致;方法体不同罢了!
为什么需要方法重写:
- 父类的功能,子类不一定需要,或者不一定满足!
eg: 静态方法与非静态方法在调用时也有很大区别,具体看案例!!!
package Demo.OOP;
//方法重写!
public class MethodOverride {//测试类
/*
* 方法的重写中,静态的方法和非静态的方法区别很大!
* 静态方法: 方法的调用只和创建对象的左边,也就是定义的数据类型有关
* 非静态方法: 方法重写只和非静态方法有关,与静态方法没有任何关系
* */
public static void main(String[] args) {
B b = new B();
b.test();//test()方法为静态方法,调用时只和定义的数据类型有关,也就是创建对象的左边有关! ! -----> 此处调用的就是B类的test()
b.say();//say()方法为非静态方法,故有方法重写一说 ------> 此处调用的是B类的say()
System.out.println("======================");
//对象能执行哪些方法,主要看创建对象时左边的类型和右边的关系不大!
A a = new B();//父类的引用指向了子类 --------> 此处用到了多态
a.test();//此处调用的是A类的test()
a.say();//子类重写了父类的方法,-------> 此处调用的是B类的say()
/* 看一个对象能调用哪些方法看定义的数据类型,
但当定义时父类的引用指向了子类,然后父类和子类都有非静态say()这个方法时
如果子类没有重写该方法则执行父类的该方法,如果子类重写了该方法则执行子类的该方法!!!*/
System.out.println("======================");
A s = new A();
s.test();//此处调用的是A类的test()
s.say();//此处调用的是A类的say()
}
}
class A{//定义一个A父类
public static void test(){//静态方法
System.out.println("A=>test()");
}
public void say(){//非静态方法,只有非静态方法才有方法重写一说!
System.out.println("我去上班了!");
}
}
class B extends A{//定义一个B类作为A的子类,继承A类
public static void test(){
System.out.println("B=>test()");
}
//方法的重写都是方法的重写,和属性无关!!!
@Override//注解: 有功能的注释! 方法的重写
public void say() {//方法的重写只和非静态方法有关,与静态方法无关!!!
// super.say(); 在IDE中同样有快捷键一键生成方法重写,alt + insert + 点击Override Methods
// 生成的方法重写默认是调用父类的方法 ------> super.方法名()
System.out.println("我去上学了!");
}
}
多态
多态:既同一方法可以根据发送对象的不同而采用多种不同的行为方式
一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多(例如:子类,父类,有关系的类)
多态存在的条件:
- 有继承关系
- 子类重写父类方法
- 父类引用指向子类对象(父类型 ,可以指向子类,但是不能调用子类独有的方法!)
多态注意事项:
- 多态是方法的多态,属性没有多态
- 父类和子类 , 类与类之间有联系 。 没有任何联系的俩个类强行转换会报异常 ----> 类型转换异常 ClassCastException
- 存在条件:继承关系、方法需要重写、父类引用指向子类对象! 类似于:Father f1 = new Son();
以下方法不允许被重写:
- static方法,属于类,它不属于实例
- final 常量
- private方法
eg:
package Demo.OOP;
/*
多态存在的条件:
- 有继承关系
- 子类重写父类方法
- 父类引用指向子类对象(父类型 ,可以指向子类,但是不能调用子类独有的方法!)
* */
public class DuoTai {//测试类
public static void main(String[] args) {
//一个对象的实际类型是确定的,但可以指向的引用类型就不确定了: 父类的引用指向子类
Student2 s1 = new Student2();//Student(子类)能调用的方法都是自己的或者继承父类的!
Person2 s2 = new Student2();//Person父类型,可以指向子类,但是不能调用子类独有的方法
Object s3 = new Student2();//在JAVA中,所有的类默认继承Object
/* 看一个对象能调用哪些方法,看定义的数据类型,
但当定义时父类的引用指向了子类,然后父类和子类都有非静态say()这个方法时
如果子类没有重写该方法则执行父类的该方法,如果子类重写了该方法则执行子类的该方法!!!*/
s2.run();//子类重写了父类的方法,故执行子类的方法
s1.run();
//s3.run()报错,原因是s3是Object对象,在Object类中不存在run()方法,父类又不能调用子类独有的方法,所以报错!!
s1.eat();
//s2.eat(); ---- > eat()是子类独有的,父类对象s2不能调用
}
}
class Person2{
public void run(){
System.out.println("run");
}
}
class Student2 extends Person2{
@Override
public void run() {
// super.run();
System.out.println("son");
}
public void eat(){
System.out.println("吃东西!");
}
}
instanceof关键字
instanceof:
instanceof 是 Java 的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。
instanceof是Java中的二元运算符,左边是对象,右边是类;当对象是右边类或子类所创建对象时,返回true;否则,返回false。
注意点:
- 类的实例包含本身的实例,以及所有直接或间接子类的实例
- instanceof左边显式声明的类型与右边操作元必须是同种类或存在继承关系,也就是说需要位于同一个继承树,否则会编译错误!!!
eg: 具体看例题,注意instanceof关键字与多态之间的联系!
package Demo.OOP;
public class Instanceof {
public static void main(String[] args) {
//对象能执行哪些方法,主要看创建对象时左边的类型和右边的关系不大!
Animal animal = new Tiger();//这里animal对象是Animal类型,但它可以指向子类Tiger()
// 一个对象的实际类型是确定的,但可以指向的引用类型就不确定了: 父类的引用指向子类,这里虽然指向子类但是不能调用子类独有的方法!
animal.test();//animal是Animal类型对象,指向子类型,由于父类和子类都有test()且子类重写了该方法,所以此时调用的是子类的test()
/*instanceof是Java中的二元运算符,左边是对象,右边是类;当对象是右边类或子类所创建对象时,返回true;否则,返回false。
instanceof左边显式声明的类型与右边操作元必须是同种类或存在继承关系,也就是说需要位于同一个继承树,否则会编译错误*/
System.out.println(animal instanceof Tiger);//此时animal指向了子类Tiger(),属于Tiger类型
System.out.println(animal instanceof Animal);
System.out.println(animal instanceof Object);
System.out.println(animal instanceof monkey);//false
//System.out.println(animal instanceof String);这条代码会直接编译报错!!!
//因为animal对象不管是实际类型Animal的对象还是指向子类Tiger类型的对象,都与String类型毫无关系
// 使用instanceof关键字时,左右俩边要有一点联系才行,不然就直接编译报错!!!
System.out.println("==============================");
Object object = new Tiger();//object对象的实际类型是Object,父类的引用指向了子类Tiger()
System.out.println(object instanceof Tiger);//此时animal指向了子类Tiger(),属于Tiger类型
System.out.println(object instanceof Animal);
System.out.println(object instanceof Object);
System.out.println(object instanceof monkey);//false ,此时object为实际类型Object对象不是monkey类或其子类创建的对象,故返回false
System.out.println(object instanceof String);//false,此时object为实际类型Object对象不是String类或其子类创建的对象,故返回false
Tiger tiger = new Tiger();
tiger.eat();
Animal animal1 =tiger;//子类对象转换为父类对象,由低到高,无需强制转换
//animal1.eat(); ------ 编译报错,转换后丢失了子类本身的方法eat()
Tiger tiger1 =(Tiger) animal1;//由父类转换到子类对象,由高到低,需要强制转换!!
tiger1.eat();
}
}
class Animal{//父类
public void test(){
System.out.println("动物世界!");
}
}
class Tiger extends Animal{//定义Tiger继承Animal
@Override
public void test() {
//
System.out.println("大老虎!");
}
public void eat(){
System.out.println("吃肉!");
}
}
class monkey extends Animal{
public void eat(){
System.out.println("吃香蕉!");
}
}
类型之间的转换:父 类 子类
子类对象可以转换为父类,但是可能会丢失自己独有的一些方法!
父类对象要转换为子类,需要强制转换!
Tiger tiger = new Tiger();
tiger.eat();
Animal animal1 =tiger;//子类对象转换为父类对象,由低到高,无需强制转换
//animal1.eat(); ------ 编译报错,转换后丢失了子类本身的方法eat()
Tiger tiger1 =(Tiger) animal1;//由父类转换到子类对象,由高到低,需要强制转换!!
tiger1.eat();
static关键字
在类中,使用 static 修饰符修饰的属性(成员变量)称为静态变量,也可以称为类变量,常量称为静态常量,方法称为静态方法或类方法,它们统称为静态成员,归整个类所有。
静态成员不依赖于类的特定实例,被类的所有实例共享,就是说 static 修饰的方法或者变量不需要依赖于对象来进行访问,只要这个类被加载,java虚拟机就可以根据类名找到它们。
注意点:
- static 修饰的成员变量和方法,从属于类。
- 普通变量和方法从属于对象。
- 静态方法不能调用非静态成员,编译会报错。但非静态方法却能调用静态属性和静态方法!
eg:
package Demo.OOP;
//静态导入包,类似以下在导入java某个包中的方法时加上static修饰,则在代码中想调用该包中的方法时就不用: 包名.方法名(),可以直接 方法名()调用!!!
//但前提是方法本身是静态方法,不然还是需要创建对象才能调用,静态导入包很少人会这么玩,主要了解一下有这种方式,但不推荐这样书写,不利于阅览代码!
import java.util.Random;
import static java.lang.Math.*;
import static java.util.Random.*;
public class Static {
//2.对象被创建的时候,它会先执行匿名代码块然后再执行构造方法。
// 如果有静态代码块的话,只会在第一次执行静态代码块,且静态代码块在匿名代码块之前执行!
{
//匿名代码块 ----- 一般可以用来赋初始值!!! 因为匿名代码块跟对象是同时产生的并且在构造方法之前!!!
System.out.println("匿名代码块!");
}
//1.只会执行一次!!! ---- 跟随类一加载就执行,且永久只执行一次!!!
static {
//静态代码块
System.out.println("静态代码块!");
}
//3.
public Static() {
//默认产生的无参构造方法
System.out.println("默认产生的无参构造方法");
}
public static void main(String[] args) {
Static s = new Static();
System.out.println("=======================");
Static a = new Static();
System.out.println("======================");
System.out.println(random());//random()随机产生随机数
System.out.println(new Random().nextInt(10));
//这里不能直接nextInt()调用,是因为Random类中的nextInt()定义时没有被static修饰,故为非静态方法,需要创建对象来调用该方法!
System.out.println(PI);
}
private static int age;//静态变量
private double score;//非静态变量
public void run(){
System.out.println(age);
go();//在非静态方法中调用静态属性和静态方法
}
public static void go(){//这里用static修饰该方法,定义为静态方法!
System.out.println(age);
/* System.out.println(score);
run(); 在静态方法中不能调用非静态属性和非静态方法!!! */
}
}
class Person3{//定义父类,注意当类被final修饰后,则不被允许被继承!!!
String name = "夏黄杰";
}
class Student3 extends Person3{
}
抽象类
- abstract修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法,如果修饰类,那么该类就是抽象类。
- 抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类!
- 抽象类不能使用new关键字来创建对象,它是用来让子类继承的。
- 抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的。
- 子类继承抽象类,那么就必须要实现抽象类中没有实现的抽象方法,除非该子类也是抽象类,则可以不去实现!!!
eg:
package Demo.OOP;
public class abstractDemo {
public static void main(String[] args) {
C demo = new C();
demo.doSomething();
System.out.println("======================");
demo.say();
}
}
abstract class Action{//用abstract关键字修饰类Action后,将其定位为了抽象类
//不能new抽象类,只能靠子类去实现它!!!
public abstract void doSomething();//用abstract修饰方法后,既该方法为抽象方法。--- 既只定义了方法名字,但没有方法的实现!
public abstract void say();//抽象方法必须要在抽象类中!!!
public void test(){
System.out.println("普通方法可以写在抽象类中!!!");
}
}
class C extends Action{//继承抽象类的子类,必须实现抽象类中的所有抽象方法。
// 除非该子类也被abstract修饰,既也为抽象类!!!则不用实现父类的抽象方法!!
@Override
public void doSomething() {
System.out.println("子类c实现父类抽象类中的抽象doSomething方法");
}
@Override
public void say() {
System.out.println("子类c实现父类抽象类中的抽象say方法");
}
}
接口
接口(英文: interface) , 在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。
接口:只有规范!自己无法写方法,只能定义没有方法的实现!接口可以实现 — 约束和实现的分离:面向接口编程!!!(软件开发中很常见的也很重要的编程思路!)
接口的作用:
- 是一个约束!
- 可以在接口中定义一些方法,让其他的类去实现
- 接口中定义的方法都是抽象方法,方法名前都默认用 public abstract 修饰了
- 接口中定义的属性都是静态常量,属性前都默认用了 public static final 修饰了
- 接口和抽象类一样不能直接被实例化,因为接口中没有构造方法
- 接口可以通过implements关键字实现多继承,既一个类可以通过implements实现多个接口
- 实现接口的类必须要重写接口中所有的方法!
- 当实现接口的类是抽象类时,可以不重写接口的方法,这一点与抽象类相似!!!
eg:
package Demo.OOP;
import java.util.Date;
public class InterfaceDemo {
public static void main(String[] args) {
UserServiceImpl demo = new UserServiceImpl();
demo.add("夏黄杰");
demo.delete("夏黄杰");
demo.query("夏黄杰");
demo.query("夏黄杰");
System.out.println("========================");
demo.time();
}
}
interface UserService{//在java中用interface关键字来定义接口
//在接口中定义的属性都是静态常量,属性前面都默认用了 public static final 修饰
//一般不会这么玩,既一般不会在接口中定义常量,一般都在接口中定义一下方法供其他类实现
public static final int age = 21;
//接口中所有定义的方法都是抽象的,方法前面都默认用了 public abstract 修饰,所以在接口中定义方法时直接写 返回类型 + 方法名字 + 参数 就定义完成!
public abstract void add(String name);
void delete(String name);
void update(String name);
void query(String name);
/* public void run(){
} 在接口中不允许写普通方法,不能写方法的实现,接口中默认写的都是抽象方法!*/
}
interface TimeService{
void time();
}
class UserServiceImpl implements UserService,TimeService{
//在java中,类可以通过implements来实现接口
//实现接口的类,必须要重写接口中所有的方法!!!
//在java中可以利用接口实现多继承,接口之间用逗号(,)相隔。
@Override
public void add(String name) {
System.out.println(name +"进行添加操作");
}
@Override
public void delete(String name) {
System.out.println(name+"进行删除操作");
}
@Override
public void update(String name) {
System.out.println(name+"进行修改操作");
}
@Override
public void query(String name) {
System.out.println(name+"进行查询操作");
}
@Override
public void time() {
System.out.println(new Date());//通过Date()返回当前时间!
}
}
abstract class b implements UserService{//当实现接口的类是抽象类时,可以不重写接口的方法,这一点与抽象类相似!!!
}
内部类
它被定义在另一个类的内部,所以称为内部类(Nested Class)。
Java的内部类分为好几种,通常情况用得不多,但也需要了解它们是如何使用的。
在内部类中可以使用外部类的私有属性和私有方法! ----- 这是内部类中最好用最强大的作用!!!
eg:
package Demo.OOP;
public class Inclass {//内部类的应用,这里只了解一下!
public static void main(String[] args) {
Outer outer = new Outer();
outer.out();
System.out.println("=========================");
//通过外部类的对象来实例化内部类!!!
Outer.Inner inner = outer.new Inner();
inner.in();
inner.getID();
System.out.println("=======================");
//匿名对象的使用,不用将实例保存到变量中!!!其中outer.new Inner()就叫匿名内部类
outer.new Inner().getID();
}
}
class Outer{//外部类
private int id = 10;
public void out(){
System.out.println("这是外部类的方法!");
}
private void outin(){
System.out.println("外部类中的私有方法!");
}
class Inner{//内部类
public void in(){
System.out.println("这是内部类的方法!");
}
//在内部类中可以使用外部类的私有属性和私有方法! ----- 这是内部类中最好用最强大的作用!!!
public void getID(){
System.out.println(id);//获取外部类中私有的属性id
out();//外部类的普通方法
outin();//外部类的私有方法
}
}
}
异常
异常(Exception)指程序运行中出现的不期而至的各种状况,如:文件找不到、网络连接失败、非法参数等!
异常发生在程序运行期间,它影响了正常的程序执行流程!
要理解Java异常处理是如何工作的,你需要掌握以下三种类型的异常:
- 检查性异常:最具有代表性的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如:要打开一个不存在的文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略!
- 运行时异常:运行时异常是可能被程序员避免的异常,与检查性异常相反,运行时异常可以在编译时被忽略!
- 错误(ERROR):错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如:当栈溢出时,一个错误就产生了,它们在编译也检查不到的。
异常体系结构
JAVA把异常当作对象来处理,并定义一个基类java.lang.Throwable作为所有异常的超类。
在java API中已经定义了许多异常类,这些异常类分为俩类,错误Error和异常Exception,其中错误Error是无法预见的!
这张图只表示了部分,仅供了解基本的一些结构!
Error
Error对象由JAVA虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关!!!
java虚拟机运行错误(Virtual MachineError),当JVM不再有继续执行操作所需的内存资源时,将出现OutofMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止!
还有发生在虚拟机试图执行应用时,如类定义错误(NoClassDefFoundError)、链接错误(LinkageError)。这些错误是不可查的,因为它们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况!
Exception
在Exception分支中有一个重要的子类RuntimeException(运行时异常),除了运行时异常之外的所有异常可以被统称为非运行时异常,但他们俩的本质还是Exception而不是Error!
- ArrayIndexOutOfBoundsException(数组下标越界)
- NullPointerException(空指针异常)
- ArithmeticException(算术异常)
- MissingResourceException(丢失资源)
- ClassNotFoundException(找不到类)等异常,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理
这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能地避免这类异常的发生!
Error和Exception的区别:Error通常是灾难性的致命的错误,是程序无法控制和处理的,当出现这些异常时,java虚拟机(JVM)一般会选择终止线程;Exception通常情况下是可以被程序处理的,并且在程序中应尽可能的去处理这些异常!
异常处理机制
- 抛出异常
- 捕获异常
异常处理中的5个关键字:
- try
- catch
- finally
- throw
- throws
eg:
package Demo.Exception;
public class ExceptionDemo01 {//捕获和抛出异常!!!
public static void main(String[] args) {
int a = 1;
int b = 0;
//在IDE中想要自动生成try,catch语句的快捷键是: 先选中要监控是否会出现异常的代码然后按住 Ctrl + Alt + T ,就会出现很多可以一键生成的代码块,选中try/catch/finally则一键生成好了!!
try {//try监控区域
//System.out.println(a/b);
new ExceptionDemo01().a();
} catch (Exception e) {//catch 捕获异常,catch(想要捕获的异常类型)方法里的参数就是想要捕获的异常类型,其中异常的超类就是Throwable,其范围最广什么异常都能捕获到!!!
System.out.println("程序出现异常,变量b不能为0!");
//e.printStackTrace();该方法默认产生的,作用就是System.err,将异常输出出来而已!
} catch (Throwable t ){
//System.exit(0);System.exit()可以手动的将程序结束!!!
//在java中支持写多个catch语句捕获异常,但有个规律就是要将范围大的异常写在下面,从上到下逐渐扩大,如果将范围广的异常写在上面,下面的catch代码块中参数的异常类型没有上面广就会报错!!!
System.out.println("程序出现异常,方法无限递归了!");
}
finally {
//处理善后工作,既在try代码块里的代码不管有没有发生异常,该finally代码块中的语句都执行,一般放在最后!例如:IO流,资源等需要关闭,可以在finally代码块中执行!
System.out.println("finally");
}
try {//算术异常ArithmeticException属于运行时异常,如果不用try,catch语句来捕获异常的话,那么程序遇到这个异常,程序就会停止了!!!
//所以一般都用try,catch语句来捕获异常,从而使得程序不用停止,可以继续往下走!!!
new ExceptionDemo01().test(1,0);
} catch (ArithmeticException e) {
e.printStackTrace();
}
System.out.println("出现异常!");
}
public void a(){
b();
}
public void b(){
a();
}
//假设在这个方法中处理不了这个异常,我们就可以将异常抛出去,抛到更高级,在方法上抛出异常使用throws + 异常类型
// 注意在方法上抛出异常使用throws,在方法里抛出异常使用throw!!!
public void test(int a,int b) throws ArithmeticException{
/*if(b == 0){
throw new ArithmeticException();//主动的在方法里抛出异常使用 thirow,一般在方法中使用!其中ArithmeticException是一个异常类型。
}*/
System.out.println(a/b);
}
}
自定义异常
使用JAVA内置的异常类可以描述在编程时出现的大部分异常情况,除此之外,用户还可以自定义异常。用户自定义异常,,只需要继承EXception类即可。
在程序中使用自定义异常类,大体可分为以下几个步骤:
- 创建自定义异常类
- 在方法中通过throw关键字抛出异常对象
- 如果在当前抛出异常的方法处理异常,可以使用try-catch语句捕获并处理;否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作。
- 在出现异常方法的调用者中捕获并处理异常
eg:
package Demo.Exception;
//自定义的异常类
public class MyExceptiontest {
static void test(int a) throws MyException{
System.out.println("传递的参数:"+a);
if(a > 10){
throw new MyException(a);//抛出异常
}
System.out.println("OK!");
}
public static void main(String[] args) {
try {//try catch语句捕获异常!
test(11);
} catch (MyException e) {
//在这里可以添加一些处理异常的代码!!!
System.out.println("MyException=>"+e);
}
}
}
class MyException extends Exception{//自定义的异常类必须继承Exception类
//创建自定义的异常类时可以看一下java中已经定义好的异常类的创建格式,自定义异常类用的不多因为java提供的异常类能解决java中大多数的异常,自定义异常作为了解!
private int detail;
public MyException(int a) {
this.detail = a;
}
@Override
public String toString() {//异常的打印信息
return "MyExceptiontest{" +
"detail=" + detail +
'}';
}
}
实际应用中的经验总结:
- 处理运行时异常时 ,采用逻辑去合理规避同时辅助try-catch处理
- 在多重catch块后面,可以加一个catch(Exception)来处理可能会被遗漏的异常
- 对于不确定的代码,也可以加上try-catch,处理潜在的异常
- 尽量去处理异常,切忌java默认产生的处理只是简单地调用printStackTrace()去打印输出
- 具体如何处理异常,要根据不同的业务需求和异常类型去决定
- 尽量添加finally语句块去释放占用的资源 IO~ Scanner~ 抛出异常
• }
System.out.println(“OK!”);
}
public static void main(String[] args) {
try {//try catch语句捕获异常!
test(11);
} catch (MyException e) {
//在这里可以添加一些处理异常的代码!!!
System.out.println(“MyException=>”+e);
}
}
}
class MyException extends Exception{//自定义的异常类必须继承Exception类
//创建自定义的异常类时可以看一下java中已经定义好的异常类的创建格式,自定义异常类用的不多因为java提供的异常类能解决java中大多数的异常,自定义异常作为了解!
private int detail;
public MyException(int a) {
this.detail = a;
}
@Override
public String toString() {//异常的打印信息
return "MyExceptiontest{" +
"detail=" + detail +
'}';
}
}
实际应用中的经验总结:
- 处理运行时异常时 ,采用逻辑去合理规避同时辅助try-catch处理
- 在多重catch块后面,可以加一个catch(Exception)来处理可能会被遗漏的异常
- **<font color="red">对于不确定的代码,也可以加上try-catch,处理潜在的异常</font>**
- 尽量去处理异常,切忌java默认产生的处理只是简单地调用printStackTrace()去打印输出
- 具体如何处理异常,要根据不同的业务需求和异常类型去决定
- **<font color="red">尽量添加finally语句块去释放占用的资源 IO~ Scanner~</font>**