封装

封装(encapsulation) 就是把抽象出来的数据[属性]和对数据的操作[方法]封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作[方法],才能对数据进行操作。

封装的好处

  1. 隐藏实现细节
  2. 可以对数据进行验证,保证安全合理

封装的实现步骤

  1. 将属性进行私有化 [不能直接修改属性]
  2. 提供一个公共的set方法,用于对属性判断并赋值。
public boid setXxx(类型,参数名){
	//加入数据验证的业务逻辑
    属性=参数名;
}
  1. 提供一个公共的get方法,用于获取属性的值

public XX getXxx(){//权限判断
	return xxx;
}

封装快速入门

package com.xxb.encap;

public class encap01 {
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("jackdfasdf");
        person.setAge(150);
        person.setSalary(30000);
        System.out.println(person.toString());
        //使用构造器来指定属性
        Person smith = new Person("smith", 2000, 50000);
        System.out.println(smith);
    }
}
class  Person{
    public String name;
    private  int age;
    private double salary;
    //无参构造器
    public Person() {
    }
    //有参构造器
    public Person(String name, int age, double salary) {
//        this.name = name;
//        this.age = age;
//        this.salary = salary;
        //我们可以将set方法写在构造器中,这样仍然可以验证
        setName(name);
        setAge(age);
        setSalary(salary);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        if(name.length()>=2 && name.length()<=6){
            this.name = name;
        }else{
            System.out.println("名字长度不对,需要(2-6)个字符,默认名字");
            this.name="无名";
        }
    }


    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        //判断
        if(age>=1 && age<=120){//合理范围
            this.age=age;
        }else{
            System.out.println("你设置的年龄不对,需要在(1-120),默认年龄18");
            this.age=18;//给一个默认年龄
        }

    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }
}

java-面向对象_ide

继承

继承可以解决代码复用,当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要extends来声明继承父类即可。

java-面向对象_面向对象_02

  • 父类又叫基类,或者超类
  • 子类又叫派生类。

java-面向对象_java_03

继承好处

  1. 提高的代码复用性
  2. 提高了代码的扩展性和维护性

继承快速入门

父类

package com.xxb.extend_;
//父类 是Pupil和Graduate的父类
public class Student {
    //共有属性
    public String name;
    public  int age;
    private  double score;
    //共有方法

    public void setScore(double score) {
        this.score = score;
    }


    public void showInfo() {
        System.out.println("学生名" +
                name  +
                ", 年龄" + age +
                ", 分数" + score);
    }
}

子类Graduate

package com.xxb.extend_;

public class Graduate extends Student{
        public  void testing(){
            System.out.println("我是大学生"+name);
        }
}

子类

package com.xxb.extend_;

public class Pupil extends Student {
    public void testing(){
        System.out.println("我是小学生"+name);
    }
}

调用

package com.xxb.extend_;

public class extends01 {
    public static void main(String[] args) {
        Pupil pupil = new Pupil();
        pupil.name="xxx";
        pupil.age=10;
        pupil.testing();
        pupil.setScore(79);
        pupil.showInfo();
        System.out.println("====");
        Graduate graduate = new Graduate();
        graduate.name="ggg";
        graduate.age=22;
        graduate.testing();
        graduate.setScore(100);
        graduate.showInfo();
    }
}

效果

java-面向对象_java_04

继承的细节

  1. 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性不能在子类直接访问,要通过公共的方法去访问。

示例代码

package com.xxb.extendsDetail_;

public class ExtendsDetail {
    public static void main(String[] args) {
        Sub sub = new Sub();
        sub.sayOk();
    }
}

父类

package com.xxb.extendsDetail_;


public class Base {
    //父类
    public  int n1=100;
    protected  int n2=200;
    int n3=300;
    private int n4=400;
    //无参构造
    public Base(){
        System.out.println("base()....");
    }
    public void test100(){
        System.out.println("test100");
    }
    protected void test200(){
        System.out.println("test200");
    }
    void test300(){
        System.out.println("test300");
    }
    private void test400(){
        System.out.println("test400");
    }
    //父类提供一个public 方法,返回自己的私有属性提供给子类使用。
    public int getN4(){
        return n4;
    }
    //父类提供一个public 方法,返回自己的私有方法提供给子类使用
    public void call(){
        test400();
    }
}

子类

package com.xxb.extendsDetail_;

public class Sub extends Base {
    //子类
    public Sub(){
        //super(); //默认调用父类的无参构造器
        System.out.println("sub()...");
    }
    public void sayOk(){
        //我们发现 父类的非private属性和方法都可以访问
        System.out.println(n1+" "+n2+" "+n3);
        //使用父类的public方法获取父类的私有属性
        System.out.println("n4="+getN4());
        //使用父类的public方法调用父类的私有方法test400()
        call();
    }

}

结果示意图

java-面向对象_java_05

  1. 子类必须调用父类的构造器,完成父类的初始化
  2. 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过。

示例代码

父类

package com.xxb.extendsDetail_;


public class Base {
    //父类
    public  int n1=100;
    protected  int n2=200;
    int n3=300;
    private int n4=400;
    //无参构造
//    public Base(){
//        System.out.println("base()....");
//    }
//    有参构造
    public Base(int n1){
        System.out.println("父类的有参构造base(int n1)....");
    }
    public void test100(){
        System.out.println("test100");
    }
    protected void test200(){
        System.out.println("test200");
    }
    void test300(){
        System.out.println("test300");
    }
    private void test400(){
        System.out.println("test400");
    }
    //父类提供一个public 方法,返回自己的私有属性提供给子类使用。
    public int getN4(){
        return n4;
    }
    //父类提供一个public 方法,返回自己的私有方法提供给子类使用
    public void call(){
        test400();
    }
}

