JAVA课堂随记5面向对象

一、什么是面向对象

  1. 面向过程思想
    • 步骤清晰简单,第一步做什么,第二步做什么…
    • 面对过程适合处理一些较为简单的问题
  2. 面向对象思想
    • 物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索。
    • 面向对象适合处理复杂的问题,适合处理需要多人协作的问题!
  3. 对于描述复杂的事物,为了从宏观上把握,从整体上合理分析,我们需要使用面向对象的思路分析整个系统。但是具体到微观操作,仍然需要面向过程的思路去处理。
  4. 面向对象编程(Object-Oriented Programming,OOP)
  5. 面向对象的本质:以类的方式组织代码,以对象的形式组织(封装)数据。
  6. 三大特性:
    • 封装:把对象的属性和行为封装在一起,形成一个不可分割的实体,只保留有限的对外接口。在编程上,将属性与方法定义在一个类中,并对来自类外部的访问进行控制。
    • 继承:子类继承父类的属性和行为,同时具有自己独特的属性与行为。
    • 多态:一种多态是一个类中有多个操作具有相同的名字,但这些操作所接收的输入参数各不相同,称为方法重载。另一种多态与继承有关,指父类的方法被不同的子类继承后表现出不同的行为。
  7. 从认识论角度考虑是先有对象后有类。对象,是具体的事物,占内存。类,是抽象的,是对对象的抽象,不占内存。
  8. 从代码运行角度考虑是先有类后有对象。类是对象的模板。

二、类与对象的创建

  1. 类与抽象的关系

    • 类是一种抽象的数据类型,它是对某一类事物整体描述/定义,但是并不能代表某一个具体的事物

      • 动物、植物、手机、电脑…
      • Person类、Pet类、Car类等,这些类都是用来描述/定义某一类具体的事物应该具备的特点和行为
    • 对象是抽象概念的具体实例

      • 张三就是人的一个具体实例,张三家里的旺财就是狗的一个具体实例。
      • 能够体现出特点,展现出功能的是具体的实例,而不是一个抽象的概念。
  2. 创建与初始化对象

    • 使用new关键字创建对象。
    • 使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象,进行默认的初始化以及对类中构造器的调用
    • new操作符创建对象完成了三件事:
      • 为对象分配存储空间
      • 调用构造方法
      • 返回对象的引用
    • 类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的。并且构造器有以下两个特点:
      • 必须和类的名字相同
      • 必须没有返回类型,也不能写void
    public class Demo05 {
        public static void main(String[] args) {
            //类:抽象的,需要实例化
            //类实例化后会返回一个自己的对象!stu1对象就是一个Stu类的具体实例
            Stu stu1 = new Stu();
            Stu stu2 = new Stu();
            stu1.name = "小明";
            stu1.age = 3;
            System.out.println(stu1.name);
            System.out.println(stu1.age);
            stu2.name = "小红";
            stu2.age = 4;
            System.out.println(stu2.name);
            System.out.println(stu2.age);
        }
    }
    class Stu{
        //属性:字段
        String name;
        int age;
        //方法
        public void study(){
            System.out.println(this.name);
        }
    }
    /*
    结果:
        小明
        3
        小红
        4
    */
    
    public class Demo06 {
        public static void main(String[] args) {
            //new   实例化了一个对象
            Per per = new Per("zanbimo");
            System.out.println(per.name);
        }
    }
    class Per{
        //一个类即使什么都不写,它也会存在一个方法,即构造方法
        String name;
        public Per(){
    
        }
        //有参构造:一旦定义了有参构造,无参就必须显示定义
        public Per(String name){//方法重载
            this.name=name;
        }
    }
    /*
    结果:
    	zanbimo
    */
    
    public class Demo07 {
        public static void main(String[] args) {
            Pet dog = new Pet();
            dog.name = "旺财";
            dog.age = 3;
            dog.shout();
            System.out.println(dog.name);
            System.out.println(dog.age);
        }
    }
    class Pet{
        public String name;
        public int age;
        public void shout(){
            System.out.println("叫了一声");
        }
    }
    /*
    结果:
        叫了一声
        旺财
        3
    */
    

