< 笔记 > Java SE - 03 Java SE 面向对象

03 Java SE 面向对象

By Kevin Song

  • 03-01 类与对象
  • 03-02 封装
  • 03-03 构造方法
  • 03-04 单例设计模式
  • 03-05 继承
  • 03-06 接口
  • 03-07 多态
  • 03-08 内部类
  • 03-09 异常
  • 03-10 Object类
  • 03-11 包

面向对象的四个基本特征
- 封装
- 继承
- 抽象
- 多态

03-01 类与对象

类:事物的描述

对象:该类事物的实例

描述汽车

  • 属性
    • 轮胎数
    • 颜色
  • 行为
    • 运行
class Car {
    int num;//成员变量
    String color; //成员变量
    void run() { //成员方法
        System.out.println(num+"..."+color);
    }
}

class CarDemo {
public static void main(String[ args[]) {
        Car c = new Car();//c是一个类类型的引用变量,指向了该类的对象。
        c.num = 4;
        c.color = "Red";
        c.run();
    }
}

成员变量和局部变量的区别

  • 定义位置不同
    • 成员变量定义在中,整个类中都可以访问
    • 局部变量定义在方法,语句,局部代码块中,只在所属的区域有效
  • 存放内存不同
    • 成员变量存在于堆内存的对象中
    • 局部变量存在于栈内存的方法中
  • 释放时间不同
    • 成员变量随着对象的创建而存在,随着对象的消失而消失
    • 局部变量随着所属区域的执行而存在,随着所属区域的结束而释放
  • 初始化不同
    • 成员变量都有默认初始化值
    • 局部变量没有默认初始化值

类类型参数使用

//骑车改装厂
public static void show(Car c) { //类类型的变量一定指向对象,要不就是null。
    c.num = 3;
    c.color = "black";
    System.out.println(c.num+“...”+c.color);
}

匿名对象:当对象对方法仅调用一次,就可以简化成匿名对象

new Car();//定义对象的简写格式
new Car().run();

03-02 封装

隐藏对象的属性和实现细节,仅对外提供公共访问方式

class Person {
    private int age; //私有:只在本类中有效,外部无法访问

    public void setAge(int a) {
        if (a>0 && a<130)
            age = a;
        else
            System.out.println("数据错误");
    }
    void speak() {
        System.out.println("age="+age);
    }
}
clasee PersonDemo {
    public static void main(String[] args) {
        Person p = new Person();
        p.setAge(20);
        p.speak();
    }
}

03-03 构造方法

构造对象的时候用的方法

特点:

  • 方法名与类名相同
  • 不用定义返回值类型
  • 没有具体的返回值

作用:

单一对象进行初始化

默认构造方法:

一个类中如果没有定义过构造方法,那么该类中会有一个默认的空参数构造方法。

class Person {
    private int age;
    private String name;//私有:只在本类中有效,外部无法访问

    //定义一个Person类的构造方法
    Person() {
        Syetem.out.println("person run");
    }

    //初始化就有名字
    Person(String n) {
    name = n;
    }

    //初始化就有名字和年龄
    Person(String n, int a) {
    name = n;
    age = a;
    }

    void speak() {
        System.out.println("age="+age);
    }
}
clasee PersonDemo {
    public static void main(String[] args) {
        Person p = new Person();//构造方法:构建创造对象时用的方法
        Person p1= new Person("就很舒服");//重载构造方法
        Person p2= new Person("就很舒服",10)//重载构造方法
    }
}

构造方法和一般方法的区别:

    • 构造方法:对象创建时,就会被调用,进行对象初始化
    • 一般方法:对象创建后,需要方法的功能时才调用
    • 构造方法:对象创建时,只调用一次
    • 一般方法:对象创建后,可以被重复调用

this关键字

当成员变量和局部变量重名,可以用关键字this区分

class Person {
    private int age;
    private String name;//私有:只在本类中有效,外部无法访问

    //定义一个Person类的构造方法

    Person(String name) {
    this.name = name;//this代表当前对象
    }

    void speak() {
        System.out.println("age="+age);
    }
}

this关键字细节:

  1. 一般方法不能调用构造方法,因为构造方法是给对象初始化,被对象所调用。
  2. 构造方法之间互相调用
Person(String name) {
    this.name = name;
}

Person(String name, int age) {
    this(name);//必须放在构造方法第一行
    this.age = age;
}

static关键字

  • static是一个修饰符,用于修饰成员(成员变量和成员方法)
  • static修饰的成员被所有对象共享
  • static**优先于**对象存在,因为static成员随着类创建而存在
  • static修饰的变量可以被类名直接调用Person.country

成员变量和静态变量的区别:

String name;
static String country = "CN";
  • 两个变量的生命周期不同
    • 成员变量随着对象的创建而存在,随着对象的被回收而释放。
    • 静态变量随着的加载而存在,随着类的消失而消失。
  • 调用方式不同
    • 成员变量只能被对象调用
    • 静态变量可以被对象类名调用
  • 别名不同
    • 成员变量也称为实例变量
    • 静态变量称为类变量
  • 数据存储位置不同
    • 成员变量存储在堆内存的对象中,是对象的特有数据
    • 静态变量数据存储在方法区,是对象的共享数据

static注意事项

