Java_面向对象

面向对象
    面向对象是基于面向过程的编程思想
    面向过程:强调的是每一个功能的步骤
    面向对象:强调的是面向对象,然后由对象去调用功能
面向对象的思想特点
    1、是一种更符合我们思想习惯的思想
    2、可以将复杂的事情简单化
    3、将我们从执行者变成了指挥者
面向对象开发
    就是不断的创建对象,使用对象,指挥对象做事情
面向对象设计
    就是在管理和维护对象之间的关系
面向对象特征
    封装(encapsulation)
    继承(inheritance)
    多态(polymorphism)
类:是一组相关的属性和行为的集合,是一个抽象的概念
对象:是该类事物的具体表现形式,具体存在的个体
创建对象的格式:
    类名 对象名 = new 类名();

/*
    在一个java文件中写两个类:一个基本的类,一个测试类。
    注意:文件名称和测试类名称一致。
    如何使用呢?
        创建对象使用。    
    如何创建对象呢?
        格式:类名 对象名 = new 类名();    
    如何使用成员变量呢?
        对象名.变量名
    如何使用成员方法呢?
        对象名.方法名(...)
*/
//这是学生类
class Student {
    //姓名
    String name; //null
    //年龄
    int age; //0
    //地址
    String address; //null
    //学习
    public void study() {
        System.out.println("学生爱学习");
    }
    //吃饭
    public void eat() {
        System.out.println("学习饿了,要吃饭");
    }
    //睡觉
    public void sleep() {
        System.out.println("学习累了,要睡觉");
    }
}

//这是学生测试类
class StudentDemo {
    public static void main(String[] args) {
        //类名 对象名 = new 类名();
        Student s = new Student();
        //输出成员变量值
        //System.out.println(s.name);
        //System.out.println(s.age);
        //System.out.println(s.address);
        //改进写法
        System.out.println(s.name+"---"+s.age+"---"+s.address);
        //给成员变量赋值
        s.name = "林青霞";
        s.age = 27;
        s.address = "北京";
        //赋值后的输出
        System.out.println(s.name+"---"+s.age+"---"+s.address);
        //调用方法
        s.study();
        s.eat();
        s.sleep();
    }
}

 

构造方法

构造方法是专门用来创建对象的方法,当我们通过关键字new来创建对象时,其实就是在调用构造方法。

格式:

  public 类名称(参数类型 参数名称){

    方法体;

  }

注意事项:

  1、构造方法的名称必须和所在的类名称完全一样,就连大小写也要一样。

  2、构造方法不要返回值类型,连void都不写。

  3、构造方法不能return一个具体的返回值。

  4、如果没有编写任何构造方法,那么编译器将会默认赠送一个构造方法,没有参数、方法体什么事情都不做

    public Student() {}

  5、一旦写了至少一个构造方法,那么编译器将不再赠送。

  6、构造方法也是可以进行重载的(方法名相同, 参数列表不同)。

package com.xdl;

public class Student {
    private String name;
    private int age;

    public Student() {
        System.out.println("构造方法被执行了!");
    }

    public Student(String name, int age) {
        System.out.println("全参构造方法被执行了!");
        this.name = name;
        this.age = age;
    }
}
/*------------------------*
package com.xdl;

public class TestPerson {
    public static void main(String[] args) {
        Student stu1 = new Student(); //无参构造
        Student stu2 = new Student("xdl", 18); //全参构造
    }
}

定义一个标准类(Java Bean)

一个标准的类通常包括下面四个组成部分:

  1、所有的成员变量都要使用private关键字修饰

  2、为一个成员变量编写一对Getter/Setter方法

  3、编写一个无参数的构造方法

  4、编写一个全参数的构造方法

技巧:编写一个类实现两个私有成员变量后,可以通过idle工具生成代码(code-->Generate)

package com.xdl;

public class Student {
    private String name; //姓名
    private int age; //年龄

    public Student() { // 无参数构造方法
    }

    public Student(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;
    }
}

static关键字

静态属性

如果一个成员变量使用了static关键字,那么这个变量不再属于对象自己,而是属于所在的类。多个对象共享同一份数据。

package com.xdl.demostatic;

public class Student {
    private int id;
    private String name;
    private int age;
    static String room;
    private static int idCounter = 0; //学号计数器,每当new了一个新对象的时候,计数器++

    public Student() {
        Student.idCounter++;
    }

    public int getId() {
        return id;
    }

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

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
        this.id = ++Student.idCounter;
    }

    public static String getRoom() {
        return room;
    }


    public static void setRoom(String room) {
        Student.room = room;
    }


    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;
    }

}
/********************/
package com.xdl.demostatic;