子类

package com.xxb.extendsDetail_;

public class Sub extends Base {
    //子类
    public Sub(){
        //指定父类的构造器
        super(9);
        System.out.println("sub()...");
    }
    public  Sub(String name){
//        指定父类的构造器
        super(8);
        System.out.println("子类的有参构造器");
    }
    public void sayOk(){
        //我们发现 父类的非private属性和方法都可以访问
        System.out.println(n1+" "+n2+" "+n3);
        //使用父类的public方法获取父类的私有属性
        System.out.println("n4="+getN4());
        //使用父类的public方法调用父类的私有方法test400()
        call();
    }

}


调用

package com.xxb.extendsDetail_;

public class ExtendsDetail {
    public static void main(String[] args) {
        Sub sub = new Sub();
        sub.sayOk();
    }
}

结果如下

java-面向对象_父类_06

  1. 如果希望指定去调用父类的某个构造器,则显式的调用一下: super(参数列表)
  2. super在使用时,需要放在构造器第一行,(super只能在构造器中使用)
  3. super()和this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器。

  1. java所有类都是Object类的子类
  2. 父类构造器的调用不限于直接父类! 将一直往上追溯直到Object类(顶级父类)

  1. 子类最多只能继承一个父类(指直接继承),即java中的单继承机制。
    1. 如何让A类继承B类和C类 [A继承B,B继承C]
  2. 不能滥用继承,子类和父类之间必须满足is-a的逻辑关系。

继承本质的详解

按照查找关系依次向上查找来返回信息。

super关键字

super代表父类的引用,用于访问父类的属性、方法、构造器。

  • 访问父类的属性,但不能访问父类的private属性,super.属性名;
  • 访问父类的方法,不能访问父类的private方法. super.方法名(参数列表);
  • 访问父类的构造器: super(参数列表):只能放在构造器的第一句。
public class Bone extends Food{
	public Bone(String name){
    	super(name);
    }
}

使用super关键字的好处

  1. 调用父类的构造器的好处(分工明确)父类属性由父类初始化,子类的属性由自乐初始化。
  2. 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须使用super。如果没有重名,使用super、 this直接访问是一样的效果。
  3. super的访问不限于直接父类,如果爷爷类和本类找那个有同名的成员,也可以使用super去访问爷爷类的成员; 如果多个基类中都有同名的成员,使用super访问遵循就近原则。A->B->C

super和this的比较

java-面向对象_子类_07

方法的重写/覆盖(override)

方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名称,返回类型、参数一样,那么我们就说子类的这个方法覆盖父类的那个方法

注意事项和使用细节

  1. 子类的方法参数,方法名称,要和父类方法的参数,方法名称完全一样。
  2. 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类,比如父类返回类型时Object,子类方法返回的类型或者是String.
  3. 子类方法不能缩小父类方法的访问权限
    1. void sayOk() public void sayOk()

重载与重写的比较

java-面向对象_面向对象_08

多态(polymorphic)

方法或对象具有多种形态。是面对对象的第三大特征,多态是建立在封装和继承基础之上的。

方法的多态(重写和重载体现多态)

package com.xxb.ploy_;

public class PloyMethod {
    public static void main(String[] args) {
        //方法重载多态
        A a = new A();
        //传入不同的参数,就会调用不容sum方法,就提现了多态
        System.out.println(a.sum(10,20));
        System.out.println(a.sum(10,20,30));
        B b = new B();
        a.say();
        b.say();
    }
}
class B{
    public void say(){
        System.out.println("B say() 方法被调用....");
    }
}
class A extends B{
    public int sum(int n1,int n2){
        return n1+n2;
    }
    public int sum(int n1,int n2,int n3){
        return n1+n2+n3;
    }
    @Override
    public  void say(){
        System.out.println("A say() 方法被调用");
    }
}

对象的多态

注意点

  • 一个对象的编译类型和运行类型可以不一致
  • 编译类型在定义对象时,就确定了,不能改变
  • 运行类型时可以变化的
  • 编译类型看定义时=号的左边, 运行类型看=号的右边

package com.xxb.ploy_;

public class PolyObject {
    public static void main(String[] args) {
        Animal animal = new Dog();
        //因为运行时,执行到改行时,animal运行类型时Dog,所以cry就是Dog的cry
        animal.cry();//Dog cry() 小狗汪汪
        //运行类型时Cat
        animal=new Cat();
        animal.cry();//Cat cry() 小猫喵喵叫

    }
}

class Animal{
    public  void cry(){
        System.out.println("Animal cry() 动物在叫");
    }
}
class Cat extends  Animal{

    @Override
    public  void cry(){
        System.out.println("Cat cry() 小猫喵喵叫");
    }
}
class Dog extends Animal{
    @Override
    public  void cry(){
        System.out.println("Dog cry() 小狗汪汪");
    }
}

java-面向对象_面向对象_09

多态的注意事项和细节

  • 多态的前提是:两个对象(类)存在继承关系

多态的向上转型

  1. 本质: 父类的引用指向了子类的对象。
  2. 语法: 父类类型 引用名=new 子类类型();
  3. 特点: 编译类型看左边,运行类型看右边
    1. 可以调用父类中的所有成员(需要遵守访问权限)
    2. 不能调用子类中特有成员;
    3. 最终运行效果看子类的具体实现。