  • 静态方法只能访问静态成员
  • 静态方法中不可以使用this或者super关键字
  • 主方法是静态的

主方法解析

public static void main(String[] args)
  • public:因为权限必须是最大
  • static:不需要对象的,直接用主方法所属类名调用即可
  • void:主方法没有具体的返回值
  • main:方法名,JVM识别的固定的名字
  • String[] args:主方法的参数列表,是一个数组类型的参数,元素都是字符串类型

static关键字使用场合

  • 静态对象: 如果是相同的数据,对象不需要做修改,只需要使用即可,不需要存储在对象中,这个变量可以用static修饰
  • 静态方法: 如果方法不需要访问非静态的成员变量,可以定义成static

静态代码块

特点:随着类的加载而执行。而且只执行一次

作用:给初始化

class StaticCode {
    static {
        System.out.println("haha");
    }
}

构造代码块

{//构造代码块。可以给所有对象进行初始化
    System.out.println("Person run");
}
  • 构造方法给对应的对象进行针对性的初始化
  • 构造代码块给所有对象初始化

数组工具类

class ArraryToolDemo {
    public static void main(String[] args) {
        int[] arr = {3,5,7,72,6};
        int max = ArrayTool.getIndex(arr,10);//静态方法可以直接被类名调用,不需要创建对象,节约内存空间
        System.out.println("max="+max);
    }
}
/**
建立一个用于操作数组的工具类,期中包含着对数组常见的操作
@author Kevin Song
@version V1.0
*/
class ArrayTool {
    private ArrayTool() {}//该类中的方法都是静态的,所以该类不需要创建对象,为了保证不让其他人创建该类对象可以将构造方法私有化
    /**
    获取整形数组的最大值
    @param arr 接收一个元素为int类型的数组
    @return 该数组的最大的元素值
    */
    public static int getMax(int[] arr) {
        int max = arr[0];
        for (int x =1; xif (arr[x]>max)
                max = arr[x];
        }
        return max;
    }
}

03-04 单例设计模式

作用:可以保证一个类在内存中的对象唯一

实现思想:

  1. 不允许其他程序用new创建该类对象。
  2. 在该类创建一个本类实例
  3. 对外提供一个方法让其他程序可以获取该对象

步骤:

  1. 私有化该类构造方法
  2. 通过new在本类中创建一个本类对象
  3. 定义一个公有的方法,将创建的对象返回
//通过在类中创建一个对象供其他程序使用
//不允许其他程序自己new对象
//对该类对象的修改都是对同一个对象的修改

//饿汉式
class Single {//类一加载,对象就已经建立
    private static Single s = new Single();
    private Single(){}//私有构造方法不允许其他程序new对象
    public static Single getInstance() {
        return s;
    }
}
//懒汉式
class Single2 {//只有调用了getInstance方法才会建立对象

    private static Single2 s = null;
    private Single2() {}//私有构造方法不允许其他程序new对象
    public static Single2 getInstance() {
        if(s==null)
            s = new Single();
        return s;
    }
}
class SingleDemo {
    public static void main(String[] args) {
        Single s = Single.getInstance();
    }
}

03-05 继承

从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。

class Person {
    String name;
    int age;
}
//继承Person类的数据
class Student extends Person {
    void study() {
        System.out.println(name+"..student study.."+age);
    }
}
//继承Person类的数据
class Worker extends Person {
    void work() {
        System.out.println(name+"..worker work.."+age);
    }
}
class ExtendsDemo {
    public static void main(String[] args) {
        Student s = new Student();
        s.name = "Kevin";
        s.age = 24;
        s.study;
    }
}

继承的优点

  • 提高代码的复用性
  • 让类与类之间产生了关系,给第三个特征多态提供了前提

Java中继承的特点

Java中支持单继承。不直接支持多继承,但对C++中的多继承机制进行改良

  • 单继承:一个子类只能有一个直接父类
  • 多继承:一个子类可以有多个直接父类(Java中用接口进行改良)不直接支持,因为多个父类中有相同成员,会产生调用的不确定性

java支持多层(多重)继承

子父类中成员的特点体现:

1. 成员变量

  • 本类的成员和局部变量同名用this区分本类成员变量
  • 子父类中的成员变量同名用super区分父类

原理

  • this:代表一个本类对象的引用
  • super:代表一个父类空间
class Fu {
    private int num = 4;//子类不能直接访问父类中的私有内容
    //子类可以通过父类提供的方法间接访问父类中的私有内容
    public int getNum() {
        return num;
    }
}
class Zi extends Fu {
    private int num = 5;
    void show() {
        System.out.println(this.num+"....."+super.getNum());
    }
}
class ExtendsDemo2 {
    public static void main(String[] args) {
        Zi z = new Zi();
        z.show();
    }
}

2. 成员方法

方法的两个特性