public class StaticField {
    public static void main(String[] args) {
        Student.setRoom("001"); // 或Student.room = "001"
        Student stu1 = new Student("张三", 15);
        System.out.println("姓名:" + stu1.getName() + "年龄:" + stu1.getAge() + "学号:" + Student.getRoom() + "班级:" + stu1.getId());
        Student stu2 = new Student("李四", 1);
        System.out.println("姓名:" + stu2.getName() + "年龄:" + stu2.getAge() + "学号:" + Student.getRoom() + "班级:" + stu2.getId());
    }
}

静态方法:

1、一旦使用static修饰成员方法,那么这就成为了静态方法。静态方法不属于对象,而是属于类。
2、如果没有static关键字,那么必须首先创建对象,然后通过对象才能使用它。

3、如果有了static关键字,那么不需要创建对象,直接就能通过类名称进行调用

总结:

无论是成员属性,还是成员方法,如果有了static,都推荐使用类名称进行调用。

注意事项:

  1、静态不能直接访问非静态  

    原因:因为在内存中是先有的静态内容,后有的非静态内容

  2、静态方法中不能用this

    原因:this代表当前对象,通过谁调用的方法,谁就是当前对象

package com.xdl.demostatic;

public class MethodStatic {
    public void method() {
        System.out.println("这是一个成员方法");
    }

    public static void staticMethod() {
        System.out.println("这是一个静态方法");
    }
}
/***********************/
package com.xdl.demostatic;

public class MethodStaticDemo {
    public static void main(String[] args) {
        MethodStatic obj = new MethodStatic(); //首先创建对象
        // 然后才能使用没有static关键字的内容
        obj.method();
        // 对于静态方法来说,可以通过对象名进行调用,也可以执行通过类名称来调用
        obj.staticMethod(); //正确,不推荐,这种写法在编译之后也会被javac编译成为"类名.静态方法名"
        MethodStatic.staticMethod(); //正确,推荐

        method(); // 对于本类当中的静态方法,可以省略类名称,
        MethodStaticDemo.method(); // 完全等效与上面的语句
    }

    public static void method() {
        System.out.println("自己的方法");
    }
}
View Code

静态代码块

格式:

  public class 类名称 {

    static {

      //静态代码块内容

    }

  }

特点:

  1、当第一次用到本类时,静态代码块执行唯一的一次。

  2、静态内容总是优先于非静态,所以静态代码块比构造方法先执行。

典型用途:

  用来一次性地对静态成员变量进行赋值。

 1 package com.xdl.demostatic;
 2 
 3 public class Person {
 4     static {
 5         System.out.println("静态代码块被执行。");
 6     }
 7 
 8     public Person() {
 9         System.out.println("构造方法被执行。");
10     }
11 }
12 /***************************/
13 package com.xdl.demostatic;
14 
15 
16 public class ChunkStatic {
17     public static void main(String[] args) {
18         Person one = new Person();
19         Person two = new Person();
20         /**
21          * 静态代码块被执行。
22          * 构造方法被执行。
23          * 构造方法被执行。
24          */
25     }
26 }

面向对象三大特性:封装、继承、多态

封装:

1、成员方法本身就是封装的体现。

2、私有的成员属性(private)也是封装的体现。

package com.xdl;

public class Person {
    private String name;
    private int age;
    private boolean male;
    // 尽量使用set+成员属性首字母大写
    public void setAge(int num) {
        if (num>=0 && num <=100) {
            age = num;
            return;
        }
        System.out.println("年龄应该在0~100之间");
    }

    public int getAge() {
        return age;
    }
    public void setMale(boolean m){
        male = m;
    }
    // 如果返回的是boolean类型, 建议使用is+属性首字母大写
    public boolean isMale() {
        return male;
    }
}
/*---------------------------------------------*/
package com.xdl;

public class TestPerson {
    public static void main(String[] args) {
        Person person = new Person();
        person.setAge(10);
        System.out.println(person.getAge());
        person.setMale(true);
        System.out.println(person.isMale());
    }
}

继承

  • 继承是多态的前提,,如果没有继承,就没有多态。
  • 继承主要解决【共性抽取】
  • 父类也被称为超类、基类
  • 子类也被称为派生类
  • 在继承关系中,“子类就是一个父类”,也就是说子类可以被当做父类看待;例如:父类是员工,子类是讲师,那么讲师就是一个员工。

特点

  1. java继承是单继承, 一个类同时只能继承一个父类(平级的)。
  2. 一个父类可以被多个子类继承。
  3. 一个子类可以继承一个父类,这个父类还可以继承其它父类。