三、封装

  1. 该露的露,该藏的藏

    我们程序设计要追求“高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合就是仅暴露少量的方法给外部使用。

  2. 封装(数据的隐藏)

    通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏。

  3. 记住这句话就够了:属性私有,get/set

    /*
    * 1.提高程序的安全性,保护数据
    * 2.隐藏代码的实现细节
    * 3.统一接口
    * 4.提高系统可维护性
    * */
    public class Demo08 {
        public static void main(String[] args) {
            Sd s1 = new Sd();
            s1.setName("赞比么");
            s1.setAge(990);//不合法
            System.out.println("年龄:"+s1.getAge());
        }
    }
    class Sd{
        //属性私有
        private String name;//名字
        private int id;//学号
        private char sex;//性别
        private int age;//年龄
        //提供一些可以操作这个属性的方法
        //即提供一些public的get、set方法
        //get   获得这个数据
        public String getName(){
            return this.name;
        }
        //set   给这个数据设置值
        public void setName(String name){
            this.name = name;
        }
        //alt+insert自动生成get、set方法
    
        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>0 && age<150){
                this.age = age;
            }else {
                this.age = -1;
                System.out.println("年龄不合法,0~150");
            }
    
        }
    }
    

四、继承

  1. 继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模。(类太多,要抽象)

    class  子类名  extends  父类名 { 
    	...
    } 
    
  2. extends的意思是“扩展”。子类是父类的扩展。

  3. JAVA中类只有单继承,没有多继承!(一个儿子只能有一个爸爸,但是一个爸爸可以有多个儿子)

  4. 继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等。

  5. 继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示。子类可以“继承父类的实例变量和实例方法”,但是子类“不继承父类的构造方法”

  6. 子类和父类之间,从意义上讲应该具有“is a”的关系。

  7. object类:所有的类,都默认直接或间接继承Object

    //在JAVA中,所有的类,都默认直接或间接继承Object
    public class Demo09 {
        public static void main(String[] args) {
            St st = new St();
            st.say();
            st.setMoney(500);
            System.out.println(st.getMoney());
        }
    }
    class Pe{//父类
        private int money = 10_0000;
    
        public int getMoney() {
            return money;
        }
    
        public void setMoney(int money) {
            this.money = money;
        }
    
        public void say(){
            System.out.println("说了一加一");
        }
    }
    class St extends Pe{//子类
    
    }
    /*
    结果:
        说了一加一
        500
    */
    
  8. super

    • super关键字表示当前对象的父类,有两种用法:

      • 在“子类的构造方法”中,使用super关键字指定调用父类的某个构造方法;
      • 在“子类”中,通过super关键字访问父类的成员变量和成员方法。
    • super调用父类的构造方法,由参数列表确定调用父类哪个构造方法,必须在构造方法中的第一个语句。这种super语句只起到占位符的作用,用于确定调用父类的哪个构造方法,而不是先执行super语句然后再执行子类构造方法的意思

    • super必须只能出现在子类的方法或者构造方法中

    • spuer和this不能同时调用构造方法(二者都必须在构造方法中的第一个语句)

    • VS this:

      • 代表的对象不同:
        • this:本身调用者这个对象
        • super:代表父类对象的引用
      • 前提:
        • this:没有继承也可以使用
        • super:只能在继承条件下才能使用
      • 构造方法:
        • this();本类的构造
        • super();父类的构造
      • 使用范围:
        • this:只能用于实例方法和构造方法中,不能用于类方法中
        • super:只能用于实例方法和构造方法中,不能用于类方法中
    public class Demo10 {
        public static void main(String[] args) {
            Std std = new Std();
            std.test("hh");
        }
    }
    class Pes{//父类
        public Pes() {
            System.out.println("Pes无参构造执行了");
        }
    
        protected String name = "zan";
        //私有的东西无法被继承
        public void print(){
            System.out.println("Pes");
        }
    }
    class Std extends Pes{//子类
        public Std() {
            //super();//隐藏代码:调用了父类的无参构造,并须在子类构造器的第一行
            this("有参构造");
            System.out.println("Std无参构造执行了");
        }
    
        public Std(String name) {
            this.name = name;
        }
    
        private String name = "zanbimo";
        public void test(String name){
            System.out.println(name);//hh
            System.out.println(this.name);//有参构造
            System.out.println(super.name);//zan
            this.print();//Std
            super.print();//Pes,子类调父类
        }
        public void print(){
            System.out.println("Std");
        }
    }
    /*
    结果:
        Pes无参构造执行了
        Std无参构造执行了
        hh
        有参构造
        zan
        Std
        Pes
    */
    
  9. 方法重写:

    • 需要有继承关系,子类重写父类的方法!
      • 方法名必须相同
      • 参数列表必须相同
    • 修饰符:范围可以扩大:public>protected>default>private
    • 抛出异常:范围可以被缩小,但不能扩大:ClassNotFoundException–>Exception(大)
    • 子类的方法和父类必须要一致,方法体不同
    • 为什么需要重写:
      • 父类的功能,子类不一定需要,或者不一定满足
      • Alt+Insert:override
    /*
    对于上转型,需要注意以下几点:
    	把子类对象作为实参传给父类的形参,这也是上转型。
     	若存在方法重写,则访问上转型对象时,上转型对象原来是哪个类的对象,则访问的就是哪个类重写的方法。
    	父类不能被子类继承的属性和方法,则上转型对象不能访问。
    */
    public class Demo11 {
        public void f(B b){
            b.test();
        }
        //静态方法:方法的调用只和左边,定义的数据类型有关
        //非静态方法:重写
        public static void main(String[] args) {
            //方法的调用只和左边即定义的数据类型有关
            A a = new A();
            a.test();//A
            //子类对象的上转型,父类的引用指向了子类
            //子类对象上转型后,不能再使用子类新增的属性和方法,只能使用从父类继承的属性和方法
            B b = new A();
            b.test();//B
            System.out.println("========");
            Demo11 demo11 = new Demo11();
            demo11.f(new A());//上转型:把子类对象作为实参传给父类的形参
        }
    }
    //重写都是方法的重写,和属性无关
    class B{
        public void test(){//static
            System.out.println("B=>test()");
        }
    }
    class A extends B{
        @Override//注解:有功能的注释,重写
        /*
        子类对象上转型后,不能再使用子类新增的属性和方法,只能使用从父类继承的属性和方法。
        对于父类被子类重写的方法,实际访问的是子类的方法,但访问权限还是按照父类方法的访问权限。
         */
        public void test() {//static
            System.out.println("A=>test()");
     }
    }
    /*
     结果1:
         A=>test()
         A=>test()
         ========
    	 A=>test()
     结果2:
         A=>test()
         B=>test()
         ========
    	 B=>test()
    */
    