  • 重载(Overload):同一个类中,方法名相同参数列表不同的方法使用叫重载
  • 重写(Override):在子父类中,出现成员方法完全相同的情况,会运行子类方法

重写注意事项:

  • 子类方法重写父类方法时,子类权限必须大于等于父类权限
  • 静态只能覆盖静态,或被静态覆盖
class Fu {
    void show() {
        System.out.println("fu show run");
    }
}
class Zi extends Fu {
    void show() { //重写父类方法
        System.out.println("zi show run");
        super.show();
    }
}
class ExtendsDemo3 {
    public static void main(String[] args) {
        Zi z = new Zi();
        z.show();
    }
}

3. 构造方法

  • 子类构造对象时,访问子类构造方法时,父类构造方法也会运行。
    原因:在子类的构造方法中第一行有一个默认的隐式语句。super();

  • 子类的实例化过程:子类中所有的构造方法默认都会访问父类中的空参数的构造方法
    原因:子类继承父类,获取了父类中内容,需要了解父类是如何对内容进行初始化的。

  • 如果父类中没有定义空参数构造方法,子类的构造方法必须在第一行super明确要调用父类中哪个构造方法。同时子类构方法数中如果使用this调用了本类构造方法时,super就没有了,因为super和this都只能定义在第一行

class Fu {
    Fu() {
        System.out.println("A fu run");
    }
    Fu(int x) {
        System.out.println("B fu run");
    }
}
class Zi extends Fu {
    Zi() {
        //super();隐式语句,调用父类中空参数的构造方法
        System.out.println("C zi run");
    }
    Zi(int x) {
        //super();隐式语句,调用父类中空参数的构造方法
        System.out.println("D zi run");
    }
}
class ExtendsDemo4 {
    public static void main(String[] args) {
        new Zi(6);//输出结果A D
    }
}

对象实例化和初始化过程

一个对象实例化过程

Person p = new Person();
  1. JVM读取指定路径下的Person.class文件,并加载进内存,并会先加载*Person的父类*
  2. 堆内存中开辟空间,分配地址
  3. 在对象空间中,对对象中的属性进行默认初始化
  4. 调用对应的构造方法进行初始化
  5. 在构造方法中第一行调用父类中的构造方法进行初始化
  6. 父类初始化完毕后对子类的属性进行显式初始化
  7. 进行子类构造方法的特定初始化
  8. 初始化完毕后,将地址值复制给引用变量

对象的初始化过程:

  1. 加载子类构造方法
  2. super();加载父类构造方法
  3. 父类构造方法调用被子类重写的方法
  4. 显示初始化
  5. 构造代码块初始化
  6. 子类构造方法具体初始化
class Fu {
    Fu() {
        System.out,println("fu constructor run");
        show();
    }
    void show() {
        System.out.println("hehe");
    }
}
class Zi extends Fu {
    int num = 9; 
    { //第五步 构造代码快初始化
        System.out.println("constructor code..."+num);
    }
    Zi() { //第一步
        super; //第二步
        //第四步 显示初始化 
        System.out.println("zi constructor..."+ num); //第六步
    }
    void show() { //第三步
        System.out.println("show..."+num);
    }
}

final关键字

  • final是一个修饰符,可以修饰类,方法,变量
  • final修饰的类不可以继承
  • final修饰的方法不可以重写
  • final修饰的变量是一个常量,只能被赋值一次
  • 内部类只能访问被final修饰的局部变量
class Fu { //final修饰的类不可以被继承
    void method() { //final修饰的方法不可以被重写
        //调用底层系统资源
    }
}
class Zi extends Fu {
    void method() {
        public static final double MY_PI = 3.14; //final修饰的变量是一个常量,只能被赋值一次
        System.out.println(MY_PI);
    }
}
class FinalDemo {
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}

常量命名规则

全部大写,不同单词之间用_分隔。例:MY_PI

全局变量:public static修饰的变量

public static int num = 9;

全局常量:public static final修饰的变量

public static final double MY_PI = 3.14;

抽象类

当一个类描述一个事物时,没有足够的信息描述事物,就是抽象类

abstract class Pet { //含有抽象方法的类是抽象类
    abstract void voice();//抽象修饰的方法
}
class Dog extends Pet{
    void voice() {
        System.out.println("woof")
    }
}
class Cat extends Pet{
    void voice() {
        System.out.println("meow")
    }
}
class AbstractDemo {
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}

抽象类特点

  1. 方法只有声明没有方法体 { } ,需要被abstract修饰,抽象方法必须定义在抽象类
  2. 抽象类不能被实例化
  3. 抽象类中的抽象方法必须被子类全部重写后才可以被实例化,否则这个子类也是抽象类

抽象类细节