继承的格式(extends)

  public class 子类名 extends 父类名 {

    //....

  }

继承中成员变量的访问特点

  1. 通过子类对象直接访问成员变量
    1. 等号左边是谁,就优先用谁,没有则向上找。
  2. 通过成员方法间接访问成员变量
    1. 该方法属于谁,就优先用谁,没有则向上找。
package com.xdl.demo01;

class Fu {
    int numFu = 10;
    int num = 100;

    public void methodFu() {
        // 使用的是本类当中的num, 不会向下找子类num
        System.out.println(num);
    }
}

class Zi extends Fu {
    int numZi = 20;
    int num = 200;

    public void methodZi() {
        // 优先找本类中的num,如果没有再去找父类中的num
        System.out.println(num);
    }
}

public class ExtendFieldDemo {
    public static void main(String[] args) {
        Fu fu = new Fu(); // 创建父类
        System.out.println(fu.numFu); //10 只有父类的东西,没有任何子类的内容
        System.out.println(fu.num); // 100
        Zi zi = new Zi(); // 创建子类
        System.out.println(zi.numZi); //20
        System.out.println(zi.numFu); //10 首先找子类中的numFu, 没有则去父类中找
        System.out.println(zi.num); //200
        zi.methodZi(); // 200
        zi.methodFu(); // 100 这个方法是在父类中定义的
    }
}
View Code

区分子类方法中三种重名的变量

  1. 局部变量:直接使用
  2. 本类的成员变量:this.变量名
  3. 父类的成员变量:super.变量名
package com.xdl.demo01;

class Fu {
    int num = 10;
}

class Zi extends Fu {

    int num = 20;

    public void method() {
        int num = 30;
        System.out.println(num); // 30  局部变量
        System.out.println(this.num); // 20 本类成员变量
        System.out.println(super.num); //10 父类成员变量
    }
}

public class ExtendFieldDemo {
    public static void main(String[] args) {
        Zi zi = new Zi();
        zi.method();
    }
}
View Code

继承中成员方法的访问特点

创建的对象是谁,就优先用谁,如果没有则向上找。

总结:

  无论是成员方法还是成员变量,如果没有都向上找父类,绝对不会向下找子类的。

重写(override)

概念:在继承关系中,方法名称一样、参数列表也一样。也称为覆盖、覆写,为了和重载区分开,经常称为覆盖重写。

特点:创建的是子类对象,则优先使用子类方法。

注意事项:

  1. 必须保证父子类中的方法名称一样、参数列表也一样。
  2. @Override修改在方法前面,用来检查是不是有效的正确覆盖重写,这个注解不是必须的。
  3. 子类方法的返回值必须【小于等于】父类方法的返回值。
    1. 提示:java.lang.Object类是所有类的公共最高父类,java.lang.Strin就是Object的子类
  4. 子类方法的权限必须【大于等于】父类方法的权限修饰符。
    1. public > protected > (default) > private, default是什么都不写。
package com.xdl.demo01;

class Fu {
    public Object method(String name) {
        System.out.println("父类的方法被调用。" + name);
        return null;
    }
}

class Zi extends Fu {

    @Override
    public String method(String name) { // 方法的权限必须大于等于父类方法的权限, String类是Object的子类
        System.out.println("子类的方法被调用。" + name);
        return name;
    }
}

public class OverrideDemo {
    public static void main(String[] args) {
        Zi zi = new Zi();
        zi.method("xdl");
    }
}

继承中父子类构造方法的访问特点

  1. 子类构造方法当中有一个默认隐含的super()调用, 所以一定是先调用的父类的构造方法,然后执行的子类构造。
  2. 子类构造可以通过super关键字来调用父类重载构造。
  3. super的父类构造调用,必须是子类构造方法的第一个语句。不能一个子类构造调用多次super构造
  4. 只有子类构造方法才能调用父类构造方法,(子类super()必须写在构造方法里面)
  5. 子类必须调用父类构造方法,不写则赠送super(),写了则用写的指定的super(),super()只能有一个,还必须是第一个语句。
package com.xdl.demo02;

class Fu {
    public Fu() {
        System.out.println("父类的无参构造方法被调用。");
    }

    public Fu(String name) {
        System.out.println("父类的有参构造方法被调用。");
    }
}

class Zi extends Fu {
    public Zi() {
        // super(); // 默认包含一个隐藏调用
        System.out.println("子类的无参构造方法被调用。");
        //super(); //java: 对super的调用必须是构造器中的第一个语句 Call to 'super()' must be first statement in constructor body
    }

