【JavaSE】08-面向对象(下)

八、面向对象(下)

8.1 关键字:static

8.1.1 static的含义

( 1 \mathbf{1} 1) static中文是静态的意思。可以用来修饰类的属性方法代码块内部类。(不可以修饰构造器)

8.1.2 static修饰属性:静态变量

( 1 \mathbf{1} 1) 属性,按是否使用static修饰,又分为:静态属性 vs 非静态属性(实例变量):

  • 实例变量:我们创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改。
  • 静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的。

( 2 \mathbf{2} 2) static修饰属性的其他说明:

  • 静态变量随着类的加载而加载。可以通过"类.静态变量"的方式进行调用。不用再造了对象才去调用

  • 例:

  • public class ObjectTest {
        @Test
        public void test1() {
            Chinese.nation = "China";
            Chinese c1 = new Chinese(26);
            Chinese c2 = new Chinese(29);
            System.out.println(c1.nation);
            System.out.println(c2.nation);
    
            c1.nation = "CHN";
            System.out.println(c1.nation);
            System.out.println(c2.nation);
        }
    }
    
    class Chinese {
        static String nation;//静态变量(类变量)
        int age;
    
        public Chinese() {
        }
    
        public Chinese(int age) {
            this.age = age;
        }
    }
    

    输出:

    China
    China
    CHN
    CHN
    
  • 静态变量的加载要早于对象的创建。

  • 由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中。

可调用吗?类变量实例变量
yesno
对象yesyes
  • 类变量和实例变量的内存解析

image-20220302102648786

8.1.3 static修饰方法:静态方法

( 1 \mathbf{1} 1) 随着类的加载而加载,可以通过"类.静态方法"的方式进行调用。

可调用吗?静态方法非静态方法
yesno
对象yesyes

( 2 \mathbf{2} 2) 调用的限制:

  • 静态方法中,只能调用静态的方法或属性。
  • 非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性。

( 3 \mathbf{3} 3) static注意点:

  • 在静态的方法内,不能使用this关键字和super关键字。

  • 关于静态属性和静态方法的使用,从生命周期的角度去理解。

  • 静态属性和静态方法是随着类的加载而产生,随着类的销毁而消亡。贯穿在多个对象。

( 4 \mathbf{4} 4) 开发中,如何确定一个属性是否要声明为static的?

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

( 5 \mathbf{5} 5) 开发中,如何确定一个方法是否要声明为static的?

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

8.1.4 static应用

( 1 \mathbf{1} 1) 自动赋值ID
  • 在新建对象的时候自动依次赋值ID。

例:

① Chinese类

class Chinese {
    static String nation;
    int age;
    private int id;
    private static int init = 1001;

    public Chinese() {
        id = init++;
    }