package com.xxb.ploy_;

public class PloyDetail {
    public static void main(String[] args) {
        //向上转型: 父类的引用指向了子类的对象
        Animal_ animal_ = new Cat_();
        //可以调用父类中的所有成员(需遵守访问权限)
        animal_.eat();
        //因为在编译阶段,能调用哪些成员,由编译类型来决定的。
//        animal_.catchMouse() 报错

    }
}
class Animal_{
    String name="动物";
    int age=10;
    public void sleep(){
        System.out.println("睡");
    }
    public void run(){
        System.out.println("跑");
    }
    public void eat(){
        System.out.println("吃");
    }
    public void show(){
        System.out.println("hello,你好");
    }
}
class Cat_ extends  Animal_{
    @Override
    public void eat(){
        System.out.println("猫吃鱼");
    }
    public void catchMouse(){
        System.out.println("猫抓老鼠");
    }
}

多态的向下转型

  1. 语法: 子类类型 引用名=(子类类型) 父类引用
  2. 只能强转父类的引用,不能强转父类的对象
  3. 要求父类的引用必须指向的是当前目标类型的对象
  4. 当向下转型后,可以调用子类类型中所有的成员

package com.xxb.ploy_;

public class PloyDetail {
    public static void main(String[] args) {
        //向上转型: 父类的引用指向了子类的对象
        Animal_ animal_ = new Cat_();
        //可以调用父类中的所有成员(需遵守访问权限)
        animal_.eat();
        //因为在编译阶段,能调用哪些成员,由编译类型来决定的。
//        animal_.catchMouse() 报错

        //向下转型
        //语法: 子类类型  引用名=(子类类型) 父类引用;
        Cat_ cat_=(Cat_) animal_;
        cat_.catchMouse();


    }
}
class Animal_{
    String name="动物";
    int age=10;
    public void sleep(){
        System.out.println("睡");
    }
    public void run(){
        System.out.println("跑");
    }
    public void eat(){
        System.out.println("吃");
    }
    public void show(){
        System.out.println("hello,你好");
    }
}
class Cat_ extends  Animal_{
    @Override
    public void eat(){
        System.out.println("猫吃鱼");
    }
    public void catchMouse(){
        System.out.println("猫抓老鼠");
    }
}

java-面向对象_面向对象_10

属性重写的问题

属性没有重写,属性的值看左边的编译类型,编译类型时什么值就是什么值。

instanceOf

//instanceOf比较操作符,用于判断对象的类型是否为xxx类型或xxx类型的子类型

BB bb=new BB();
//判断bb是AA的子类型不
System.out.printIn(bb instanceOf AA);

java动态绑定机制

  • 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
  • 当调用对象属性时,没有动态绑定机制,哪里声明哪里使用。

案例一

package com.xxb.ploy_.dynamic_;

public class DynamicBinding {
    public static void main(String[] args) {
        //向上转型
        A a= new B();
        System.out.println(a.sum());
        System.out.println(a.sum1());
    }
}
class A{
    public int i=10;
    public  int sum(){
        return  getI()+10;
    }
    public int sum1(){
        return i+10;
    }
    public int getI(){
        return i;
    }
}
class B extends A{//子类
    public  int i=20;
    @Override
    public int sum(){
        return i+20;
    }
    @Override
    public  int getI(){
        return i;
    }
    @Override
    public int sum1() {
        return i+10;
    }
}

效果

java-面向对象_java_11

案例二

package com.xxb.ploy_.dynamic_;

public class DynamicBinding {
    public static void main(String[] args) {
        //向上转型
        A a= new B();
        System.out.println(a.sum());//20+10=30
        System.out.println(a.sum1());
    }
}
class A{
    public int i=10;
    //此时getI()是子类的getI()
    public  int sum(){
        return  getI()+10;
    }
    public int sum1(){
        return i+10;
    }
    public int getI(){
        return i;
    }
}
class B extends A{//子类
    public  int i=20;
//    @Override
//    public int sum(){
//        return i+20;
//    }
    @Override
    public  int getI(){
        return i;
    }
    @Override
    public int sum1() {
        return i+10;
    }
}

效果

java-面向对象_ide_12

案例三

package com.xxb.ploy_.dynamic_;

public class DynamicBinding {
    public static void main(String[] args) {
        //向上转型
        A a= new B();
        System.out.println(a.sum());
        System.out.println(a.sum1());
    }
}
class A{
    public int i=10;
    public  int sum(){
        return  getI()+10;
    }
    public int sum1(){
        return i+10;
    }
    public int getI(){
        return i;
    }
}
class B extends A{//子类
    public  int i=20;
//    @Override
//    public int sum(){
//        return i+20;
//    }
    @Override
    public  int getI(){
        return i;
    }
//    @Override
//    public int sum1() {
//        return i+10;
//    }
}

效果

java-面向对象_子类_13

多态的应用

多态的数组

数组的定义类型为父类的类型,里面保存的实际元素类型为子类类型

案例

package com.xxb.ploy_.polyarr_;

