栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【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
  • 类变量和实例变量的内存解析

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) 设计模式的概念

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

( 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) 单例模式的应用场景

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 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 模板方法设计模式

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 接口的设计模式:代理模式

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
    
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/885782.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号