五、多态

  1. 多态是指在程序中相同的名称表现出不同的行为,包括静态多态和动态多态两种。

    • 静态多态一般指方法重载
    • 动态多态也称为运行时多态,实现条件:
      • 实现继承,或实现接口
      • 子类重写父类方法,或实现类实现接口的方法
      • 执行上转型,通过上转型对象访问父类被重写的方法,或接口被实现的方法(父类引用指向子类对象)。
  2. 多态即同一方法可以根据发送对象的不同而采用多种不同的行为方式

  3. 一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多(父类,有关系的类)

  4. 注意:

    • 多态是方法的多态,属性没有多态性。
    • 父类和子类有联系,存在类型转换异常!ClassCastException
    • 无法重写的情况:
      • static方法,属于类,不属于实例
      • final方法,常量
      • private方法,私有
    public class Demo12 {
        public static void main(String[] args) {
            //new了一个对象,一个对象的实际类型是确定的
            //new Student();
            //可以指向的引用类型就不确定了:父类的引用指向子类
            Stud s1 = new Stud();
            Pers s2 = new Stud();
            Object s3 = new Stud();
            s1.run();
            s2.run();//子类对象上转型后,子类重写了父类的方法,执行子类的方法
            //对象能执行哪些方法,主要看对象左边的类型,和右边关系不大
            s1.eat();
            //s2.eat();//非法的,上转型对象只能访问从父类继承的成员变量或方法
        }
    }
    class Pers{
        public void run(){
            System.out.println("run");
        }
    }
    class Stud extends Pers{
        @Override
        public void run() {
            System.out.println("son");
        }
        public void eat(){
            System.out.println("eat");
        }
    }
    /*
    结果:
        son
        son
    */
    
  5. instanceof 运算符:左操作数是对象,右操作数是类型。当左操作数的对象属于右操作数的类型时,结果是true,否则是false。判断一个对象是什么类型。

    public class Demo13 {
        public static void main(String[] args) {
            //Object>String
            //Object>P>T
            //Object>P>S
            //System.out.println(X instanceof Y);
            //能不能编译通过?看X和Y之间是否存在父子关系,存在则通过,反之相反
            //结果是true还是false?看X所指向的实际类型是否是Y的子类型
            Object object = new S();
            System.out.println(object instanceof S);//true
            System.out.println(object instanceof P);//true
            System.out.println(object instanceof Object);//true
            System.out.println(object instanceof T);//false
            System.out.println(object instanceof String);//false
            System.out.println("===============");
            P p = new S();
            System.out.println(p instanceof S);//true
            System.out.println(p instanceof P);//true
            System.out.println(p instanceof Object);//true
            System.out.println(p instanceof T);//false
            //System.out.println(p instanceof String);//编译出错
            System.out.println("===============");
            S s = new S();
            System.out.println(s instanceof S);//true
            System.out.println(s instanceof P);//true
            System.out.println(s instanceof Object);//true
            //System.out.println(s instanceof T);//编译出错
            //System.out.println(s instanceof String);//编译出错
        }
    }
    class P{
    
    }
    class S extends P{
    
    }
    class T extends P{
    
    }
    /*
    结果:
        true
        true
        true
        false
        false
        ===============
        true
        true
        true
        false
        ===============
        true
        true
        true
    */
    
    /*
    * 下转型需要强制转换,分两种情况:
    * (1) 把一个父类对象强制转换为子类对象,这是不允许的,尽管编译能通过,但不能运行;
    * (2) 先将子类对象上转型,然后再下转型,这是允许的,上转型对象通过下转型又转换回原来的子类对象,可以使用子类的全部属性和方法。
    * */
    public class Demo14 {
        public static void main(String[] args) {
            //类型之间的转化:父类:高 子类:低
            PerS p = new StuD();//上转型:把子类对象赋值给父类变量
            ((StuD) p).go();//go
            //p.go();报错
            //将p这个对象转换为StuD类型,我们就可以使用StuD类型的方法了
            StuD s = (StuD) p;//下转型:把父类对象赋值给子类变量,强制转换
            s.go();//go
        }
    }
    class PerS{
        public void run(){
            System.out.println("run");
        }
    }
    class StuD extends PerS{
        public void go(){
            System.out.println("go");
        }
    }
    /*
    结果:
        go
        go
    */
    