public class PolyArray {
    public static void main(String[] args) {
        Person[] persons=new Person[5];
        persons[0]=new Person("jack",20);
        persons[1]=new Student("mck111",18,100);
        persons[2]=new Student("smith",19,70);
        persons[3]=new Teacher("scott",30,20000);
        persons[4]=new Teacher("king",50,25000);

        //循环遍历多态数组,调用say
        for(int i=0;i<persons.length;i++){
            //person[i]编译类型Person ,运行类型时根据实际情况JVM来判断
            //动态绑定机制
            System.out.println(persons[i].say());
            if(persons[i] instanceof Teacher){
                //向下转型
              Teacher teacher=(Teacher) persons[i];
                teacher.teach();
            }
            if(persons[i] instanceof Student){
                //向下转型
                Student student=(Student) persons[i];
                student.study();
            }
        }


    }
}

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

    }
    //返回名字和年龄
    public  String say(){
        return name+"\t"+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;
    }


}

class Student extends Person{
    private  double score;

    public Student(String name, int age, double score) {
        super(name, age);
        this.score = score;
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }
    //重新父类say

    @Override
    public String say() {
        return super.say()+" score="+score;
    }
    //学生特有的方法
    public void study(){
        System.out.println("学生 "+getName()+" 正在学习");
    }
}
class Teacher extends  Person{
    private  double salary;

    public Teacher(String name, int age, double salary) {
        super(name, age);
        this.salary = salary;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    //重写父类的say方法

    @Override
    public String say() {
        return super.say()+" salary="+salary;
    }
    //teacher特有的方法teach
    public  void teach(){
        System.out.println("老师 "+getName()+" 正在讲课...");
    }
}

效果

java-面向对象_java_14

多态的参数

方法定义的形参类型为父类类型,实参类型允许为子类类型。

案例

package com.xxb.ploy_.polyparameter;

public class PloyParameter {
    public static void main(String[] args) {
        Worker tom = new Worker("tom", 2500);
        Manager milan = new Manager("milan", 5000, 20000);
        PloyParameter.showEmpAnual(tom);
        PloyParameter.showEmpAnual(milan);
        PloyParameter.testWork(tom);
        PloyParameter.testWork(milan);

    }
    //实现获取任何员工对象的年工资
    public static void showEmpAnual(Employee e){
        System.out.println(e.getAnnual());
    }

    public  static  void testWork(Employee e){
        //向下转型
        if(e instanceof Worker){
            ((Worker) e).work();
        }
        //向下转型
        if(e instanceof Manager){
            ((Manager) e).manage();
        }
    }
}

//员工类
class Employee{
    private  String name;
    private  double salary;

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }
    //获取年工资
    public double getAnnual(){
        return 12*salary;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }


}
class Worker extends Employee{
    public Worker(String name, double salary) {
        super(name, salary);
    }
    public void work(){
        System.out.println("普通员工 "+getName()+" is working");
    }
    @Override
    public double getAnnual(){
        return  super.getAnnual();
    }
}
class Manager extends Employee{
    private  double bonus;
    public  Manager(String name,double salary,double bonus){
        super(name,salary);
        this.bonus=bonus;
    }

    //
    public  void manage(){
        System.out.println("经理 "+getName()+" is managing");
    }
    //重写获取年薪的方法


    @Override
    public double getAnnual() {
        return super.getAnnual()+bonus;
    }

    public double getBonus() {
        return bonus;
    }

    public void setBonus(double bonus) {
        this.bonus = bonus;
    }
}

效果

java-面向对象_父类_15

类变量(静态变量)

静态(static)变量是所有对象共享。

快速入门

package com.xxb.static_;

public class ChildGame {
    public static void main(String[] args) {

    }
}

class Child{
    private  String name;
    //定义一个静态变量 该变量最大的特点就是会被Child类的所有的对象实例共享
    public  static int count=0;
    public  Child(String name){
        this.name=name;
    }
    public  void join(){
        System.out.println(name+" 加入了游戏。。。");
    }
}

类变量内存布局

静态变量放在gc堆中(jdk8以上)

访问方式

//类名.静态变量名
Child.count

//对象名.静态变量名
child1.count

类变量使用细节

java-面向对象_父类_16

类方法(静态方法)

访问形式

//访问修饰符  static 数据返回类型  方法名(){}

//调用  类名.类方法名 或者 对象名.类方法名

类方法使用场景

java-面向对象_子类_17

类方法的使用细节

java-面向对象_ide_18

main方法

基本语法

java-面向对象_java_19

java-面向对象_java_20

代码块

抽象类

final关键字

final可以修饰类、属性、方法和局部变量

在某些情况下,会使用到final

  1. 当不希望类被继承时,可以用final修饰
  2. 当不希望父类的某个方法被子类覆盖/重写(override)时,可以用final关键字修饰。
  3. 当不希望类的的某个属性的值被修改,可以用final修饰。
  4. 当不希望某个局部变量被修改,可以使用final修饰。
package com.xxb.final_;

public class Final01 {
    public static void main(String[] args) {
        E e = new E();
//        e.TAX_RATE=0.09;
    }
}
//A类不能被其他类继承,可以使用final修饰 A类
final class A{

}
//class B extends A{
//
//}
class C{
    //要求hi方法不能被子类重写,可以使用final修饰hi方法
    public final void hi(){}
}
class D extends C{
//    @Override
//    public void hi() {
//        System.out.println("重写了C类的hi方法");
//    }
}

//当不希望类的某个属性的值不能被修改,可以用final修饰。
class E {
    public final static double TAX_RATE=0.08;
}

//当不希望某个局部变量被修改,可以使用final修饰
class F{
    public void cry(){
        final double NUM=0.01;
//        NUM=0.9;
        System.out.println("NUM"+NUM);
    }
}