    public Zi(String name) {
        // super(); // 默认包含一个隐藏调用
        super(name); // 调用父类的有参构造方法
        System.out.println("子类的有参构造方法被调用。");
    }
    public void method() {
        // super(); java: 对super的调用必须是构造器中的第一个语句
    }
}

public class ConstructDemo {
    public static void main(String[] args) {
        new Zi();
        new Zi("xdl");
    }
}

super关键字的三种用法

  1. 在子类的成员方法中,访问父类的成员变量
  2. 在子类的成员方法中,访问父类的成员方法
  3. 在子类的构造方法中,访问父类的构造方法

this关键字的三种用法

  1. 在本类的成员方法中,访问本类的成员变量
  2. 在本类的成员方法中,访问本类的另一个成员方法
  3. 在本类的构造方法中,访问本类的另外一个构造方法。
    1. this(...)调用也必须是构造方法的第一个语句,唯一一个。
    2. super和this两种构造调用,不能同时使用。
package com.xdl.demo03;

class Fu {
    public Fu() {
        System.out.println("父类的无参构造方法被调用");
    }
}

class Zi extends Fu {
    private int age;

    public void method() {
        int age = 20;
        System.out.println("age: " + this.age); // 访问成员变量
        this.method2(); // method2()
    }

    public void method2() {
        System.out.println("method2方法被执行。");
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Zi() {
        this(30); // 子类的有参构造方法被调用, 父类的构造方法将不会被调用
        System.out.println("子类的无参构造方法被调用");

        // this(); // Call to 'this()' must be first statement in constructor body
    }

    public Zi(int age) {
        System.out.println("子类的有参构造方法被调用");
        this.age = age;
    }
}

public class ThisDemo {
    public static void main(String[] args) {
        Zi zi = new Zi();
        zi.setAge(10);
        zi.method();
    }
}

抽象(abstract)

抽象方法:就是加上abstract关键字,然后去掉大括号,直接分号结束。

抽象类:抽象方法所在的类必须是抽象类,在class之前加上abstract即可。

如何使用抽象类和抽象方法:

  1. 不能直接创建(new)抽象类对象。
  2. 必须用一个子类来继承抽象类。
  3. 子类必须覆盖重写抽象类中所有的抽象方法,除非子类也是抽象类。
    1. 覆盖重写(实现): 子类去掉抽象方法的abstract关键字,然后补上方法体大括号。
  4. 创建子类对象进行使用。
  5. 抽象类中不一定有抽象方法,但是抽象方法一定要在抽象类中。
  6. 抽象类中可以有构造方法。
  7. 非抽象子类必须实现所有父类的所有抽象方法。
package com.xdl.demo04;

abstract class Animal {
    public abstract void eat();// Abstract method in non-abstract class, Abstract methods cannot have a body

    // 这是一个普通的成员方法
    public void method() {
        System.out.println("普通方法被调用,子类不是必须重写该方法");
    }
    public Animal() {
        System.out.println("抽象类中可以有构造方法,是供子类创建对象时,初始化父类成员使用的。");
    }
}

class Cat extends Animal {//// Class 'Cat' must either be declared abstract or implement abstract method 'eat()' in 'Animal'
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }

}
abstract class  Dog extends Animal {

    public abstract void sleep();

}

class JinMao extends Dog { // 非抽象子类必须实现所有父类的所有抽象方法


    @Override
    public void sleep() {

    }

    @Override
    public void eat() {

    }
}

public class AbstractDemo {
    public static void main(String[] args) {
        //new Animal(); // 'Animal' is abstract; cannot be instantiated
        Cat cat = new Cat();
        cat.eat();
        cat.method();
    }
}

接口(interface)

概念:接口就是多个类的公共规范, 接口是一种引用数据类型,最重要的内容就是其中的抽象法方法。

格式:

public interface 接口名称 {

  //接口内容

}

说明:换成关键字interface之后,编译生成的字节码文件任然是:.java-->.class

接口中的5个内容:

如果是java7那么接口中可以包含的内容有

1、常量

2、抽象方法

package com.xdl.demo02;