六、static详解

import static java.lang.Math.random;
public class Demo15 {
    private static int age;//静态变量
    private double score;//非静态变量
    public static void go(){//静态方法
        System.out.println("go");
    }
    public void run(){//非静态方法
        System.out.println("run");
    }
    public static void main(String[] args) {
        Demo15 demo15 = new Demo15();
        System.out.println(demo15.age);
        System.out.println(Demo15.age);
        new Demo15().run();
        Demo15.go();
        go();
        System.out.println("=========");
        Dmk dmk1 = new Dmk();
        System.out.println("=========");
        Dmk dmk2 = new Dmk();
        System.out.println("=========");
        System.out.println(Math.random());
        //因为import static java.lang.Math.random;
        System.out.println(random());

    }
}
final class Dmk{
    //2:赋初值
    {
        System.out.println("匿名代码块");//代码块(匿名代码块)
    }
    //1:只执行一次
    static {
        System.out.println("静态代码块");//静态代码块
    }
    //3
    public Dmk() {
        System.out.println("构造方法");
    }
}
//class Test extends Dmk{}//final类不能被继承,即不能有子类

/*
结果:
    0
    0
    run
    go
    go
    =========
    静态代码块
    匿名代码块
    构造方法
    =========
    匿名代码块
    构造方法
    =========
    0.26228394791145926
    0.1986239214077118
*/