final使用细节

  1. final修饰的属性又叫常量,一般用xx_xx_xx来命名
  2. final修饰属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置之一:
    1. 定义时: 如 public final double TAX_RATE=0.08;
    2. 在构造器中
    3. 在代码块中
  3. 如果final修饰的属性是静态的,则初始化的位置只能是
    1. 定义时
    2. 在静态代码块 不能再构造器中赋值
  4. final类不能继承,但是可实例化对象
  5. 如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承。
  6. 一般来说:如果一个类已经是final类了,就没有必要再将方法修饰成final方法
  7. final不能修饰构造方法(即构造器)
  8. final和static往往搭配使用,效率更高(不会导致类的加载),底层编译器做了优化处理。
  9. 包装类(Interger,Double,Float,Boolean等都是final),String也是final类。

抽象类使用场景

当父类的某些方法,需要声明,但是又不确定如何实现时,可以将其声明为抽象方法,那么这个类就是抽象类。

抽象类会被继承,有其子类去实现其抽象方法。

abstract class Animal{
    String name;
    int age;
    public abstract void eat();
}

抽象类简介

  1. 用 abstract 关键字来修饰一个类时,这个类就叫做抽象类,访问修饰符 abstract 类名{}
  2. 用 abstract 关键字来修饰一个方法时,这个方法就是抽象方法。
    1. 访问修饰符 abstract返回类型 方法名(参数列表); //没有方法体。
  3. 抽象类的价值更多作用是在于设计,是设计者设计后,让子类继承并实现抽象类()
  4. 抽象类,在框架和设计模式使用较多。

抽象类使用的注意细节

  1. 抽象类不能被实例化
  2. 抽象类不一定要包含abstract方法, 也就是说,抽象类可以没有abstract方法
  3. 一旦类包含了abstract方法,则这个类必须声明为abstract
  4. abstract只能修饰类和方法,不能修饰属性和其他的。
  5. 抽象类可以有任意成员[因为抽象类还是类],比如: 非抽象方法、构造器、静态属性等等。
  6. 抽象方法不能有主体,即不能实现
  7. 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己声明为abstract类。
  8. 抽象方法不能使用private、final和static来修饰,因为这些关键字都是和重写相违背的。

抽象类的最佳实践-模板设计模式

package com.xxb.abstract_;

public class templateTest {
    public static void main(String[] args) {
        AA aa = new AA();
        aa.calculateTime();
    }
}
abstract class Template{ //抽象类-模板设计模式
    public  abstract  void job(); //抽象方法
    public void calculateTime(){
        //得到开始的时间
        long start=System.currentTimeMillis();
        job();//这里的job是子类的job(),动态机制绑定
        //得到结束的时间
        long end=System.currentTimeMillis();
        System.out.println(" 执行时间 "+(end-start));
    }
}
class AA extends Template{

    @Override
    public void job() {
        long num =0;
        for(long i=1;i<=80000;i++){
            num+=i;
        }
    }
}



接口

接口快速入门

package com.xxb.interface_;

public class interface01 {
    public static void main(String[] args) {
        //创建手机,相机对象
        Camera camera = new Camera();
        Phone phone = new Phone();
        //创建计算机
        Computer computer = new Computer();
        //接入手机
        computer.work(phone);
        System.out.println("=======");
        //接入相机
        computer.work(camera);
    }
}
//接口
interface  UsbInterface{
    //规定接口的相关方法 规范
    public void start();
    public void stop();
}
//手机
class Phone implements UsbInterface{

    @Override
    public void start() {
        System.out.println("手机开始工作");
    }

    @Override
    public void stop() {
        System.out.println("手机停止工作");
    }
}
//相机
class Camera implements  UsbInterface{

    @Override
    public void start() {
        System.out.println("相机开始工作");
    }

    @Override
    public void stop() {
        System.out.println("相机停止工作");
    }
}

class  Computer{
    public  void work(UsbInterface usbInterface){
        //通过接口,来调用
        usbInterface.start();
        usbInterface.stop();
    }
}

效果图

java-面向对象_面向对象_21

接口基本介绍

interface  接口名{
    //属性
    //方法(1.抽象方法,2,默认实现方法 3.静态方法)
}
class 类名 implements  接口{
    自己属性;
    自己方法;
    必须实现的接口的抽象方法
}

小结:

  1. 在Jdk7.0前 接口里的所有方法都没有方法体
  2. Jdk8.0后接口类可以有静态方法,默认方法,也就是说接口中可以有房的具体实现。

interface AInterface{
    //写属性
    public int n1=10;
    //写方法
    //在接口中,抽象方法,可以省略abstract关键字
    public void hi();
    //在jdk8后,可以有默认实现方法,需要使用default关键字修饰
    default public  void ok(){
        System.out.println("ok");
    }
    //在jdk8以后,可以有静态方法
    public static void cry(){
        System.out.println("cry");
    }
}

接口的应用场景

  1. 用于同一规范

接口的细节

  1. 接口不能被实例化
  2. 接口中所有的方法都是public方法,接口中抽象方法,可以不用abstaract修饰
  3. 一个普通类实现接口,就必须将该接口的所有方法都实现
  4. 抽象类实现接口,可以不用实现接口的方法
  5. 一个类同时可以实现多个接口
  6. 接口中的属性,只能是final的,而且是public static final修饰符
  7. 接口中属性的访问形式:接口名.属性名
  8. 一个接口不能继承其他的类,但是可以继承多个别的接口。

