面向对象(十五)

138 阅读16分钟

Static关键字:

static关键字的使用:

  1. static:静态的

  2. static可以用来修饰:属性、方法、代码块、内部类

  3. 使用static修饰属性:

    ​ 3.1 属性,按是否使用sattic修饰,又分为:静态属性 vs 非静态变量(实例变量) ​ 实例变量:创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性 ​ 当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改 ​ 静态变量:创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时, ​ 会导致其他对象调用此静态变量时, ​ 3.2 static修饰属性的其他说明: ​ ① 静态变量随着类的加载而加载。可以通过“类.静态变量”的方式进行调用 ​ ② 静态变量的加载要早于对象的创建 ​ ③ 由于类只会加载一次,则静态变量在内存中也只会存在一份,存在方法区的静态域中 ​ ④   类   实例变量 ​ 类   yes   no ​ 对象   yes  yes ​ 3.3 静态属性举例:System.out ; Math.PI;

  4. 使用static修饰方法:

    ①随着类的加载而加载,可以通过“类.静态方法”的方法进行调用 ②   类   实例变量 类   yes   no 对象   yes   yes ③ 静态方法中,只能调用静态的方法或属性 非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性

  5. static注意点: 5.1 在静态的方法内,不能使用this关键字、super关键字 5.2 关于静态属性和静态方法的使用,都从生命周期的角度去理解

  6. 开发中,如何确定一个属性是否要声明为static的?

    • 属性是可以被多个对象所共享的,不会随着对象的不同而不同的。
    • 类中的常量也常常声明为static

    开发中,如恶化确定一个方法是否要声明为static的?

    • 操作静态属性的方法,通常设置为static的
    • 工具类中的方法,习惯上声明为static的.比如:Math.PI、Arrays

代码块:

类的成员之四:代码块 (类的成员前三:属性、方法、构造器(构造方法))

  1. 代码块的作用:用来初始化类、对象
  2. 代码块如果有修饰的话,只能用static
  3. 分类:静态代码块 vs 非静态代码块
  4. 静态代码块:
    • 内部可以有输出语句
    • 随着类的加载而执行(自动执行),并且只执行一次
    • 作用:初始化类的信息
    • 如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行(推荐合成一个,没有必要造多个代码块)
    • 静态代码块的执行要优先于非静态代码块的执行
    • 静态代码块内只能调用静态的属性、静态的方法、不能调用非静态的结构
  5. 非静态代码块:
    • 内部可以有输出语句
    • 随着类的创建而执行(自动执行)
    • 每创建一个对象,就执行一次非静态代码块
    • 作用:可以在创建对象时,对对象的属性等进行初始化
    • 如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行(推荐合成一个,没有必要造多个代码块)
    • 非静态代码块内可以调用静态的属性、静态的方法、或非静态的属性、方法

对属性可以赋值的位置:

① 默认初始化

② 显式初始化

③ 构造器初始化

④ 有对象之后,可以以通过“对象.属性”或“对象.方法”的方式进行赋值

⑤ 在代码块中赋值

执行顺序:① -> ② \ ⑤ -> ③ -> ④(②与⑤,看写的位置)


public class Test012 {

public static void main(String[] args) {
    String describle = Person.describle;
    Person person = new Person();
}

}
class Person{
    //属性
    String name;
    int age;
    static String describle = "我是人,不是神";
    //构造器
    public Person() {

    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    //方法
    public void eat(){
        System.out.println("人需要吃饭!\n");
    }
    //静态代码块
    static{
        System.out.println("HelloWorld!   Good");
    }
    //非静态代码块
    {
        System.out.println("HelloWorld!   God");
    }
    //静态方法
    public static void print(){
        System.out.println("Hello");
    }
}

抽象类:

abstract关键字的使用:

  1. abstract:抽象的

  2. abstract可以用来修饰类的结构:类、方法

  3. abstract修饰类:抽象类

    • 此类不能实例化
    • 抽象类中一定有构造器,便于子类实体化时调用(涉及:子类对象实体化的全过程)
    • 开发中,都会提供抽象类的子类,让子类对象实体化,完成相关操作
  4. abstract修饰方法:抽象方法

    • 抽象方法只有方法的声明,没有方法体
    • 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。
    • 若子类重写了父类中所有的抽象方法后,此子类方可实体化。
    • 若子类没有重写父类中的所有的抽象方法,则此子类也是一个抽象类,即abstract类

abstract使用的注意点:

  1. abstract不能用来修饰:属性、构造器等结构
  2. abstract不能用来修饰私有方法、静态方法、final的方法、final的类

练习:

public class Test014 {
    public static void main(String[] args) {
        //多态
        Employee manager = new Manager("小明",10001,6000,50000);
        manager.work();
        CommonEmployee commonEmployee = new CommonEmployee();
        commonEmployee.work();
    }

}
abstract class Employee{
    private String name;
    private int id;
    private double salary;

    public Employee() {
    }

    public Employee(String name, int id, double salary) {
        this.name = name;
        this.id = id;
        this.salary = salary;
    }
    public abstract void work();
}
class Manager extends Employee{
    private double bonus;//奖金

    public Manager(String name, int id, double salary, double bonus) {
        super(name, id, salary);
        this.bonus = bonus;
    }

    @Override
    public void work() {
        System.out.println("管理员工,提供公司运行效率\n");
    }
}
class CommonEmployee extends Employee{
    @Override
    public void work() {
        System.out.println("员工正在一线生产车间生产产品\n");
    }
}

模板方法:

public class TemplateMethod {

public static void main(String[] args) {
    SubTemplate subTemplate = new SubTemplate();
    subTemplate.spendTime();
}

}
abstract class Template{
    //计算某段代码执行所需要花费的时间
    public void spendTime(){
        long start = System.currentTimeMillis();
        code();//不确定的部分,易变的部分
        long end = System.currentTimeMillis();
        System.out.println("花费的时间为:"+(end-start));
    }
    public abstract void code();
}
class SubTemplate extends Template{
    @Override
    public void code() {
        for (int i = 2; i <=1000; i++) {
            boolean judge = true;
            for(int j=2;j<=Math.sqrt(i);j++){
                if(i%j==0){
                    judge = false;
                    break;
                }
            }
            if(judge){
                System.out.println(i);
            }
        }
    }
}

接口

一方面, 有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。

另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有is-a的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打印机、扫描仪、摄像头、充电器、MP3机、手机、数码相机、移动硬盘等都支持USB连接。

接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要...则必须能...”的思想。继承是一个"是不是"的关系,而接口实现则是"能不能"的关系。接口的本质是契约,标准,规范,就像我们的法律一样,制定好后大家都要遵守。

接口的使用:

  1. 接口使用interface来定义

  2. Java中,接口和类是并列的两个结构

  3. 如何定义接口:定义接口中的成员

  4. JDK7及以前:只能定义全局常量和抽象方法

  • 全局常量:public static final的,书写时,可以省略不写

  • 抽象方法:public abstract

    1. JDK8:除了定义全局变量和抽象方法之外,还可以定义静态方法、默认方法(略)

JDK8新特性:

知识点1:接口中定义的静态方法,只能通过接口来调用

知识点2:通过实现类的对象,可以调用接口中的默认方法。

如果实现类重写了接口的默认方法,调用时,仍然调用的是重写以后的方法

知识点3:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。 --->类优先原则

知识点4:如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,'那么在实现类没有重写此方法的情况下,报错。 --->接口冲突 解决方法:在实现类中重写此方法

  1. 接口不能定义构造器,意味着接口不可以实体化
  2. Java开发中,接口通过让类去实现**(implements)**的方式来使用.
  • 如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化
  • 如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类
  1. Java类可以实现多个接口 —> 弥补了Java单继承性的局限性
  • 格式: class AA extends BB implements CC,DD,EE
  1. 接口与接口之间可以实现多继承
  2. 接口的具体使用:体现多态化
  3. 接口:实际上可以看做是一种规范

面试题:

抽象类和接口的异同:

相同点:不能实体化;都可以包含抽象方法

不同点:1.把抽象类和接口(java7,java8,java9)的定义、内部结构解释说明

  1. 类:单继承性 接口:多继承性 类与接口:多实现

public class Test015 {
public static void main(String[] args) {
    System.out.println(Flyable.MAX_SPEED);
    System.out.println(Flyable.MIN_SPEED);
    Plane plane = new Plane();
    plane.fly();
}
    
}
interface Attackable{
    void attack();
}
interface Flyable{
    //全局变量
    public static final int MAX_SPEED = 7900;//第一宇宙速度
    //省略了public static final
    int MIN_SPEED = 1;

    //抽象方法
    public abstract void fly();
    //省略了public abstract
    void stop();
}
class Plane implements Flyable{
    @Override
    public void fly() {
        System.out.println("飞机起飞!");
    }

    @Override
    public void stop() {
        System.out.println("飞机降落!");
    }
}
abstract class Kite implements Flyable{

}
class Bullet implements Flyable,Attackable{
    @Override
    public void attack() {

    }

    @Override
    public void fly() {

    }