七、抽象类

  1. abstract修饰符可以用来修饰方法也可以修饰类;如果修饰方法,那么该方法就是抽象方法;如果修饰类,那么该类就是抽象类。

    abstract class A{
    	...
    }
    
  2. 抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类。

  3. 抽象类:不能使用new关键字来创建对象(即不能用来创建实例),它是用来让子类继承的。不允许使用final和abstract同时修饰一个类。

  4. 抽象方法:只有方法的声明,没有方法的实现,它是用来让子类实现的。同样不允许使用final和abstract同时修饰一个方法。

  5. 子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类。

    public class Demo16 {
        public static void main(String[] args) {
            //new Action();抽象类不能被实例化,只能靠子类去实现它
            Cx cx = new Cx();
            cx.doSomething();
            cx.go();
        }
    }
    //abstract 抽象类
    abstract class Action{
        //约束~有人帮我们实现
        //abstract,抽象方法,只有方法名字,没有方法实现
        public abstract void doSomething();//抽象方法,没有方法体
        public void go(){// //非抽象方法,有方法体
            System.out.println("go");
        }
        //abstract public Action() {}//构造方法不可以是抽象方法
    }
    //抽象类的所有方法,继承了它的子类,都必须要实现它的方法,除非它的子类也是抽象的
    class Cx extends Action{
        @Override
        public void doSomething() {
            System.out.println("do");
        }
    }
    abstract class Zl extends Action{
    
    }
    /*
    结果:
        do
        go
    */
    