package com.xxb.interface02_;

public class InterfaceDetail02 {
    public static void main(String[] args) {
        //说明n1是static
        System.out.println(IB.n1);
    }
}
interface  IB{
    //接口中的属性,只能是final的,而且是public static final修饰符

    int n1=10;
    void hi();
}
interface IC{
    void say();
}
//接口不能继承其他的类,但是可以继承多个接口
interface  ID extends IB,IC{
    
}
class Pig implements IB,IC{

    @Override
    public void hi() {

    }

    @Override
    public void say() {

    }
}

接口与继承的比较

继承是先天能力,接口是后天去努力实现的能力

当子类继承了父类,就自动拥有父类的功能,如果子类需要扩展功能,可以通过实现接口的方式了扩展。

实现接口是对java单继承机制的一种补充。

区别

  1. 继承的加载主要在于:解决代码的复用性和可维护性
  2. 接口的加载主要在于:设计,设计好各种规范(方法),让其他类去实现这些方法。
  3. 接口在一定程度上实现代码解耦。
  4. 继承时is-a的关系, 接口是like-a的关系
package com.xxb.interface03_;

public class ExtendsVSInterface {
    public static void main(String[] args) {
        LittleMonkey tx = new LittleMonkey("tx");
        tx.climbing();
        tx.swimming();
        tx.flying();
    }

}
//猴子
class Monkey{
    private  String name;

    public Monkey(String name) {
        this.name = name;
    }

    public void climbing(){
        System.out.println(this.name+"猴子会爬树");
    }

    public String getName() {
        return name;
    }
}
//接口
interface  Fishable{
    void swimming();
}
interface  Birdable{
    void flying();
}
//继承
class LittleMonkey extends  Monkey implements Fishable,Birdable{
    public  LittleMonkey(String name){
        super(name);
    }

    @Override
    public void swimming() {
        System.out.println(getName()+"通过向鱼学习,学会了游泳");
    }

    @Override
    public void flying() {
        System.out.println(getName()+"通过向鸟儿学习,学会了飞行");
    }
}

效果图

java-面向对象_子类_22

接口多态

package com.xxb.interface_;

public class interface01 {
    public static void main(String[] args) {
        //创建手机,相机对象
        Camera camera = new Camera();
        Phone phone = new Phone();
        //创建计算机
        Computer computer = new Computer();
        //接入手机
        computer.work(phone);
        System.out.println("=======");
        //接入相机
        computer.work(camera);

        //多态数组---接口类型数组
        UsbInterface[] usbs=new UsbInterface[2];
        usbs[0]=new Phone();
        usbs[1]=new Camera();
    }
}
//接口
interface  UsbInterface{
    //规定接口的相关方法 规范
    public void start();
    public void stop();
}
//手机
class Phone implements UsbInterface{

    @Override
    public void start() {
        System.out.println("手机开始工作");
    }

    @Override
    public void stop() {
        System.out.println("手机停止工作");
    }
}
//相机
class Camera implements  UsbInterface{

    @Override
    public void start() {
        System.out.println("相机开始工作");
    }

    @Override
    public void stop() {
        System.out.println("相机停止工作");
    }
}

class  Computer{
    //接口多态的体现,
    //接收了 实现了 UsbInterface接口的    类的对象实例
    public  void work(UsbInterface usbInterface){
        //通过接口,来调用
        usbInterface.start();
        usbInterface.stop();
    }
}


interface AInterface{
    //写属性
    public int n1=10;
    //写方法
    //在接口中,抽象方法,可以省略abstract关键字
    public void hi();
    //在jdk8后,可以有默认实现方法,需要使用default关键字修饰
    default public  void ok(){
        System.out.println("ok");
    }
    //在jdk8以后,可以有静态方法
    public static void cry(){
        System.out.println("cry");
    }
}

接口多态传递

package com.xxb.interface04_;

public class InterfacePolyPass {
    public static void main(String[] args) {
        //接口类型的变量可以指向,实现了该接口的类的对象实例
        IG ig=new Teacher();
        //如果IG继承了IH接口,Teacher类实现了IG接口
        //那么,实际上就相当于Teacher类也实现了IH接口。
        //这就是 接口多态传递
        IH ih=new Teacher();
    }
}
interface  IH{}
interface  IG extends IH{}
class Teacher implements  IG{

}


内部类

一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。是我们类的第五大成员。内部类最大的特点就是可以直接访问私有属性,并且可以提现类与类之间的包含关系。

补充:类的五大成员: 属性,方法,构造器,代码块、内部类。

demo

package com.xxb.innerclass_;
//外部其他类
public class InnerClass01 {
    public static void main(String[] args) {

    }
}
//外部类
class Outer{
    //属性
    private  int n1=100;
    //构造器
    public Outer(int n1){
        this.n1=n1;
    }
    //方法
    public  void m1(){
        System.out.println("m1()");
    }
    //代码块
    {
        System.out.println("代码块。。。");
    }
    //内部类
    class Inner{

    }
}

局部内部类

局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。

  1. 可以直接访问外部类的所有成员,包含私有的
  2. 不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final修饰。因为局部变量也可以使用final
  3. 作用域:仅仅在定义它的方法或代码块中。
  4. 局部内部类---访问----> 外部类的成员 [访问方式:直接访问]
  5. 外部类---访问---> 局部内部类的成员
    1. 访问方式: 创建对象, 再访问(注意:必须在作用域内)
  6. 外部其他类---不能访问---> 局部内部类(因为局部内部类地位是一个局部变量)
  7. 如果外部内核局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问