    public Chinese(int age) {
        this();
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

② 测试

public class ObjectTest {
    @Test
    public void test1() {
       
        Chinese c1 = new Chinese(26);
        System.out.println("c1's ID: " + c1.getId());
        Chinese c2 = new Chinese(29);
        System.out.println("c1's ID: " + c2.getId());
    }
}

③ 输出:

c1's ID: 1001
c1's ID: 1002

( 2 \mathbf{2} 2) 题目2

  • 编写一个类实现银行账户的概念,包含的属性有“帐号”、“密码”、“存款余额”、“利率”、“最小余额”,定义封装这些属性的方法。账号要自动生成。

  • 编写main类,使用银行账户类,输入、输出3个储户的上述信息。(考虑:哪些属性可以设计成static属性。)

8.1.5 单例设计模式

( 1 \mathbf{1} 1) 设计模式的概念

所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例

image-20220302141444702

( 2 \mathbf{2} 2) 单例模式1:饿汉式

//饿汉式
class Bank{
    //1.私有化构造器,防止在类外部新建对象
    private Bank(){
    }
    //2.在类内部新建静态对象,并私有化
    private static Bank instance = new Bank();

    //3.提供public方法供外部调用,返回这个对象
    public Bank getInstance(){
        return instance;
    }
}

( 3 \mathbf{3} 3) 单例模式2:懒汉式

//懒汉式
class Person {
    //1.私有化构造器,防止在类外部新建对象
    private Person() {
    }

    //2.在类内部声明静态对象,并私有化,但没有初始化
    private static Person instance = null;

    //3.提供public的静态方法供外部调用,返回这个对象
    public static Person getInstance() {
        if (instance == null) {
            instance = new Person();
        }
        return instance;
    }
}

( 4 \mathbf{4} 4) 饿汉式和懒汉式对比

饿汉式懒汉式
好处:饿汉式是线程安全的好处:延迟对象的创建。
坏处:对象加载时间过长目前的写法坏处:线程不安全。—>到多线程内容时,再修改

( 5 \mathbf{5} 5) 单例模式的优点
由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可
以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方
式来解决。

( 6 \mathbf{6} 6) 单例模式的应用场景

image-20220302151105676

8.2 main方法的理解

public static void main(String[] args) {

}

( 1 \mathbf{1} 1) main()方法作为程序的入口。

( 2 \mathbf{2} 2) main()方法也是一个普通的静态。

( 3 \mathbf{3} 3) main()方法可以作为我们与控制台交互的方式。(之前:使用Scanner)。

8.3 类成员四:代码块

8.3.1 代码块的含义

( 1 \mathbf{1} 1) 代码块是声明在类中,以大括号框住的一段代码。用于进行初始化,分为静态代码块和非静态代码块。

( 2 \mathbf{2} 2) 代码块如果有修饰的话,只能使用static.

class Person{

static{
	静态代码块语句;
}

{
	非静态代码块语句;
}

}

8.3.2 静态代码块

  • 内部可以有输出语句。
  • 随着类的加载而执行,而且只执行一次。
  • 作用:初始化类的信息。
  • 如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行。
  • 静态代码块的执行要优先于非静态代码块的执行。
  • 静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构。

8.3.3 非静态代码块

  • 内部可以有输出语句。

  • 随着对象的创建而执行。

  • 每创建一个对象,就执行一次非静态代码块。

  • 作用:可以在创建对象时,对对象的属性等进行初始化。

  • 如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行。

  • 非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法。

8.3.4 执行顺序总结

  • 由父及子,静态先行。

例:

class Father {
  static {
  	System.out.println("11111111111");
  }
  {
  	System.out.println("22222222222");
  }

  public Father() {
  	System.out.println("33333333333");

  }

}

public class Son extends Father {
  static {
  	System.out.println("44444444444");
  }
  {
  	System.out.println("55555555555");
  }
  public Son() {
  	System.out.println("66666666666");
  }


  public static void main(String[] args) { // 由父及子 静态先行
  	System.out.println("77777777777");
  	System.out.println("************************");
  	new Son();
  	System.out.println("************************");
  	new Son();
  	System.out.println("************************");
  	new Father();
  }

}

输出:

11111111111
44444444444
77777777777
************************
22222222222
33333333333
55555555555
66666666666
************************
22222222222
33333333333
55555555555
66666666666
************************
22222222222
33333333333
  • 对属性可以赋值的位置:

    ①默认初始化

    ②显式初始化/⑤在代码块中赋值

    ③构造器中初始化

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

  • 执行的先后顺序:① - ② / ⑤ - ③ - ④

  • ② 和⑤是谁先写谁执行。

8.4 关键字:final

8.4.1 final的含义

  • final可以修饰的结构:方法变量
  • 当final修饰类时:最终子类,不能再向下延伸子类,不能被继承。
  • 当final修饰方法时:表明方法不能被重写。
  • 当final修饰变量时:表面变量不能被修改。即常量。
    • final修饰属性时,可以赋值的位置有:显式赋值、在代码块中赋值、构造器中赋值。
    • final修饰局部变量时,是常量,用全大写。也可以用在形参。
  • static final 可以修饰属性:全局常量。
  • static final 可以修饰方法。

8.5 抽象类与抽象方法

8.5.1 抽象的含义

  • 希望增强父类的通用性,还未创建子类的时候,不知道父类的一些方法该怎么写。于是出现了抽象类模糊化,使父类的方法不必写得太具体,增强了父类的通用性。
  • 抽象类可以继承非抽象类。
  • 关键字:abstract。可以修饰:类和方法。
  • abstract 不可以修饰:
    • 不可以修饰属性、构造器等结构。
    • 不可以修饰私有方法、静态方法、final方法和final类。

8.5.2 抽象类的特点

  • 一旦类被声明为抽象类,则该类不能被实例化(不能创建对象)。具体的执行需要子类实现。

  • 抽象类的声明:用关键字——abstract

    abstract class 类名{
    
    }
    
  • 抽象类中,同样有属性、构造器和方法。以供子类继承。可以这样理解:抽象类就是比普通类多定义了抽象方法,除了不能直接进行实例化操作之外,并没有任何不同。

  • 包含抽象方法的类,一定是抽象类。

8.5.3 抽象方法的特点

  • 抽象方法的声明:

    public abstract void method();
    
  • 抽象方法必须声明在抽象类中。

  • 抽象方法没有方法体。不写大括号。

  • 继承了抽象类的子类,必须重写所有父类(包括间接父类)中的抽象方法,才能创建实例对象。否则,这个子类依然是抽象类,要用 abstract 修饰。

8.5.4 abstract使用注意点

  • abstract 不能修饰属性、构造器和代码块。
  • abstract 不能修饰以下:
    • 私有方法:因为私有方法不能被类以外的子类调用,因此没办法被重写。一直不能被重写就一直无法创建实例对象。
    • 静态方法:因为方法前用static修饰,不认为是重写。
    • final方法:final修饰的方法不能被重写。
    • final类:因为final类不能被继承。

8.5.5 创建抽象类的匿名子类对象

  • 跟第六章的匿名对象一样,当我们只想用一次抽象类的子类对象去传形参时,我们可以创建抽象类的匿名子类对象。

  • 创建匿名子类的对象:

    /*其中,Person是抽象类,不能实例化。
     *下面演示创建抽象类的匿名子类对象
     */
    Person p = new Person(){
        @Override
    	public void eat() {
    		System.out.println("吃东西");
    	}
    
    	@Override
    	public void breath() {
    		System.out.println("好好呼吸");
    	}
    };
    
  • 更懒一点,甚至可以直接创建匿名子类的匿名对象:

    //其中,method1(Object obj)方法的形参必须传入对象
    method1(new Person(){
    	@Override
    	public void eat() {
    		System.out.println("吃好吃东西");
    	}
    
    	@Override
    	public void breath() {
    		System.out.println("好好呼吸新鲜空气");
    	}
    });
    

8.5.6 模板方法设计模式

image-20220303104607987

8.6 接口(interface)

8.6.1 接口的含义

  • 解决了类不能被多继承的缺陷,接口可以得到多重继承的效果。
  • 实现接口有点像继承父类,就能拥有接口中声明的方法了。
  • Java中,一个类可以实现多个接口。但父类只能有一个。
  • 抽象类可以实现 (implements) 接口。
  • Java中,接口和类是并列的结构。
  • 用关键字:interface 来声明。

8.6.2 接口的成员

  • 接口声明格式:

    interface Flyable {
    
        //全局常量
        public static final int MAX_SPEED = 7900;//第一宇宙速度
        int MIN_SPEED = 1;//前面修饰符可省略,自动生效
        
        //抽象方法
        public abstract void fly();
        void stop();//同可省略
        
    }
    
  • JDK7及以前:只能定义全局常量和抽象方法。

    • 全局常量:public static final 的。但是书写时可省略,但还是生效的。
    • 抽象方法:public abstract 的。
  • JDK8及以后:除了以上两种,新增了静态方法默认方法

  • 接口中,不能定义构造器。意味着,接口不可以实例化。

  • Java开发中,接口通过让类去实现 (implement) 的方式来使用。

    • 如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化。
    • 否则,此实现类仍为抽象类。
    class Plane implement Flyable{
        @Override
        public void fly(){
            
        }
        
        @Override
        public void stop(){
            
        }
    }
    

8.6.3 接口的多实现与接口的继承性

  • Java类可以实现多接口,可以弥补单继承性的缺陷。

  • 多实现格式:

    class Bullet extends Object implements Flyable, Attackale {
        @Override
        public void attack(){
            
        }
        
        @Override
        public void fly(){
            
        }
        
        @Override
        public void stop(){
            
        }
    }
    
    • 其中,Bullet是子类,Object是父类。继承 extends 应该写在实现 implements 前面。多个接口名用逗号分隔。
    • 若想让此类实例化,则必须重写接口中的所有抽象方法。
  • 接口与接口之间可以继承,且可以多继承

  • 接口多继承格式:

    interface AA {
        void method1();//省略了前面3个修饰符
    }
    
    interface BB {
        void method2();
    }
    
    interface CC extends AA, BB {
        自动继承了AA和BB的所有抽象方法;
    }
    
  • 接口的具体使用体现多态性。

  • 接口的本质是契约、标准、规范。

8.6.4 匿名接口的创建

    1. 创建了接口的非匿名实现类的非匿名对象:
    Flash flash = new Flash();
    
    1. 创建了接口的非匿名实现类的匿名对象:
    com.transferData(new Printer());
    
    1. 创建了接口的匿名实现类的非匿名对象:
    USB phone = new USB() {
    
    	@Override
    	public void start() {
    		System.out.println("手机开始工作");
    	}
    
    	@Override
    	public void stop() {
    		System.out.println("手机结束工作");
    	}		
    }
    
    1. 创建了接口的匿名实现类的匿名对象:
    com.transferData(new USB() {
    	@Override
    	public void start() {
    		System.out.println("mp3开始工作");
    	}
    
    	@Override
    	public void stop() {
    		System.out.println("mp3结束工作");
    	}
    }
    

8.6.5 接口的设计模式:代理模式

image-20220305090502059

image-20220305091222380

8.6.6 接口练习

  • 题目1

    interface A {
    	int x = 0;
    }
    
    class B {
    	int x = 1;
    }
    
    class C extends B implements A {
    	public void pX() {
    		//编译不通过。因为x是不明确的
    		// System.out.println(x);
    		System.out.println(super.x);//1
    		System.out.println(A.x);//0
    		
    	}
    
    	public static void main(String[] args) {
    		new C().pX();
    	}
    }
    

    如果在接口和父类重名的情况下,想调用接口的x,直接A.x即可。因为接口中的变量是全局常量。如果想调用父类中的x,则直接super.x即可。

8.6.7 Java8中接口的新特性

  • 接口中定义的静态方法,只能通过接口调用。

  • 接口中定义的默认方法,通过接口的实现类对象调用。

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

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

  • 如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么在实现类没有重写此方法的情况下,报错。–>接口冲突

例子:

public interface CompareA {
    //静态方法:只能通过接口来调用
    public static void method1() {
        System.out.println("CompareA: 北京");
    }
    //默认方法: 实现类的对象中才能用
    public default void method2() {
        System.out.println("CompareA: 上海");
    }
}
public class SubClassTest {
    public static void main(String[] args) {
        SubClass s = new SubClass();
//        s.method1(); //报错,不能通过实现类调用接口的静态方法
        CompareA.method1();//通过接口来调用静态方法
        s.method2();//实现类的对象中调用默认方法
    }
}

class SubClass implements CompareA {

}

输出:

CompareA: 北京
CompareA: 上海
  • 如何在子类(或实现类)的方法中调用父类、接口中被重写的方法

    public void myMethod(){
    	method3();//调用自己定义的重写的方法
    	super.method3();//调用的是父类中声明的
    	//调用接口中的默认方法
    	CompareA.super.method3();
    	CompareB.super.method3();
    }
    

8.7 类成员五:内部类

8.7.1 内部类的含义

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

  • 内部类的分类:成员内部类(静态、非静态) vs 局部内部类(方法内、代码块内、构造器内)。

  • 在外部定义的一些类,如果在外面再也用不着,就可以考虑定义为内部类。

  • 成员内部类:

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

    • 调用外部类的结构:Person.this.eat();

    • 可以被static修饰

    • 可以被4种不同的权限修饰

    • 另一方面,作为一个

    • 类内可以定义属性、方法、构造器等

    • 可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承

    • 可以被abstract修饰

  • 4.关注如下的3个问题

    • 4.1 如何实例化成员内部类的对象

      //创建static的Dog实例(静态的成员内部类)
      //其中,Person是外部类,Dog是static的内部类
      Person.Dog dog = new Person.Dog();
      
      //创建非静态Bird实例(非静态的成员内部类)
      //其中,Person是外部类,Bird是非static的内部类
      Person p = new Person();
      Person.Brid bird = new p.new Bird();
      
    • 4.2 如何在成员内部类中区分调用外部类的结构

    • 例:在外部类属性名、内部类属性名和内部类方法形参名同名时,如何区分这3个结构:

      class Person {
          String name = "小明";
          //非静态成员内部类
          class Bird {
              String name = "杜鹃";
              
              public void display(String name) {
                  System.out.println(name);//该方法形参
                  System.out.println(this.name);//内部类属性"杜鹃"
                  System.out.println(Person.this.name);//外部类属性"小明"
              }
          }
      }
      
    • 4.3 开发中局部内部类的使用。

      例:

      //外部类Person
      class Person {
          //在方法中的局部内部类
          public void method() {
              class AA {
                  
              }
          }
          
          //在代码块中的局部内部类
          {
              class BB {
                  
              }
          }
          
          //在构造器中的局部内部类
          public Person() {
              class CC {
                  
              }
          }
      }
      

8.7.2 注意点

  • 在局部内部类的方法中(比如下面的show),如果调用外部类所声明的方法(比如下面的method)中的局部变量(比如num),则要求此局部变量必须声明为 final 的。

  • 在 JDK8 以后,如果忘记写 final ,编译器默认会为你省略,但 final 依然生效。

    //外部类InnerClassTest
    class InnerClassTest {
        
        //外部类的方法method()
        public void method() {
            //外部类的方法中的局部变量num
            int num = 10;
            //final int num = 10;//JDK7及以前必须声明final
            
            //局部内部类AA
            class AA {
                
                //局部内部类AA的方法show()
                public void show() {
                    //num = 20;//报错,final变量不能改
                    System.out.println(num);
                }
            }
        }
    }
    

8.7.3 总结

  • 成员内部类和局部内部类在编译以后,都会生成字节码文件。

    格式:

    成员内部类:外部类$内部类名.class
    局部内部类:外部类$数字 内部类名.class
    
面向对象编程是一种编程范式,它将程序的构建和设计思路以面向对象的方式进行组织和实现。在Java中,面向对象编程是基于Java SE(Standard Edition)的一种编程方式。第07讲主要介绍了面向对象编程中的一些基本概念和关键术语。 在面向对象编程中,我们将程序中的数据和对数据的操作(方法)封装在一起,形成一个对象。对象由两部分构成:属性和方法。属性是用来描述对象的特征,而方法则是对象可以执行的操作。对象之间通过消息(方法调用)进行通信和交互。面向对象的核心思想是通过封装、继承和多态实现程序的复用和扩展。 封装是面向对象编程中的一个重要概念,它指的是将类的属性和方法进行封装,使得外部无法直接访问和修改对象的内部状态,只能通过公共的方法来操作属性和执行方法。封装提供了一种将数据和行为组合在一起的方式,可以保护数据的完整性和安全性。 继承是面向对象编程中的另一个重要概念,它指的是通过定义一个新的类来继承现有类的属性和方法。通过继承,子类可以继承父类的属性和方法,并可以在此基础上进行扩展和修改。继承提供了一种代码复用的机制,可以减少重复编码的工作量。 多态是面向对象编程的又一个重要概念,它指的是同一类型的对象在不同的情况下可以有不同的表现形式。多态通过方法的重写和方法的重载实现。方法的重写指的是在子类中重新定义和实现父类的方法,方法的重载指的是在同一个类中可以定义多个同名但参数列表不同的方法。 总结来说,面向对象编程是一种将程序组织和设计思路以对象为中心的编程方式。在JavaSE中,我们可以通过封装、继承和多态来实现面向对象编程的目标。封装可以提高程序的可维护性和可复用性,继承可以减少重复编码的工作量,多态可以灵活地操作对象。掌握这些基本概念和关键术语,可以帮助我们更好地理解和应用面向对象编程的思想。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卡皮巴拉不躺平

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值