/**
 * 在任何版本的java中,接口都能定义抽象方法
 * 注意事项:
 * 1、接口当中的抽象方法,修饰符必须是两个固定的关键字:public abstract
 * 2、这两个关键字修饰符,可以选择性地省略.
 * 3、方法的三要素,可以随意定义。
 * 使用步骤:
 * 1、接口不能直接使用,必须有一个实现类来实现该接口
 * 格式:
 *      public class 实现类名称 implements 接口名称 {
 *          //...
 *      }
 * 2、接口的实现类必须覆盖重写接口中所有的抽象方法。
 * 3、创建实现类的对象,进行使用
 * 在任何版本的java中,接口都能定义"成员变量",但是必须使用public static final 三个关键字进行修饰。
 * 从效果上看,这其实就是接口的常量。
 * 格式:
 *     public static final 数据类型 常量名称 = 数据值;
 * 备注:
 *     一旦使用final关键字进行修饰,说明不可变。
 * 注意事项:
 * 1、接口当中的常量,可以省略public static final,注意:不写也照样是这样的。
 * 2、接口当中的常量,必须进行赋值,不能不赋值。
 * 3、接口当中的常量,使用完全大写的字母,用下划线进行分割。(推荐使用命名规则)
 * 使用方式:
 * 1、接口.名称
 * 2、实现类.名称
 * 3、实现类对象.名称
 */
interface MyInterface {
    public static final int NUM1 = 10;
    int NUM2 = 10;

    public abstract void methodAbs1();
    void methodAbs2();
}
class Inter implements MyInterface {

    @Override
    public void methodAbs1() {
        System.out.println("methodAbs1");
    }

    @Override
    public void methodAbs2() {
        System.out.println("methodAbs2");
    }
}
public class MyInterfaceAbs {
    public static void main(String[] args) {
        System.out.println(MyInterface.NUM1);
        Inter inter = new Inter();
        inter.methodAbs1();
        System.out.println(inter.NUM1);
        System.out.println(Inter.NUM1);
    }
}
View Code

java8可以额外包含:

3、默认方法

package com.xdl.demo02;

/**
 * 从java8开始,接口允许定义默认方法。
 * 格式:
 * public default 返回值类型 方法名称(参数列表) {
 * // 方法体
 * }
 * 备注:
 * 接口当中的默认方法,可以解决接口升级的问题。在接口中添加了新的默认方法,不会应该原始的接口的实现类
 * 1、接口的默认方法,可以通过接口的实现类对象直接调用
 * 2、接口的默认方法,也可以通过接口实现类进行覆盖重写
 * 
 */
interface DefaultInter {
    // 抽象方法 public abstract可以省略
    public abstract void method1();
    //新添加一个默认方法 public 可以省略
    public default void method2() {
        System.out.println("新添加了method2方法");
    };
}
class D1 implements DefaultInter {

    @Override
    public void method1() {
        System.out.println("D1");
    }
    @Override
    public void method2() {
        System.out.println("重写了接口的默认方法");
    }
}
class D2 implements DefaultInter {

    @Override
    public void method1() {
        System.out.println("D2");
    }
}

public class InterfaceDefault {
    public static void main(String[] args) {
        D1 d1 = new D1();
        D2 d2 = new D2();
        d1.method1();
        d2.method1();
        d1.method2();
        d2.method2();
    }
}
View Code

4、静态方法

package com.xdl.demo02;

/**
 * 从java8开始,接口允许定义静态方法。
 * 格式:
 * public static 返回值类型 方法名称(参数列表) {
 * // 方法体
 * }
 * 注意:
 * 不能通过接口实现类或实现类的对象来调用接口的静态方法,原因:一个类可以实现多个接口会产生冲突。
 * 直接通过接口直接调用:
 * 接口名称.静态方法名(参数);
 */
interface StaticInter {
    // public可以省略
    public static void methodStatic() {
        System.out.println("这是一个接口的静态方法!");
    }
}

class S1 implements StaticInter {

}

public class InterfaceStatic {
    public static void main(String[] args) {
        StaticInter.methodStatic(); // 接口直接调用静态方法
        S1 s1 = new S1();
        // S1.methodStatic(); //错误写法
        // s1.methodStatic(); //错误写法
    }
}
View Code

java9可以额外包含:

5、私有方法:

用来解决接口中多个方法中重复代码的问题,并且这个方法不能被接口实现类使用。

package com.xdl.demo05;

/**
 * 问题描述:
 * 我们需要一个公共的方法,用来解决两个默认方法之间重复代码的问题,
 * 但是这个公共方法不应该让是现实使用,应该是私有化的。
 * 解决方案:
 * 从Java9开始接口中允许定义私有方法, 私有方法只能在接口中使用
 * 1、普通私有方法:解决多个默认方法之间重复代码问题
 * private 返回值类型 方法名称(参数列表) {
 *     // 方法体
 * }
 * 2、静态私有方法:解决多个静态方法之间重复代码问题
 * private static 返回值类型 方法名称(参数列表){
 *     //方法体
 * }
 */