八、接口的定义与实现

  1. 接口的定义

    • 用关键字interface来声明一个接口。接口中只能有两样东西:常量抽象方法

      [public] interface 接口名 {    
      	//常量声明,公有的静态常量
      	[public] [static] [final] 变量类型 变量名 =;
          ...
          //抽象方法声明,公有的抽象方法
          [public] [abstract] 返回类型 方法名(参数列表); 
          ...
      }
      
    • 普通类:只有具体实现

    • 抽象类:具体实现和规范(抽象方法)都有!

    • 接口:只有规范!自己无法写方法专业的约束约束和实现分离:面向接口编程

    • 接口就是规范,定义的是一组规则,体现了现实世界中"如果你是…则必须能…"的思想。如果你是天使必须能飞。

    • 接口的本质是契约,就像我们人间的法律一样,制定好后大家都遵守。

    • 和抽象类一样,接口不能用来创建对象实例,因为接口中没有构造方法

    • OO的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计模式都只针对具备了抽象能力的语言(比如c++、java、c#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。

  2. 接口的实现

    • 接口由类来实现,一个类通过关键字implements声明自己实现了一个或多个接口。如果实现了多个接口,用逗号分隔接口名。接口实现的定义格式如下:

      [修饰符] class 类名 implements 接口1, 接口2,{
      	//接口方法的实现
          ...
          //实现类新增的内容
          ...         
      }
      
      • 一个类实现某个接口也就是要实现该接口的所有抽象方法。如果只实现了一部分,则接口未被实现的那些抽象方法会被实现类所继承,从而成为抽象类。
      • 实现类继承接口的常量,成为自己的静态常量,既可以通过接口名访问,也可以通过类名访问,还可以通过实现类的对象访问。
      • 在实现类中实现接口的抽象方法时,必须使用完全相同的方法原型,并且不能降低方法的访问权限,因而实现类所实现的方法权限必须是public。
    • 一个类可以在继承某个父类的同时实现若干个接口,这种情况下的定义形式如下:

      [修饰符] class 类名 extends 父类名 implements 接口1, 接口2,{
      	...
      }
      
    • 有些接口没有声明任何方法,这种接口叫做标识接口。标识接口仅仅充当标识的作用,用来表明实现它的类属于某个特定的类型,如下例:

      interface Ore {}  //矿石接口
      
      interface Weapon {}  //武器接口
      
      interface Rubbish{}  //垃圾接口
      
      class Gold implements Ore{ //金矿
             public String toString(){
                    return "Gold";
             }
      }
      
      class Copper implements Ore{  //铜矿
             public String toString(){
                    return "Copper";
             }
      }
      
      class Gun implements Weapon{ //枪
             public String toString(){
                    return "Gun";
             }
      }
      
      class Grenade implements Weapon{ //榴弹 
             public String toString(){
                    return "Grenade";
             }
      }
      
      class Stone implements Rubbish{  //石头
             public String toString(){
                    return "Stone";
             }
      }
      
      
    public class Demo17 {
        public static void main(String[] args) {
            //new UserService();接口不能用来创建对象实例
            UserServiceImpl userService = new UserServiceImpl();
            userService.add("add");
            userService.delete("delete");
            userService.update("update");
            userService.query("query");
            userService.time();
        }
    }
    interface UserService{//接口1
        //静态常量,在接口中如果有变量声明,那么该变量一定由public、final、static修饰,没有的话系统在编译时也会默认加上。
        //接口中final常量必须在定义时初始化
        public static final int age = 9;
        //接口中的所有定义的方法,默认其实都是抽象的public abstract
        void add(String name);
        void delete(String name);
        void update(String name);
        void query(String name);
    }
    interface TimeService{//接口2
        void time();
    }
    //类可以实现抽象类:extends
    //类可以实现接口:implements
    //实现接口的类就需要重写接口中的方法
    //多继承~利用接口实现多继承
    class UserServiceImpl implements UserService,TimeService{//接口实现类
        @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("time");
        }
    }
    /*
    结果:
        add
        delete
        update
        query
        time
    */
    
  3. 接口的继承

  • Java允许一个接口继承其它的接口,声明格式如下

    [public] interface 接口名 extends 父接口1, 父接口2,{
    	//新增的抽象方法或静态常量
    	...
    }
    
  • 一个接口可以继承一个或多个接口,从而继承其它接口的抽象方法和常量。

  • 实现类除了实现当前接口自己定义的抽象方法,还要实现该接口继承而来的抽象方法。如果在子接口中声明了和父接口同名的常量或原型相同的方法,则父接口中的常量被隐藏,方法被重写(覆盖)。

    interface x{//接口x
    	void m1();
    	void m2();
    }
    interface y {//接口y
    	void m3();
    }
    interface z extends x, y{//接口z
    	void m4();
    }
    class xyz implements z{//实现类
    	public void m1() {
        	……//实现代码
    	}
    	public void m2() {
    	    ……//实现代码
    	}
    	public void m3() {
    	    ……//实现代码
    	}
        public void m4() {
    	    ……//实现代码
    	}
    }
    
    
  1. 接口类型: 接口也是一种引用数据类型,可以当作数据类型使用。可以用接口来声明变量,也可以把一个实现类的对象赋值给接口变量,这也是上转型

  2. 接口回调与多态

    • 接口回调是指把实现类的对象赋值给接口变量,然后通过接口变量访问接口方法,则实际访问的是实现类所实现的方法:

      接口类型 接口变量 = 用实现类创建的对象;        
      接口变量.接口方法([参数列表]);
      
    • 通过接口变量来访问接口方法时,由于不同的实现类对同一方法有不同的实现,从而表现出多态。

      public class Demo18 {
          public static void main(String[] args) {
              ShowMessage sm;//声明接口变量。
              sm = new TV();//TV的对象赋值给接口变量。
              sm.showTradeMark();//通过接口回调来调用TV的方法showTradeMark()     
              sm = new PC();//PC的对象赋值给接口变量。
              sm.showTradeMark();//通过接口回调来调用PC的方法showTradeMark()           
          }
      }
      interface ShowMessage {//接口
          void showTradeMark();
      }
      class TV implements ShowMessage {//实现类
          public void showTradeMark()   {
              System.out.println("电视机");
          }
      }
      class PC implements ShowMessage  {//实现类
          public void showTradeMark()  {
              System.out.println("电脑");
          }
      }
      /*
      结果:
          电视机
          电脑
      */
      
  3. 接口做参数
    如果一个方法的形参是接口类型,就可以将实现类的对象传给这个参数,这也是上转型。

    public class Demo19 {
        public void f(Show s){//接口作为参数
            s.show();
        }
        public static void main(String[] args) {
            Demo19 demo19 = new Demo19();
            demo19.f(new C());//实现类的对象传给接口参数
            demo19.f(new D());//实现类的对象传给接口参数
        }
    }
    interface Show{//接口
        void show();
    }
    class C implements Show{//实现类A
        public void show(){
            System.out.println("I love This Game");
        }
    }
    class D implements Show{//实现类B
        public void show(){
            System.out.println("我喜欢看NBA");
        }
    }
    /*
    结果:
        I love This Game
        我喜欢看NBA
    */
    