    @Override
    public void stop() {

    }
}
//***************************************************
interface AA extends Flyable{

}
interface BB extends AA{

}
interface CC extends AA,BB{

}

练习:

public class Test017 {

public static void main(String[] args) {
    ComparableCircle comparableCircle1 = new ComparableCircle(3.4);
    ComparableCircle comparableCircle2 = new ComparableCircle(3.6);
    int n = comparableCircle1.compareTo(comparableCircle2);
    if (n >0) {
        System.out.println("comparableCircle1对象大");
    }else if(n<0){
        System.out.println("comparableCircle2对象大");
    }else{
        System.out.println("对象一样大");
    }
}

}
interface CompareObject{
    //若返回值是0,代表相等;若为正数,代表当前对象大;负数代表当前对象小
    public int compareTo(Object object);
}
class Circle{
    private double redius;

    public double getRedius() {
        return redius;
    }

    public void setRedius(double redius) {
        this.redius = redius;
    }

    public Circle(double redius) {
        this.redius = redius;
    }
    public Circle() {
    }
}
class ComparableCircle extends Circle implements CompareObject{
    public ComparableCircle(double radius){
        super(radius);
    }
    @Override
    public int compareTo(Object object) {
        if(this == object){
            return 0;
        }
        if(object instanceof ComparableCircle){
            ComparableCircle comparableCircle = (ComparableCircle) object;
            if(this.getRedius()>comparableCircle.getRedius()){
                return 1;
            }else if(this.getRedius()<comparableCircle.getRedius()){
                return -1;
            }else{
                return 0;
            }
        }else{
            throw new RuntimeException("传入的数据类型不匹配!");
        }
    }
}

代理模式:

public class Test016 {

    public static void main(String[] args) {
        Server server = new Server();
        ProxyServer proxyServer = new ProxyServer(server);
        proxyServer.browse();
    }

}
interface Network{
    public void browse();
}
//被代理类
class Server implements Network{
    @Override
    public void browse() {
        System.out.println("访问网络");
    }
}
//代理类
class ProxyServer implements Network{
    private Network network;
    public ProxyServer(Network network){
        this.network = network;
    }
    public void check(){
        System.out.println("联网之前检查工作");
    }

    @Override
    public void browse() {
        check();
        network.browse();
    }
}

内部类

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

在Java中, 允许一一个类的定义位于另一个类的内部,前者称为内部类,后者 称为外部类。

Inner class-般用 在定义它的类或语句块之内,在外部引用它时必须给出完整的名称。 ➢Innerclass的名字不能与包含它的外部类类名相同;

分类:成员内部类(static成员内部类和非static成员内部类) **局部内部类(**不谈修饰符)、匿名内部类

类的内部成员之五:内部类

  1. Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类

  2. 内部类的分类:成员内部类 vs 局部内部类(方法类、代码块内、构造器类)

  3. 成员内部类:

    一方面,作为外部类的成员:

    • 调用外部类的结构
    • 可以被static修饰
    • 可以被四种不同的权限修饰

    另一方面,作为一个类:

    • 类内可以定义属性、方法、构造器、
    • 可以被final修饰,表示此类不能被继承
    • 可以被abstract修饰
  4. 关注:

    1. 如何实例化成员内部类的对象
    import Test07.Person.brain;
    public class Test018 {
    
      public static void main(String[] args) {
          Person person = new Person("小明",18);
          Person.lung.breathe();
          //创建静态内部类
          Person.brain brain = new Person.brain();
          brain.think();
          //创建非静态内部类
          Person.eyes eyes = person.new eyes();
          eyes.view();
      }
    
    }
    class Person{
      private String name;
      private int age;
      class heart {
          private double rate;//心脏跳动的频率
    
          public double getRate() {
              return rate;
          }
    
          public void setRate(double rate) {
              this.rate = rate;
          }
    
          public void jump(double rate){
              System.out.println("心脏以"+this.rate+"速率跳动");
          }
      }
      class lung{
          public static void breathe(){
              System.out.println("肺部可以自主呼吸为身体供氧");
          }
      }
      static class brain{
          public void think(){
              System.out.println("大脑会思考!");
          }
          public static void imagine(){
              System.out.println("大脑会幻想");
          }
      }
      class eyes{
          public void view(){
              System.out.println("眼睛可以看见外面的世界");
          }
      }
      public Person() {
    
      }
    
      public Person(String name, int age) {
          this.name = name;
          this.age = age;
      }
    
      public String getName() {
          return name;
      }
    
      public void setName(String name) {
          this.name = name;
      }
    
      public int getAge() {
          return age;
      }
    
      public void setAge(int age) {
          this.age = age;
      }
    }
    
    1. 如何在成员内部类中区分调用外部类的结构
    xxx //方法的形参
    this.xxx  //内部类的属性
    Xxx.this.xxx  //外部类的属性(Xxx为外部类名)
    