  1. 抽象类中有构造方法,因为需要给子类对象初始化
  2. 抽象类中可以不定义抽象方法,但是很少见,目的是不让这个类创建对象
  3. abstract关键字不可以和这些关键字共存
    • private:因为子类需要重写抽象父类,private无法访问
    • static:静态成员可以直接被类名调用
    • final:final类无法被继承而抽象类必须被继承,final方法无法被重写而抽象方法必须被重写
  4. 抽象类和一般类的异同
    • 相同点:抽象类和一般类都用来描述事物
    • 不同点
      • 一般类有足够的信息描述事物
        抽象类描述事物的信息有可能不足
      • 一般类中不能定义抽象方法
        抽象类中可以定义抽象和非抽象方法
      • 一般类可以被实例化
        抽象类不可以被实例化
  5. 抽象类一定是父类,因为需要子类覆盖其方法后才可以对子类实例化。

示例:

/*
员工:
    属性:姓名,工号,薪水
    行为:工作。
程序员:
    属性:姓名,工号,薪水。
    行为:工作。
经理:
    属性:姓名,工号,薪水,奖金。
    行为:工作。
*/
abstract class Employee {
    private String name;
    private String id;
    private double pay;
    Employee(String name, String id, double pay) {
        this name = name;
        this id = id;
        this pay = pay;
    }
    public abstract void work(); //没有方法体的抽象方法
}
class Programmer extends Employee { //描述程序员
    Programmer(String name, String id, double pay) {
        super(name, id, pay);
    }
    public void work() { //加入方法体,重写父类方法
        System.out.println("programming");
    }
}
class Manager extends Employee { //描述经理
    Manager(String name, String id, double pay, int bonus) {
        super(name, id, pay);
        this.bonus = bonus;
    }
    public void work() { //加入方法体,重写父类方法
        System.out.println("managing");
    }
}
class AbstractTest {
    public static void main(String[] args) {
        System.out.println("Hello World");
        Programmer Kevin = new Programmer(Kevin, 1110009945, 7330);
    }
}

03-06 接口

interface {}

抽象类中的方法都是抽象的抽象类就可以定义为接口

接口中的成员都是公共权限:

  1. 全局常量:
public static final int NUM = 3;
  1. 抽象方法:
public abstract void show(); //返回值类型void可以改变
  • 类与类之间是继承关系
  • 类与接口之间是实现关系
  • 接口与接口之间是继承关系,而且接口之间可以多继承

接口不可以实例化

只能由实现了接口的子类重写了接口中所有的方法后,该子类才可以实例化,否则这个子类是一个抽象类

interface Demo {
    public static final int NUM = 3; //全局常量
    public abstract void show1(); //抽象方法
}
class DemoImp1 implements Demo {
    public static final int NUM = 3; 
    public abstract void show1(){}
}
class InterfaceDemo {
    public static void main(String[] args) {
        DemoImp1 d = new DemoImp1();
        System.out.println(d.NUM);
        System.out.println(DemoImp1.NUM);
        System.out.println(Demo.NUM);
}

接口多实现

接口的方法前修饰符是固定的都是public abstract 返回值类型,因此只要两个接口中的方法名相同,多实现不会存在不确定性

interface A {
    public void show();
}
interface B {
    public void show();
}
class Test implements A, Z {
    public void show(){} //接口都是抽象方法,不存在不确定性,因此可以多实现
}

一个类实现另一个类的同时,还可以实现多个接口,避免了单继承的局限性

class Test2 extends Q implements A,Z {}

接口和抽象类的异同点

相同点:都是向上抽取而来

不同点

与子类的关系:

  • 抽象类需要被继承,而且只能多继承
  • 接口需要被实现,而且可以多实现

方法的定义:

  • 抽象类中可以定义抽象与非抽象方法,子类继承后可以直接使用非抽象方法
  • 接口中只能定义抽象方法,必须由子类去实现

性质

  • 抽象类中的继承是is a关系,在定义该体系的基本共性内容
  • 接口的实现是 like a 关系,在定义体系额外功能

示例:

//Dog Classification: GuideDog, DetectDog
abstract class Dog {
    abstract void bark();
}
interface Guide {
    abstract void guide();
}
class GuideDog extends Dog implement Guide{
    public void bark() {}
    public void guide() {}
}

接口的应用

/*
Laptop Usage
为了扩展笔记本的功能,定义一个规则,以后不论什么设备只要符合这个规则就可以使用
*/
interface USB {
    public void open();
    public void close();
}
//一年后,出现了U盘和鼠标
//实现规则
class Udisk implement USB {
    public void open() {
        System.out.println("Udisk Open");
    }
    public void open() {
        System.out.println("Udisk Open");
    }
}
class UsbMouse implement USB {
    public void open() {
        System.out.println("UsbMouse Open");
    }
    public void open() {
        System.out.println("UsbMouse Open");
    }
}
class Laptop {
    public static void main(String[] args) {
        useUSB(new Udisk());
        useUSB(new UsbMouse());
    }
    //使用规则
    public static void useUSB(USB u) { //接口类型的引用,用于指向接口的子类对象
        u.open();
        u.close();
    }
}

03-07 多态

定义:某一类事物的多种存在形态

体现:父类或者接口的引用指向其子类的对象

Fu z = new Zi();

多态优缺点

  • 优点:提高了代码的扩展性,前期定义的代码可以使用后期的内容
  • 缺点:父类引用不能调用子类的特有内容

多态的前提