interface Inter {
    public default void method1() {
        System.out.println("method1");
        methodCommon();
    }
    public default void method2() {
        System.out.println("method2");
        methodCommon();
    }
    public static void method3() {
        System.out.println("method3");
        //methodCommon(); // Non-static method 'methodCommon()' cannot be referenced from a static context
        methodStaticCommon();
    }

    private void methodCommon(){
        System.out.println("AAA");
        System.out.println("BBB");
        System.out.println("CCC");
    }
    private static void methodStaticCommon(){
        System.out.println("AAA");
        System.out.println("BBB");
        System.out.println("CCC");
    }
}

class InterClass implements Inter {

}

public class PrivateMethod {
    public static void main(String[] args) {
        InterClass ic = new InterClass();
        ic.method1();
        Inter.method3();
        // ic.methodCommon();// 无法调用
    }
}
View Code

接口使用的注意事项

  1. 接口是没有静态代码块或构造方法的
  2. 一个类的直接父类是唯一的,但是一个类可以同时实现多个接口
    1. 格式:public class MyInterfaceImpl implements MyInterfaceA, MyInterfaceB { // 覆盖重写所有抽象方法}
  3. 如果实现类所实现的多个接口当中,存在重复的抽象方法,那么只需要覆盖重写一次即可
  4. 如果实现类没有覆盖重写所有接口当中的所有抽象方法,那么实现类就必须是一个抽象类
  5. 如果实现类所实现的多个接口当中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写
  6. 一个类如果直接父类当中的方法和接口当中的默认方法产生了冲突,优先用父类当中的方法。
  7. 类与类之间是单继承的,直接父类只有一个;类与接口之间是多实现的,一个可以实现多个接口;接口与接口之间是多继承的。
  8. 多个父接口中的抽象方法如果重复,没关系;多个父接口中的默认方法如果重复,那么子接口必须进行默认方法的覆盖重写,而且必须带着default关键字。

多态

extends继承或者implements实现,是多态性的前提。

代码中多态的体现,就是父类的引用指向子类的对象

格式:

  父类名称 对象名 = new 子类名称();

或者:

  接口名称 对象名 = new 实现类名称();

多态中成员变量和成员方法的使用特点

package demo01;

/**
 * 多态中成员变量访问特点
 * 1、直接访问:通过对象直接成员变量
 * 等号左边是谁,就优先用谁,没有则向上找。
 * 2、间接访问:通过成员方法访问
 * 该方法属于谁,就优先用谁,没有则向上找。
 * 多态中成员方法的访问特点
 * 1、new的是谁,就有限用谁,没有这向上找
 * 口诀:
 * 成员变量:编译看左边,运行看左边
 * 成员方法:编译看左边,运行看右边。
 */


class Fu {
    int age = 10;

    public void showAge() {
        System.out.println(age);
    }

    public void method() {
        System.out.println("父类方法");
    }

    public void methodFu() {
        System.out.println("父类特有方法");
    }
}

class Zi extends Fu {
    int age = 20;

    @Override
    public void showAge() {
        System.out.println(age);
    }

    public void method() {
        System.out.println("子类方法");
    }

    public void methodZi() {
        System.out.println("子类特有方法");
    }
}

public class Multi {
    public static void main(String[] args) {
        // 多态:父类的引用指向子类的对象
        Fu zi = new Zi();
        System.out.println(zi.age); // 10 通过对象直接访问成员变量
        // 通过成员方法间接访问,子类如果覆盖重写了父类方法则为20, 如果没有覆盖重写则为10
        zi.showAge();//20
        zi.method();
        zi.methodFu();
        //编译看左边,左边是Fu,Fu中没有methodZi方法所有编译报错
        // zi.methodZi(); // 错误写法
    }
}
View Code

优点:无论右边new的时候换成那个子类对象,等号左边调用方法都不会改变。

向上转型

对象的向上转型,其实就是多态写法。

格式:父类名称 对象名 = new 子类名称();

含义:右侧创建一个子类对象,把它当作父类来看待使用。

注意事项:

  向上转型一定是安全的,从小范围转向了大范围

类似于:double num = 100; int -->double,自动类型转换

向下转型

对象的向下转型,其实是一个【还原的动作】。

格式:子类名称 对象名 = (子类名称) 父类对象;

含义:将父类对象,【还原】成为本来的子类对象。

Animal animal = new Cat(); // 本来是猫,向上转型成为动物

Cat cat = (Cat) animal;//本来是猫,已经被当成动物了,还原回来成为本来的猫。

注意事项:

a. 必须保证对象本来创建的时候,就是猫,才能向下转型成为猫

b.如果对象创建的时候不是猫,现在非要向下转型成为猫,就会报错.(ClassCastException)

类似于:int num = (int) 10.0; //可以  int num = (int) 10.5;//错误,精度损失精度

instanceof: 用于判断某个对象是否是某个类的对象(包括父类、接口)

对象名 instanceof 类名

package demo02;
abstract class  Animal{
    abstract void eat();
}
class Cat extends Animal{

    @Override
    void eat() {
        System.out.println("猫吃鱼");
    }
    void catchMouse(){
        System.out.println("猫捉老鼠");
    }
}
public class Switch {
    public static void main(String[] args) {
        Animal animal = new Cat(); // 本来是猫,向上转型成为动物
        System.out.println(animal instanceof Cat); //true
        System.out.println(animal instanceof Animal); //true
        animal.eat();
        // cat.catchMouse() // 错误写法
        Cat cat = (Cat) animal; // 动物上下转型还原成为猫
        cat.catchMouse();
        System.out.println(cat instanceof Animal); //true
        System.out.println(cat instanceof Cat); //true
    }
}
View Code

final关键字

final关键字代表最终、不可改变的。

常见四种用法:

  1. 可以用来修饰一个类,表示这个类不能继承
  2. 可以用来修饰一个方法
  3. 可以用来修改一个局部变量
  4. 可以用来修饰一个成员变量
package com.xdl.demo07;

/**
 * 当final关键字用来修饰一个类的时候,格式:
 * [修饰符] final class 类名称 {
 *     //...
 * }
 * 含义:当前这个类不能有任何的子类,不能不继承
 * 注意:一个类如果是final的, 那么其中所有的成员方法无法进行覆盖重写,不能有抽象方法
 * 当final关键字用来修饰一个方法的时候,这个方法就是最终方法,也就不能被覆盖重写。格式:
 * [修饰符] final 返回值类型 方法名称(参数列表){
 *     //方法体
 * }
 * 注意事项:
 *      对于类、方法来说,abstract关键字和final关键字不能同时使用。
 * 对于成员变量来说,如果使用final关键字修饰,那么这个变量也照样是不可变的。
 * 1、由于成员变量具有默认值,所以用了final之后必须手动赋值,不会再给默认值了。
 * 2、对于final的成员变量,要么使用直接赋值,要么通过构造方法赋值。二者选其一。
 * 3、必须保证类当中所有重载的构造方法, 都最终会对final的成员变量进行赋值。
 */
final class Fu {
    public void method() {
        System.out.println("方法执行!");
    }
}

public class FinalDemo {
    private final int age = 18;
    private final String name; //所有的构造方法都要对name进行初始化
    
    public FinalDemo(String name) {
        this.name = name;
    }

    public FinalDemo() {
        this.name = "xdl";
    }

    public static void main(String[] args) {
        // 一旦使用final关键字修饰局部变量,那么这个变量就不能进行更改, 一次赋值,终生不变。
        // 对于基本类型来说,不可变说的是变量中的数据不可变。
        // 对于引用类型来说, 不可变说的是变量当中的地址值不可改变。
        final int num1 = 10;
        // num1 = 100; // Cannot assign a value to final variable 'num1'
        final int  num2; //局部变量没有初始化
        // System.out.println(num2); // Variable 'num2' might not have been initialized
        num2 = 100; //正确写法,只要保证唯一一次赋值即可
        
    }
}
View Code

 四个权限修饰比较

是否可以访问 public protected 什么都不写 private
同类 true true true true
同包/通过类对象方法 true true true false
不同包子类 true true false false
不同包非子类 true false false false

 

 

 

 

 

 

package com.xdl.demo06;

public class C1 {
    public int age = 10;

    public void method() {
        // age 权限修饰符为private、不写、protected、public是同类都可以访问
        System.out.println("age");
    }

}
/***********************/
package com.xdl.demo06;

public class C2 {
    public void method () {
        //age修饰符为public、protected、不写时可以访问,private不能访问
        System.out.println(new C1().age);
    }
}
/**************************/
package com.xdl.demo06.other;

import com.xdl.demo06.C1;

public class C3 extends C1 {

    public void method1() {
        // 当age的修饰符为public、protected时可以访问;不写或者private时不能访问
        System.out.println(age);
        System.out.println(super.age);
    }
}
/****************************************/
package com.xdl.demo06.other;

import com.xdl.demo06.C1;

public class C4 {
    public void method() {
        // 当age的修饰符为public时可以访问;当为protected、不写、private时不能访问
        System.out.println(new C1().age);
    }
}
View Code

内部类(Inner Class)

概念: 如果一个类定义在另一个类的内部,这个类就是内部类

分类:

  1. 成员内部类
  2. 局部内部类(包含匿名内部类)

成员内部类

修饰符 class 类名称 {

  修饰符 class 内部类名称 {

    //...

  }

}

注意:内用外随意访问;外用内需要内部类对象。

使用成员内部类的两种方式

  1. 间接访问:在外部类的方法中,使用内部类;然后main只是调用外部类的方法
  2. 直接使用:公式【外部类.内部类 对象名 = new 外部类().new 内部类();】

如果出现重名现象,那么格式是:外部类名称.this.外部类成员变量名

package com.xdl.demo08;

public class Body { //外部类
    int num = 10; // 外部类的成员变量

    public class Heart { //内部类
        int num = 20; // 内部类的成员变量

        public void beat() {
            int num = 30; // 内部类的局部变量
            System.out.println("内部类方法");
            System.out.println("可以直接使用外部类的成员变量" + name);
            System.out.println(num); //30 局部变量就近原则
            System.out.println(this.num);//20内部类的成员变量
            System.out.println(Body.this.num);//10外部类的成员变量
        }
    }

    public void methBody() {
        System.out.println("外部类方法");
        // 通过内部类间接使用内部类方法
        Heart heart = new Heart();
        heart.beat();
    }

    private String name = "xdl";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
/******************************************/
package com.xdl.demo08;

public class DemoInner {
    public static void main(String[] args) {
        Body body = new Body();
        body.methBody();
        Body.Heart heart = new Body().new Heart();
        heart.beat(); //直接使用内部类方法
    }
}
View Code

局部内部类

如果一个类是定义在一个方法内部的,那么这就是一个局部内部类

只有当前所属的方法才能使用它,出了这个方法外就不能使用了

定义格式:

修饰符 class 外部类名称{

  修饰符 返回值类型 外部类方法名称(参数列表){

    class 局部内部类名称{

      // ...

    }

  }

}

局部内部类,如果希望访问所在方法的局部变量,那个这个局部变量必须是【有效final的】。

备注: 从Java 8+开始,只要局部变量事实不变, 那么final关键字可以省略。

原因:

  1. new 出来的对象在堆内存中。
  2. 局部变量是跟着方法走的,在栈内存中。
  3. 方法运行结束之手,立刻出栈,局部变量就会立刻消失
  4. 但是new出来的对象会在堆当中持续存在,直到垃圾回收消失。
package com.xdl.demo09;

class Outer {
    public void methOuter() {
        int num = 20; // 内部类方法使用了外部类局部变量,则这个变量默认会变为常量
        class Inner { // 局部内部类, 不能写任何修饰符

            public void methodInner() {
                // Variable 'num' is accessed from within inner class, needs to be final or effectively final
                System.out.println(num);
            }
        }
        // 只能在外部类方法中使用内部类
        Inner inner = new Inner();
        inner.methodInner();
    }
}

class DemoOuter {
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.methOuter();
    }
}
View Code

定义一个类的时候,权限修饰符规则:

  1. 外部类: public / (default),如果使用public修饰还需要与.java文件同名(Class 'Outer' is public, should be declared in a file named 'Outer.java')  
    1. 为什么不能用private修饰:private表示同类中可以方法,外部类不存在同类。
    2. 为什么不能用protected修饰:protected比defalut多一个不同包子类可以访问,那么不同包的类首先要继承这个类,如果使用protected修饰则无法使用又怎么继承呢。
  2. 成员内部类: public/protected/(default)/private
  3. 局部内部类: 什么都不能写

匿名内部类

如果接口的实现类(或者是父类的子类)只需要使用唯一的一次,那么这种情况下就可以省略掉该类的定义,而改为使用【匿名内部类】。

匿名内部类的定义格式:

接口名称 对象名 = new 接口名称() {

  //覆盖重写所有抽象方法

};

对格式"new 接口名称() {...}"进行解析:

  1. new代表创建对象的动作
  2. 接口名称就是匿名内部类需要实现的那个接口
  3. {...}这才是匿名内部类的内容

注意事项:

  1. 匿名内部类,在创建对象的时候,只能使用唯一一次。如果希望多次创建对象,而且类的内容一样的话,那么久必须使用单独定义的实现类
  2. 匿名对象,在调用方法的时候,只能调用唯一一次。如果希望同一个对象,调用多次方法,那么必须对对象起一个名字。
  3. 匿名内部类是省略了实现类/子类名称,但是匿名对象是省略了对象名称
posted @ 2019-06-30 17:38  xdl_smile  阅读(216)  评论(0编辑  收藏  举报