    1. 开发中,局部内部类的使用
    //返回一个实现了Comparable接口的类的对象
    public Comparable getcomparable( ){
      //创建一个实现了Comparable接口的类:局部内部类
      //方式一:
      class MyComparable implements Comparable{
      	@override
      	public int compareTo(object o) {
              return e;
      	}
      }
      return new MyComparable();
    }
      //方式二:
      return new Comparable(){
          @override
          public int compareTo(Object o){
              return 0;
          }
      };
    }
    
    1. 在局部内部类的方法中(比如:show)如果调用局部内部类所声明的方法(比如:method)中的局部变量(比如:num)时,要求此局部变量声明为final的

      public void method(){
          //局部变量
          int num = 10;//java8新特性(默认自动生成final),Java8以前这样写会报错(要求此局部变量显式的声明为final的 )
          class AA{
              public void show(){
      //           num = 20;
                  System.out.println(num);
              }
          }
      }
      

异常处理:

异常概述与异常

onism11690684938701599.png


Error:1. 栈溢出:java.lang.StackOverlowError

  1. 堆溢出:java.lang.OutOfMemoryError

Exception:

onism11690684959137128.png


异常的处理:

一、抓抛模型

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

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

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

public static void main(String[] args) {
    try {
        Student student = new Student();
        student.regist(-1001);
    } catch (Exception e) {
        System.out.println(e.getMessage());//打印异常信息
    }
}
class Student{
    private int id;
    public void regist(int id) throws Exception {
        if(id>0){
            this.id = id;
        }else{
            //手动抛出异常
            throw new Exception("输入数据无效!");
        }
    }
}

二、try - catch - finally的使用

try{

​ //可能出现异常的代码

}catch(异常类型1 变量名1){

​ //处理异常的方式1

}catch(异常类型2 变量名2){

​ //处理异常的方式2

}catch(异常类型3 变量名3){

​ //处理异常的方式3

}

……

finally{

​ //一定会执行的代码

}

说明:

  1. finally是可选的。
  2. 使用try将可能出现异常的代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配
  3. 一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理,一旦处理完成,就跳出当前的try - catch结构(在没有写finally的情况),继续执行其后的代码
  4. catch中的异常类型如果没有子父类关系,则无先后顺序关系。catch中的异常类型如果满足子父类关系,则要求子类一定要声明在父类上面,否则会报错
  5. 常用的异常对象处理的方式:① String getMessage()(说明异常出现的原因) ② printStackTrace()(打印输出异常出现位置)
  6. 在try结构中声明的变量,在出了try结构以后,就不能再被调用
  7. try - catch - finally结构可以相互嵌套

体会:

  1. 使用try - catch - finally处理编译时异常,是程序在编译时就不再报错,但是运行时仍可能报错。相当于我们使用try - catch - finally将一个编译时可能出现的异常,延迟到运行时出现
  2. 开发中,由于运行时异常比较常见,所以通常不针对运行时异常编译try - catch - finally了。针对于编译时异常,一定要考虑异常的处理。

try - catch - finally中finally的使用:

  1. finally是可选的
  2. finally中声明的是一定会被执行的代码,即使catch中又出现异常了,try中有return语句,catch中有return语句等情况
  3. 像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动回收的,我们需要手动的进行资源的释放。此时的资源释放,就需要声明在finally中

三、throws + 异常类型

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

  2. try-catch-finally :真正的将异常给处理掉了***(此处理治标不治本,并未从源头上解决异常,而是以另一种形式展示异常)***,throws的方式只是将异常抛给了方法的调用者。并没有真正将异常处理掉。

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

​ 3.1==如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果 子类重写的方法中有异常,必须使用try-catch-finally方式处理。== ​

​   3.2==执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。我们建议这几个方法使用throw的方式进行处理。而执行的方法a可以考虑使用try-catch-finally方式进行处理。==


自定义异常

如何自定义异常类?

  1. 继承于现有的异常结构:RuntimeException、Exception
  2. 提供一个全局变量:==serialVersionUID==
  3. 提供重载构造器
public static void main(String[] args) {
    try {
        Student student = new Student();
        student.regist(-1001);
    } catch (Exception e) {
        System.out.println(e.getMessage());
    }
}
class Student{
    private int id;
    public void regist(int id) throws MyException {
        if(id>0){
            this.id = id;
        }else{
            //手动抛出异常
            throw new MyException("输入数据无效!");
        }
    }
}
class MyException extends Exception{
    static final long serialVersionUID = -3390882787877454L;
    public MyException(){

    }
    public MyException(String message){
        super(message);
    }
}

总结:

onism11690684976306257.png