  • 必须有关系,继承或者实现
  • 必须要重写
/*
狗具备狗形态,也具备动物形态
猫具备猫形态,也具备动物形态
*/
abstract class Animal {
    abstract void eat();
}
class Dog extends Animal {
    void eat() {
        System.out.println("eat bone");
    }
    void housekeep() {
        System.out.println("house keeping");
    }
}
class Cat extends Animal {
    void eat() {
        System.out.println("eat fish");
    }
    void catchMouse() {
        System.out.println("catching mouse");
    }
}
class DuoTaiDemo {
    public static void main(String[] args) {
        Animal c = new Cat();//父类引用指向子类对象
        Animal d = new Dog();//父类引用指向子类对象
        method(c);
        method(d);
    public static void method(Animal a) {
        a.eat(); //Animal父类特有内容可以直接调用
        if(a instanceof Cat) { //判断对象是否为猫类,如果是
            Cat c = (Cat)a; //转换成猫类
            c.catchMouse(); //调用猫类特有方法
        } else if(a instanceof Dog) { //判断对象是否为狗类,如果是
            Dog d = (Dog)a; //转换成狗类
            d.housekeep(); //调用狗类特有方法
        }
    }
}

转型

  • 向上转型:限制对特有功能的访问
  • 向下转型:可以使用特有功能

父类引用指向子类对象时,对象只能访问子父类共有内容。

Animal c = new Cat();//向上转型,猫对象提升到动物类型
c.eat();//只能访问共有功能,不能访问特有功能
Cat c = (Cat)a;//向下转型,猫对象降低到猫类
c.catchMouse//可以访问特有功能

类型判断

instanceof:用于判断对象的具体类型。只能用于引用数据类型判断

public static void method(Animal a) {
    a.eat(); //Animal父类特有内容可以直接调用
    if(a instanceof Cat) { //判断对象是否为猫类,如果是
        Cat c = (Cat)a; //转换成猫类
        c.catchMouse(); //调用猫类特有方法
    } else if(a instanceof Dog) {
        Dog d = (Dog)a;
        d.housekeep();
    }
}

多态中的成员特点

成员变量

  • 编译时,参考引用型变量所属的类中是否有调用的成员变量。有,编译通过;没有,编译失败。
  • 运行时,参考引用型变量所属的类中是否有调用的成员变量。并运行该所属类中的成员变量
  • 总结:参考引用型变量所属的类中的成员变量

示例:

class Fu {
    int num = 3;//如果父类没有num,则编译失败
}
class Zi extends Fu {
    int num = 5;
}
class DuoTaiDemo {
    public static void main(String[] args) {
        Fu f = new Zi();
        System.out.println(f.num);// 输出3
    }
}

成员方法

  • 编译时,参考引用型变量所属的类中是否有调用的方法。有,编译通过;没有,编译失败。
  • 运行时,参考的是对象所属的类中是否有调用的方法。并运行该所属类中的成员变量
  • 总结:编译看左边,运行看右边

写程序时只能调用父类的方法,实际运行时调用的是子类重写父类后的方法。

class Fu {
    void show() {
        System.put.println("fu show");
    }
}
class Zi extends Fu {
    void show() {
        System.put.println("zi show");
    }
}
class DuoTaiDemo {
    public static void main(String[] args) {
        Fu f = new Zi();
        f.show();//输出"zi show"
    }
}

静态方法

  • 编译时,参考引用型变量所属的类中是否有调用的静态方法
  • 运行时,参考引用型变量所属的类中是否有调用的静态方法
  • 总结:编译运行都看左边

其实对于静态方法,是不需要对象的,可以直接被类名调用

class Fu {
    static void method() {
        System.put.println("fu static");
    }
}
class Zi extends Fu {
    static void method() {
        System.out.println("zi static");
    }
}
class DuoTaiDemo {
    public static void main(String[] args) {
        Fu f = new Zi();
        f.method();//输出"fu static"
    }
}

03-08 内部类

定义在一个类内部的类,又称为内置类,嵌套类

内部类访问特点

  • 内部类可以直接访问外部类中的成员
  • 外部类需要建立内部类的对象才能访问内部类成员
class Outer {
    private int num = 3; //内部类可以直接访问私有内容
    static class Inner { //内部类
        void show() {
            System.out.println("show run.."+num);
        }
        //如果内部类中方法是静态的,则内部类也是静态的
        static void staticShow() { //静态直接调用,不需要对象
            System.out.println("staticShow run.."+num);
        }
    }
    //外部类需要创建内部类对象才能访问非静态内部类内容
    public void method() { 
        Inner in = new Inner();
        in.show();
    }
}
class InnerClassDemo {
    public static void main(String[] args) {
        //直接访问外部类的成员
        Outer out = new Outer();
        o.method;

        //直接访问外部类中的内部类中的成员
        Outer.Inner in = new Outer().new Inner();
        in.show();

        //如果内部类是静态的,相当于一个外部类
        Outer.Inner in = new Outer.Inner();
        in.show();

        //如果内部类是静态的,成员也是静态的,直接调用,不需要对象
        Outer.Inner.staticShow();
    }
}

内部类可以存放在局部位置上

内部类在局部位置上只能访问局部中被final修饰的变量

class Outer {
    int num = 3;
    void method() {
        final int x = 9; //内部类中访问的局部变量必须是final
        class Inner {
            void show() {
                System.out.println("show..."+x);
            }
        Inner in = new Inner();
        in.show();
        }
    }
}

匿名内部类

内部类的简写格式

new 父类or接口() {
    子类内容;
}

前提:内部类必须继承一个外部类,或者实现一个接口

匿名内部类其实就是一个子类对象

abstract class Demo {
    abstract void show();
}
class Outer {
    int num = 4;