注意:

  1. 局部内部类定义在方法中/代码块
  2. 作用域在方法体或者代码块中
  3. 本质仍然是一个类
package com.xxb.localInnerClass;

/**
 * 演示内部类的使用
 */
public class LocalInnerClass {
    public static void main(String[] args) {
        Outer02 outer02 = new Outer02();
        outer02.m1();
    }
}
//外部类
class  Outer02{
    private int n1=100;
    private  void m2(){
        System.out.println("outer02 m2");
    }//私有方法
    public void m1(){//方法
        //1.局部内部类是定义在外部类的局部位置,通常在方法体中
        //2.不能添加访问修饰符,但是可以使用final修饰
        //3.作用域:仅仅在定义它的方法或代码块中。
        class Inner02{//局部内部类(本质仍然是一个类)
            //4.可以直接访问外部类的所有成员,包含私有的
            private  int n1=800;
            public void f1(){
                //5.局部内部类可以直接访问外部类的成员,比如下面外部类n1和m2()
                //7.如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员。
                //使用(外部类名.this.成员)去访问
                //Outer02.this 本质就是外部类实例对象,哪个对象调用m1方法,Outer02.this就是哪个对象。
                System.out.println("n1= "+n1+" 外部类的n1="+Outer02.this.n1);
                m2();
            }

        }
        //6. 外部类在方法中,可以创建Inner02对象,然后调用方法即可。
        Inner02 inner02 = new Inner02();
        inner02.f1();

    }
}


java-面向对象_父类_23

匿名内部类

匿名内部类解读

  1. 本质是一个类
  2. 该类没有名字
  3. 同时还是一个对象
package com.xxb.AnonymousInnerClass;

/**
 * 演示匿名内部类的使用
 */
public class AnonymousInnerClass {
    public static void main(String[] args) {
        Outer04 outer04 = new Outer04();
        outer04.method();
    }
}
//外部类
class Outer04{
    private  int n1=10;
    //方法
    public void method(){
        //基于接口的匿名内部类
        //需求:  想使用IA接口,并创建对象 该类只是使用一次,后面不再使用
        //可以使用匿名内部类来简化开发
        //匿名内部类智能使用一次
        //tiger的编译类型 IA
        //tiger的运行类型  就是匿名内部类
        /**
         * 底层
         * class Xxx implements IA{
         * @Override
         * public void cry(){
         *     System.out.println("老虎叫");
         * }
         * }
         */
        IA tiger=new IA(){
            @Override
            public void cry() {
                System.out.println("老虎叫");
            }
        };
        tiger.cry();
        //运行类型
        System.out.println("tiger的运行类型="+tiger.getClass());

    }
}
interface  IA{
    public void cry();
}
class Father{
    public Father(String name) {
        super();
    }
    public void test(){

    }
}

java-面向对象_父类_24

匿名内部类的使用

package com.xxb.AnonymousInnerClass;

/**
 * 演示匿名内部类的使用
 */
public class AnonymousInnerClass {
    public static void main(String[] args) {
        Outer04 outer04 = new Outer04();
        outer04.method();
    }
}
//外部类
class Outer04{
    private  int n1=10;
    //方法
    public void method(){
        //基于接口的匿名内部类
        //需求:  想使用IA接口,并创建对象 该类只是使用一次,后面不再使用
        //可以使用匿名内部类来简化开发
        //匿名内部类智能使用一次
        //tiger的编译类型 IA
        //tiger的运行类型  就是匿名内部类
        /**
         * 底层
         * class Xxx implements IA{
         * @Override
         * public void cry(){
         *     System.out.println("老虎叫");
         * }
         * }
         */
        IA tiger=new IA(){
            @Override
            public void cry() {
                System.out.println("老虎叫");
            }
        };
        tiger.cry();
        //运行类型
        System.out.println("tiger的运行类型="+tiger.getClass());

        //演示基于类的匿名内部类
        //分析
        //1. father编译类型 Father
        //2. father运行类型  匿名内部类 Outer04$2
        /**
         * class Outer04$2 extends Father{}
         */
        Father father=new Father("jack"){
            @Override
            public void test(){
                System.out.println("匿名内部类重写了test方法");
            }
        };
        System.out.println("father对象的运行类型="+father.getClass());
        father.test();
        //0749
        //基于抽象类的匿名内部类
        Animal animal=new Animal(){
            @Override
            void eat() {
                System.out.println("小狗吃馒头");
            }
        };
        //调用
        animal.eat();

    }
}
interface  IA{
    public void cry();
}
class Father{
    public Father(String name) {
        super();
    }
    public void test(){

    }
}
abstract  class Animal{
    abstract  void eat();
}

java-面向对象_面向对象_25

匿名内部类细节

package com.xxb.AnonymousInnerClassDetail;

public class AnonymousInnerClassDetail {
    public static void main(String[] args) {
        Outer05 outer05 = new Outer05();
        outer05.f1();
    }
}
class Outer05{
    private  int n1=99;
    public void f1(){
        //创建一个基于类的匿名内部类
        Person p=new Person(){
            private int n1=88;
            @Override
            public void hi(){
                System.out.println("匿名内部类重写了hi方法 ni="+n1);
                System.out.println("外部内的n1= "+Outer05.this.n1);

            }
        };
        p.hi();//动态绑定,运行类型时Outer05$1

        //也可以直接调用
        new Person(){
            @Override
            public  void hi(){
                System.out.println("匿名内部类重写了hi,哈哈");
            }
            @Override
            public  void ok(String str){
                super.ok(str);
            }
        }.ok("jack");
    }
}
//类
class Person{
    public void hi(){
        System.out.println("Person hi()");
    }
    public  void ok(String str){
        System.out.println("Person ok() "+str);
    }
}