九、内部类

内部类就是在一个类的内部再定义一个类,比如,A类中定义一个B类,那么B类相对A类来说就称为内部类,而A类相对B类来说就是外部类了。

  1. 成员内部类:定义在类体中(不是方法体中)且没有用static修饰的类

    • 定义常规内部类时可以使用private、protected、public、友好等访问权限,与用于成员变量和成员方法时的规则相同,可以将常规内部类看作是外部类的一个成员。

    • 在常规内部类中可以访问外部类的成员,用“外部类名.this.外部类成员”的形式访问外部类的实例成员,用“外部类名.外部类成员”的形式访问外部类的*静态成员*。在不引起歧义的情况下,前缀“外部类名.this”和“外部类名”可以省略。

    • 在外部类的实例方法和构造方法中可以直接声明内部类的变量、创建内部类的对象:

      内部类名 变量名 = new 内部类名();
      
    • 在外部类的实例方法和构造方法之外的地方创建常规内部类的对象:

      外部类名.内部类名 变量名 = 外部类对象.new 内部类名();
      
    public class Demo20 {
        public static void main(String[] args) {
            Outer outer = new Outer();//outer外部类对象
            //通过这个外部类来实例化内部类
            Outer.Inner inner = outer.new Inner();
            inner.getID();
            System.out.println("========");
            outer.out();
        }
    }
    class Outer{
        private int id = 99;//static
        public void out(){
            System.out.println("这是外部类的方法");
            Inner inner = new Inner();
            System.out.println(inner.name);
        }
        // 常规内部类:定义在类体中(不是方法体中)且没有用static修饰的类
        class Inner{//static
            private String name = "zanbimo";
            public void in(){
                System.out.println("这是内部类的方法");
            }
            //获得外部类的私有属性
            public void getID(){
                System.out.println(id);
            }
        }
    }
    /*
    结果:
        99
        ========
        这是外部类的方法
        zanbimo
    */
    
  2. 静态内部类:定义在类体中并且用static关键字修饰的内部类。

    • 静态内部类可以直接访问外部类的*static成员*,但不能直接访问外部类的实例成员。同样可以用权限修饰符限制静态内部类的访问权限。

    • 在static内部类中不能使用“外部类名.this”访问外部类的成员。

    • 创建静态内部类的对象:

      外部类名.内部类名 对象变量 = new 外部类名.内部类名();
      
    • 静态内部类可以定义在接口中,定义在接口中的任何类都自动成为public和static的。

    public class MyOuter {
    	static int x = 100;
        int m = 20;
        static class MyInner{
        	private String y = "Hello!";
            public void innerMethod(){
            	System.out.println("x = " + x);//x = 100
                System.out.println("y = " + y);//y = Hello!
                System.out.println("m = " + m);//错误,静态内部类不能直接访问外部类的实例成员
                //正确写法:
                SyOuter myouter = new MyOuter;//需要实例化外部类为一个对象
    	   		System.out.println("m = " + myouter.m); //通过这个对象来访问
        	}   
    	}
    	public static void main(String[] args) {
    		MyOuter.MyInner si = new MyOuter.MyInner();//创建静态内部类的对象 
        	si.innerMethod();
        }
    }
    /*
    结果:
        x = 100
        y = Hello!
        m = 20
    */
    
  3. 局部内部类:在方法或语句块中定义的类称为局部内部类。

    • 局部内部类只能在定义它的方法或语句块内可以使用
    • 局部内部类同局部变量一样不能使用private、protected、public等访问修饰符,也不能使用static修饰。
    • 局部内部类可以访问外部类的成员(static成员和实例成员),也可以通过“外部类名.this”访问外部类对象及成员。
    public class MyOuter{
        private int size = 5;
        private static int y = 7;
        public Object makeInner(int localVar){
            class MyInner{//该内部类只在makeInner()方法中有效
                int y = 4;
                public String toString() {//重写了Object类的toString方法//取得对象信息,返回该对象的字符串表示
                    String s1 = "Outer size = " + size +";";//访问外部类私有成员变量
                    String s2 = " Inner localVar = " + localVar +";";//访问局部变量
                    String s3 = " Inner y = " + this.y +";";//访问内部类的成员变量y,可以省略this
                    String s4 = " Outer y = " + MyOuter.this.y;//必须通过MyOuter.this访问外部类的同名成员变量y
                    return  s1 + s2 + s3 + s4;//5 + 47 + 4 + 7
                }//方法
            }//内部类
            return new MyInner();//返回一个引用,所以方法类型为Object
        }
        public static void main(String[] args) {
            Object obj = new MyOuter().makeInner(47);//创建MyOuter对象,不能使用MyInner来声明对象变量
            System.out.println(obj.toString());
        }
    }
    /*
    结果:
    	Outer size = 5; Inner localVar = 47; Inner y = 4; Outer y = 7
    */
    
  4. 匿名内部类 (简称匿名类):在一个类的内部定义,在定义类的同时创建对象并且类没有名字

    • 定义格式:

      new  类或接口(){
          匿名内部类的类体    
      };
      
    • 匿名类必须继承一个类或实现一个接口,new后面的名字是匿名类的父类或要实现的接口,new返回的是所创建的匿名类对象。

    • 匿名类的定义和使用都在同一个地方,如果某个类的对象只使用一次,可以考虑使用匿名类。

    • 由于匿名类没有名字,所以不能定义构造方法

    • 匿名类可以访问外部类的成员变量和成员方法:

      • 如果匿名类定义在外部类的静态方法中,那么只能访问外部类的静态成员;如果匿名类定义在外部类的实例方法中,则外部类的静态成员和非静态成员都可以访问。
      • 如果匿名类定义在外部类的类体中,那就看把匿名类对象赋值给外部类的静态成员变量还是实例成员变量。如果是赋值给静态成员变量,则只能访问外部类的静态成员;如果赋值给实例成员变量,则外部类的静态的和非静态成员都可以访问。
      • 可以用“外部类名.this.成员”的形式来访问外部类的实例成员,用“外部类名.成员”的形式来访问外部类的静态成员。在不引起歧义的情况下,前缀“外部类名.this”和“外部类名”可以省略。
    class MyOuter {
    	private  int size = 10;
        static private int n = 2;
    
        Object x = new Object() {
        	public String toString(){//重写父类的toString()方法
                return "成员变量x的值是: " + MyOuter.this.size / MyOuter.n; //10/2
            }   
        };//将匿名类对象赋值给父类Object类型的成员变量x,是上转型
    
        public  Object makeInner(int localVar){          
        	int  unit = 2;
            return new Object() {    
                public String toString() {//重写父类的toString()方法
                    return "OuterSize =  " + size / unit + "  LocalVar = " + localVar / n;//10/2+48/2
                } 
            };//将匿名类对象作为方法的返回值
        }
         
        public void printObject(int x) { 
        	System.out.println(  
                new Object(){ 
                    public String toString(){ 
                        return "输入参数的值是: " + MyOuter.this.size / MyOuter.n;//10/2
                    }
                }
            ); //匿名类对象做方法的输入参数
         }
         
         public static void main(String args[]) {
         	MyOuter obj = new MyOuter();            
            obj.printObject(15);            
            System.out.println(obj.makeInner(48)); 
            System.out.println(obj.x);
        }
    }
    /*
    结果:
        输入参数的值是: 5
        OuterSize =  5  LocalVar = 24
        成员变量x的值是: 5
    */
    
    public class Demo21 {
        public static void main(String[] args) {
            //没有名字初始化类,不用考虑实例保存到变量中
            new Apple().eat();//eat
            System.out.println("========================");
            User user = new User() {//匿名内部类
                @Override
                public void hello() {
                    System.out.println("hello");
                }
                public String toString(){
                    return "重写object类的toString方法";
                }
            };
            user.hello();
            System.out.println(user.toString());//toString()可以省略,默认调用
        }
    }
    class Apple{//类
        public void eat(){
            System.out.println("eat");
        }
    }
    interface User{//接口
        void hello();
    }
    /*
    结果:
    eat
    ========================
    hello
    重写object类的toString方法
    */
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值