    //匿名内部类不能有名字,所以不能这样定义
    //class Inner extends Demo {
    //    void show() {
    //        System.out.println("show..."+num);
    //    }
    //}

    public void method() { 
        //相当于 new Inner().show();
        new Demo() { //创建匿名内部类
            void show() { //重写Demo抽象方法
                System.out.println("show..."+num);
            }
        }.show();
    }
}
class InnerClassDemo {
    public static void main(String[] args) {
        new Outer().method();
    }
}

匿名内部类应用

当方法参数是接口类型时,而且接口中的方法不超过三个,可以用匿名内部类作为实际参数进行传递

interface Inter {
    void show1();
    void show2();
}
class Outer {
    //class Inner implements Inter {
    //    public void show1() {}
    //    public void show2() {}
    //}
    public void method() {
    //    Inner in = new Inner();
    //    in.show1();
    //    in.show2();
        Inter in = new Inter() { //重写接口方法,创建匿名内部类对象
            public void show1() {}
            public void show2() {}
        }
        in.show1;
        in.show2;
    }
}

03-09 异常

异常:运行时发生的不正常情况,根据面向对象的思想被java封装成了对象

异常类:在java中用类的形式对不正常的情况进行了描述和封装对象,描述不正常的情况的类,就称为异常类。

class ExceptionDemo {
    public static void main(Stirng[] args) {
        int[] arr = new int[3];
        arr = null;
        System.out.println(arr[3]);
    }
    public static void sleep(int time) {
        if(time < 0) {
            //抛出一个对象
            new FuTime();//新建一个负time对象,这个对象中包含问题的名称,信息,位置等信息
        }
        if (time > 100000) {
            //抛出一个对象
            new BigTime();
        }
    }
}

异常体系

不正常的情况分成两类

Throwable(可抛出):可以被throwsthrow两个关键字操作的类和对象都具备可抛性

  • Error:不可处理的问题
    特点:由JVM抛出的严重性的问题,这种问题一般不针对处理,直接修改代码
  • Exception:可处理的问题

该体系的特点:子类的后缀名都是用其父类名作为后缀

ArrayIndexOutOfBoundsException

异常处理方式

自动抛出异常

//自动抛出异常:
class Demo {
    public void method(int[] arr, int index) {  
        //JVM在这里封装了一个对象然后丢出:throw new ArrayIndexOutOfBoundsException(index)
        System.out.println(arr[index]);
    }
}
class ExceptionDemo {
    public static void main(Stirng[] args) {
        int[] arr = new int[3];
        Demo d = new Demo();
        d.method(arr,3);
    }
}

手动抛出异常:

自定义异常:java中没有定义过的异常,按照java异常的创建思想和面向对象的思想,对异常进行自定义描述,并封装成对象。

自定义异常类:自定义异常类必须继承异常体系,因为只有称为异常体系的子类才具有可抛性,才可以被throw,throws操作。要么继承Exception,要么继承RuntimeException

  • 继承Exception或者其子类(编译时异常):
    • 声明:只声明不捕捉,无法通过编译,可以自定义报错的信息
      • 自定义异常类,继承Exception或者其子类
      • 通过throws 类名 在方法名后声明自定义异常类
      • 通过throw new 类名(); 创建对象
      • 如果需要程序通过编译正常运行,需要用Try-Catch对抛出的异常进行处理
    • 捕捉:try-catch捕捉处理异常,处理完输出提示信息并且程序继续运行
  • 继承RuntimeException(运行时异常):继承了RuntimeException的子类可以不用Try-Catch或者throws就可以通过编译。

throw:异常抛出的关键字
throws:声明自定义异常类的关键字

throws和throw的区别

  • throws使用在方法上
    throw使用在方法内
  • throws抛出的是异常类,可以抛出多个,用逗号隔开
    throw抛出的是对象

Exception类的所有子类中分为两种