java-面向对象_子类_26

匿名内部类的实践

使用场景

  1. 把匿名内部类当做实参传递,简洁高效
package com.xxb.InnerClassExe;

public class InnerClassExecise01 {
    public static void main(String[] args) {
        //当做实参直接传递,简洁高效
        f1(new IA(){
            @Override
            public void show(){
                System.out.println("这是一幅画");
            }
        });
    }
    //静态方法
    public static void f1(IA ia){
        ia.show();
    }
}
//接口
interface IA{
    void show();
}

java-面向对象_父类_27

实践2

package com.xxb.InnerClass02;

public class InnerClass02 {
    public static void main(String[] args) {
        CellPhone cellPhone = new CellPhone();
        //一个闹钟
        cellPhone.alarmClock(new Bell() {
            @Override
            public void ring() {
                System.out.println("兰州人该起床了");
            }
        });
        //第二个闹钟
        cellPhone.alarmClock(new Bell(){
            @Override
            public void ring(){
                System.out.println("小伙伴们该上课了");
            }
        });

    }
}
interface  Bell{
    void ring();
}
class CellPhone{
    public  void alarmClock(Bell bell){
        bell.ring();
    }
}

java-面向对象_java_28

成员内部类(定义在外部类的成员位置上)

成员内部类的使用

成员内部类是定义在外部类的成员位置,并且没有static修饰。

  • 可以直接访问外部类的所有成员,包含私有的。
  • 可以添加任意访问修饰符(public protected, 默认, private),因为它的定位就是一个成员
  • 作用域和外部类的其他成员一样,在外部类的成员方法中创建成员内部类对象,再调用方法
  • 成员内部类-----访问-----外部类(比如属性)[访问方式: 直接访问]
  • 外部类----访问-----内部类(说明) [访问方式: 创建对象,再访问]
  • 外部其他类-----访问------>成员内部类
  • 如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问

package com.xxb.MemberInner;

public class MemberInnerClass {
    public static void main(String[] args) {
        Outer01 outer01 = new Outer01();
        outer01.t1();

        //外部其他类使用 成员内部类
        //Outer01.new Inner08(); 相当于把 new Inner08()当作是Outer01成员
        Outer01.Inner08 inner08 = outer01.new Inner08();
        inner08.say();
        //第二种方式:  在外部类中,编写一个方法,可以放回Inner08对象
        Outer01.Inner08 inner08Instance = outer01.getInner08Instance();
        inner08Instance.say();
    }
}
class Outer01{//外部类
    private int n1=10;
    public String name="张三";
    private void hi(){
        System.out.println("hi()方法...");
    }
    class Inner08{
        //成员内部类
        public  double sal=99.8;
        private   int n1=66;
        public  void say(){
            //就近原则 n1=66;
            System.out.println("Outer01的n1= "+n1+" outer01的name= "+name+" 外部类的n1= "+Outer01.this.n1);
            //调用外部类方法
            hi();
        }

    }
    //返回一个成员内部类实例对象
    public  Inner08 getInner08Instance(){
        return new Inner08();
    }
    //写方法
    public  void t1(){
        //使用成员内部类
        Inner08 inner08=new Inner08();
        inner08.say();
        System.out.println(inner08.sal);
    }

}


java-面向对象_ide_29

静态内部类(定义在外部类的成员位置上)

说明: 静态内部类是定义在外部类的成员位置,并且有static修饰

  • 可以直接访问外部类的所有静态成员,包含私有的,但不能访问非静态成员
  • 可以添加任意访问修饰符(public,proteccted,默认,private),因为它的定位就是一个成员
  • 作用域:同其他的成员,为整个类体
  • 静态内部类---访问---> 外部类 访问方式: 创建对象,再访问
  • 外部类-----访问---> 静态内部类 访问方式: 创建对象,再访问
  • 外部其他类---> 访问---->静态内部类
  • 如果外部类和静态内部类的成员重名的时候,静态内部类访问的时候, 默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.成员)去访问。
package com.xxb.staticInnerClass;

public class StaticInnerClass {
    public static void main(String[] args) {
        Outer01 outer01 = new Outer01();
        outer01.show();
        //外部其他类  使用静态内部类
        //方式一
        //因为静态内部类,是可以通过类名直接访问()
        Outer01.Inner02 inner02 = new Outer01.Inner02();
        inner02.say();
        //方式二
        //编写一个方法,返回一个静态内部类的对象实例
        Outer01.Inner02 inner021 = Outer01.getInner02();
        inner021.say();
    }
}
class Outer01{
    private int n1=10;
    private static String name="张三";
    static class Inner02{
        private  static String  name="测试";
        public void say(){
            System.out.println(name+" 外部类name= "+Outer01.name);
            //不能直接访问外部类的非静态成员
//            System.out.println(n1);
        }
    }
    //外部类使用内部类
    public void show(){
        new Inner02().say();
    }
    public static Inner02 getInner02(){
        return new Inner02();
    }

}

java-面向对象_面向对象_30