  • 编译时异常:其他子类
  • 运行时异常:RuntimeException和其子类

异常声明

只声明,不处理的异常,无法通过编译,可以自定义报错信息

//手动抛出异常
/*
角标为负的异常在java中没有定义过,需要自定义
*/
class FuShuIndexException extends Exception {
    FuShuIndexException() {}//空参数构造方法
    FuShuIndexException(String msg) {
        super(msg);//直接调用父类构造方法
    }
}
class Demooo {
    //创建自定义异常类对象的方法需要用throws声明自定义异常
    public int method(int[] arr, int index) throws FuShuIndexException {
        if(index>=arr.length) {
            //手动抛出异常
            throw new ArrayIndexOutOfBoundsException("数组角标越界"+index);
        } else if(index<0) {
            throw new FuShuIndexException("数组角标不能是负数"+index);
        }
        return arr[index];
    }
}
class ExceptionDemo {
    //有自定义异常的主方法需要用throws声明自定义异常
    public static void main(String[] args) throws FuShuIndexException {
        int[] arr = new int[3];
        Demooo d = new Demooo();
        d.method(arr,3);
    }
}

异常捕捉

try-catch处理完程序可以继续运行

try {
    需要被检测异常的代码
}
catch(FuShuIndexException e) {
    处理异常的代码
}
catch(Exception e) { //多个catch,父类catch一定要放在后面
    处理异常的代码
}
finally {
    一定会被执行的代码
}

try-catch-finally代码块特点

  • try catch finally
  • try catch:当没有资源需要释放时,可以不用finally
  • try finally:异常无法直接catch处理,但是资源需要关闭
class FuShuIndexException extends Exception {
    FuShuIndexException() {}//空参数构造方法
    FuShuIndexException(String msg) {
        super(msg);//直接调用父类构造方法
    }
}
class Demoo {
    public int method(int[] arr, int index) throws FuShuIndexException {
            if(index<0) {
               throw new FuShuIndexException("Index must be positive");
            }
        return arr[index];
    }
}
class ExceptionDemo {
    public static void main(String[] args) {
        int[] arr = new int[3];
        Demoo d = new Demoo();
        try {
            int num = d.method(arr, -3);
            System.out.println("num=" + num);
        } catch (FuShuIndexException e) { 
            //FuShuIndexException e = new FuShuIndexException("Index must be positive")
            //获得自定义的异常提示信息
            System.out.println("Message:"+e.getMessage());
            //获得字符串形式的自定义异常类信息
            System.out.println("String:"+e.toString());
            //获得出现异常的代码位置
            e.printStackTrace();
            System.out.println("负数角标异常");
        }
        System.out.println("over");
    }
}
/*
输出:
Message:Index must be positive
FuShuIndexException: Index must be positive
String:FuShuIndexException: Index must be positive
    at Demoo.method(ExceptionDemo.java:10)
负数角标异常
    at ExceptionDemo.main(ExceptionDemo.java:20)
over
*/

异常处理的原则

  • 方法内如果抛出(throw)需要检测的异常,那么方法名后面必须要声明(throws),否则必须在方法内部用try-catch捕捉,否则编译失败。
  • 如果调用到了声明异常的方法,要么try-catch,要么throws,否则编译失败
  • 功能内容可以解决,就用try-catch。解决不了,用throws让调用者解决。
  • 一个方法如果抛出多个异常,那么调用时,必须有对应多个catch进行针对性处理。

finally代码块

通常用于关闭(释放)资源,比如关闭数据库连接

class Demo {
    public void show(int index) throws ArrayIndexOutOfBoundsException :
        if(index < 0)
            throw new ArrayIndexOutOfBoundsException("越界");
        int[] arr = new int[3];
        return arr[index];
    }
}
class ExceptionDemo {
    public static void main(String[] args) {
        Demo d = new Demo();
        try {
            int num = d.show(-3);
            System.out.println("num"+num);
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println(e.toString());
            //return; 如果加了return;则不会运行over
            //System.exit(o);如果加了这句,则不会运行finally代码块,直接退出JVM
        } finally { //一定会被执行,除非System.exit(0);退出JVM
            System.out.println("finally");
        }
        System.out.println("over");
    }
}

异常应用

//自定义蓝屏异常类
class BlueScreenException extends Exception {
    BlueScreenException(String msg) {
        Super(msg);
    }
}
//自定义冒烟异常类
class MaoYanException extends Exception {
    MaoYanException(String msg) {
        Super(msg);
    }
}
//自定义无法处理异常类
class NoPlanException extends Exception {
    NoPlanException(String msg) {
        Super(msg);
    }
}
class Computer {
    private int state = 0;
    //定义电脑运行功能
    public void run() throws BlueScreenException, MaoYanException { 
        if(state == 1) { //状态值为1时电脑蓝屏
            throw new BlueScreenException("蓝屏");
        } else if(state == 2)
            throw new MaoYanException("冒烟");
        System.out.println("电脑运行");
    }
    //定义电脑重启功能
    public void reboot() {
        state = 0; //重置电脑到正常状态
        System.out.println("重启");
    }
}
class Teacher {
    private String name;
    private Computer comp;
    Teacher(String name) {
        this.name = name;
    }
    public void teach() throws NoPlanException { 
        try {
            comp.run(); //电脑运行
            System.out.println("讲课"); //正常讲课
        } catch (BlueScreenException e) {
            System.out.println(e.toString());
            comp.reset();
            teach();
        } catch (MaoYanException e) {
            System.out.println(e.toString());
            test();
            //异常转换:收到冒烟异常,转换成NoPlan异常对外进行告知
            throw new NoPlanException("课时进度无法完成");
        }
    }
    public void test() {
        System.out.println("大家练习");
    }
}
class ExceptionTest {
    public static void main(String[] args) {
        Teacher t = new Teacher("Kevin");
        try (
            t.teach();
        ) catch(NoPlanException e) {
            System.out.println(e.toString()+"...");
            System.out.println("换老师");
        }
    }
}

异常的注意事项:

  • 子类在重写父类方法时,父类的方法如果跑出了异常,那么子类的方法只能抛出父类的异常或者该异常的子类
  • 如果父类抛出多个异常,那么子类只能抛出父类异常的子集

03-10 Object类

Object类是所有类的根类,是不断抽取而来,具备所有对象都具备的共性内容

Object类常用方法

equals方法

当两个引用值指向同一个对象时才返回true

class Person extends Object {
    private int age;
    Person(int age) {
        this.age = age;
    }
}
Class EqualsDemo {
    public static void main(String[] args) {
        Person p1 = new Person();
        Person p2 = new Person();
        Person p3 = p1;

        System.out.println(p1 == p2);//false
        System.out.println(p1.equals(p2));//false
        System.out.println(p1.equals(p3));//true
    }
}

equals方法重写

equals方法一般都会被重写,根据对象的特有内容,建立判断对象是否相同的依据

class Person extends Object {
    private int age;
    Person(int age) {
        this.age = age;
    }
    public boolean equals(Object obj) {//重写Object类中的equals方法,进行对象年龄比较
        if(!(obj instanceof Person)) {
            return false;
            throw new ClassCastException("类型错误");
        }
        Person p = (Person)obj;
        return this.age == p.age;
    }
}
Class EqualsDemo {
    public static void main(String[] args) {
        Person p1 = new Person(20);
        Person p2 = new Person(20);

        System.out.println(p1 == p2);//false
        System.out.println(p1.equals(p2));//true
        System.out.println(p1.equals(new Demo()));//报错,类型错误
    }
}

hashCode方法

判断两个对象是否完全相同

class Person extends Object {
    private int age;
    Person(int age) {
        this.age = age;
    }
    public int hashCode() { //重写object类中的hashCode方法,输出年龄
        return age;
    }
}
Class EqualsDemo {
    public static void main(String[] args) {
        Person p1 = new Person(20);
        Person p2 = new Person(20);

        System.out.println(p1);//输出p1的哈希值
        System.out.println(p1.hashCode());//输出p1的哈希值
        System.out.println(Integer.toHexString(p1.hashCode()));//输出p1的十六进制哈希值

    }
}

getClass方法

获取当前对象所属字节码对象

Class clazz1 = p1.getClass();
Class clazz1 = p2.getClass();
System.out.println(clazz1 == clazz2);//true
System.out.println(clazz1.getNma());//Person

toString方法

返回对象的字符串表示形式,它的值等于:

getClass().getNmae() + '@' + Integer.toHexString(hashCode())

建议重写该方法

public String toString() {
    return "Person: " + age;
}

System.out.println(p1);//Person: 20
System.out.println(p1.getClass().getNmae() + '@' + Integer.toHexString(hashCode()));////Person@61de33

03-11 包

对类文件进行分类管理

package allpack.boypack.mypack;

class PackageDemo {
    public static void main(String[] args) {
        System.out.println("Hello Package!");
    }

}
//javac -d . PackageDemo.java
//java mypack.PackageDemo

包与包之间的访问

包与包之间的类进行访问,被访问的包中的类必须是public的,被访问的包中的类的方法也必须是public

package packa;

public class DemoA { //包与包之间访问必须是public
    public void show() //包与包之间访问必须是public{
        System.out.println("demoa show run");
    }
}
package packb;

public class DemoA { //包与包之间访问必须是public
    protected void method() { //protected只能被继承了父类的访问
        System.out.println("demoa show run");
    }
}
package mypack;

class PackageDemo {
    public static void main(String[] args) {
        packa.DemoA d = new packa DemoA();
        d.show();
        System.out.println("Hello Package!");
    }
}
位置 public protected default private
同一类中 ok ok ok ok
同一包中 ok ok ok
子类中 ok ok
不同包中 ok

导入import

package packa;

public class DemoA { //包与包之间访问必须是public
    public void show() //包与包之间访问必须是public{
        System.out.println("demoa show run");
    }
}
package packb;

public class DemoA { //包与包之间访问必须是public
    protected void method() {
        System.out.println("demoa show run");
    }
}
package mypack;
import packa.*//导入了packa包中所有的类
class PackageDemo {
    public static void main(String[] args) {
        packa.DemoA d = new packa DemoA();
        d.show();
        System.out.println("Hello Package!");
    }
}

Jar包
Java的压缩包

你可能感兴趣的:(<,笔记,>,Python,<,笔记,>,Java,SE)