Java面向对象编程(继承、多态等)-学习笔记(最全~~~)

目录

1、包(package)

1.1  导入包中的类

1.2  静态导入

2.包的访问控制权限

3.常见的系统包

4.继承

5.protected关键字

6.Java中的对属性和方法的四种访问权限

7.final关键字

8.组合

9.向上转型

10.动态绑定

11.方法重写

 12.多态

13.向下转型

 14.super关键字

15.在构造方法中调用重写方法

16.抽象类

17.接口

18.接口使用的实例

19.接口与抽象类比较

 20.Object类

21.子类调用父类构造方法

22.代码的执行顺序 


1、包(package)

包是组织类的一种方式,对应操作系统中的文件夹,使用包的主要目的是保证类的唯一性。

1.1  导入包中的类

      可以使用java.util.Date这种方式引入java.util这个包中的类。

public class Test {
    public static void main(String[] args) {
        java.util.Date date=new java.util.Date();
        System.out.println(date.getTime());
    }
}

但是这种导入比较麻烦,因此我们可以使用import语句导入包。

import java.util.Date;
public class Test {
    public static void main(String[] args) {
        Date date=new Date();
        System.out.println(date.getTime());
    }
}

1.2  静态导入

使用import static 可以导入包中的静态的方法和属性。

import static java.lang.Math.*;
public class Test {
    public static void main(String[] args) {
        double x=30;
        double y=40;
        double result=sqrt(pow(x,2)+pow(y,2));
        System.out.println(result);
    }
}

2.包的访问控制权限

即为default权限,如果某个成员不包含public和private关键字,此时这个成员就是包访问权限控制,这个成员可以在包内部的其他类使用,但是不能在包外部的类使用。

包访问权限的使用必须在同一个包中,一定是同级目录下可见,该包的子包下都不可见。

示例:Demo1和Demo2在同一个包中,Test在其他包中。

Demo1.java

package demo;

public class Demo1 {
    int value=0;
}

  Demo2.java

package demo;

public class Demo2 {
    public static void main(String[] args) {
        Demo1 demo=new Demo1();
        System.out.println(demo.value);
    }
}
//执行结果,能够访问到value变量
0

Test.java

import demo.Demo1;

public class Test {
    public static void main(String[] args) {
        Demo1 demo=new Demo1();
        System.out.println(demo.value);
    }
}

3.常见的系统包

1.java.lang:系统常用基础类(String、Object),此包从JDK1.1后自动导入,无需自己导入

2.java.util:是java提供的工具程序包。

3.java.lang.reflect:java反射编程包。

4.java.net:进行网络编程开包。

5.java.sql:进行数据库开发的支持包。

6.java.util.concurrent(2的子包):多线程部分的线程工具包。

4.继承

基本语法

class 子类 extends 父类{

}

 ·  使用extends指定父类

·  java中一个子类只能继承一个父类(c++、Python等语言支持多继承),但是支持多层继承。

·  对于父类的private的属性和方法,子类是无法访问的。

·  子类的实例中,也包含着父类的实例,可以用super关键字得到父类实例的引用。

class Animal{
    public String name;

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

    public void eat(String food){
        System.out.println(this.name + "正在吃" + food);
    }
}

class Cat extends Animal{
    public Cat(String name) {
        //使用super调用父类的构造方法
        super(name);
    }
}

class Bird extends Animal{
    public Bird(String name) {
        super(name);
    }
    public void fly(){
        System.out.println(this.name + "正在飞");
    }
}
public class Test {
    public static void main(String[] args) {
        Cat cat=new Cat("猫咪");
        cat.eat("鱼干");
        Bird bird=new Bird("鹦鹉");
        bird.fly();
    }
}

多层继承

class Animal{
    public String name;

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

    public void eat(String food){
        System.out.println(this.name + "正在吃" + food);
    }
}

class Person extends Animal{

    public Person(String name) {
        super(name);
    }
}

class China extends Person{

    public China(String name) {
        super(name);
    }
}

5.protected关键字

我们知道,如果我们将属性设置为private,子类则不能访问,但是设置为public违背了我们要封装的初衷,因为有了protected关键字。

·  对于类的调用者来说,proteced修饰的属性和方法是不能访问的。

·  对于类的子类同一个包的其他类(所有类,不一定是子类)来说,protected修饰的属性和方法是可以访问的。

class Animal{
    protected String name;//继承访问权限

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

    public void eat(String food){
        System.out.println(this.name + "正在吃" + food);
    }
}

class Bird extends Animal{

    public Bird(String name) {
        super(name);
    }
}

6.Java中的对属性和方法的四种访问权限

·  private:类内部能访问,类外部不能访问

·  包访问权限:类内部访问,同一个包的其他类也能访问,其他类不能访问。

·  protected:类内部能访问,子类和同一个包中的其他类也能访问,其他类不能访问。

·  public:类内部和类的调用者都能访问。

  表示范围:pubic>protected>default>private

7.final关键字

final修饰变量和属性时,表示常量(不能修改),修饰方法时,表示该方法不能被重写,修饰类时,则该类不能被继承。 

final关键字的作用是限制类被继承。我们平时用的String类就是用final修饰的,不能被继承。

8.组合

组合并没有设计到特殊的语法,仅仅是将一个类的实例作为另一个类的字段 。

 组合表示has-a语义,上述的例子中可以理解为一个学校中“包含”若干学生和教师。

 继承表示is-a语义,在之前提到的动物和猫的例子中,可以理解为猫也是一种动物。

9.向上转型

语法:父类名称 父类引用=new 子类();

向上转型就是将子类对象转换为父类的引用。

 Animal bird=new Bird("圆圆");

向上转型发生的时机:

· 直接赋值(上述代码)

· 方法传参

· 方法返回

 方法传参:

public class Test {
    public static void main(String[] args) {
       Bird bird=new Bird("圆圆");
       feed(bird);
    }

    public static void feed(Animal animal) {
        animal.eat("谷子");
    }
}

这样方法的参数就会统一化。

方法返回:

public class Test {
    public static void main(String[] args) {
       Bird bird=new Bird("圆圆");
       Animal animal=findMyAnimal();
    }

    public static Animal findMyAnimal() {
        Bird bird=new Bird("圆圆");
        return bird;
    }
}

将一个子类对象转换为父类的引用。

10.动态绑定

在java中,调用某个类的方法,究竟执行了哪段代码(是父类方法的代码还是子类方法的代码),要看究竟这个引用指向的是父类对象还是子类对象,这个过程是程序运行时决定的(而不是编译期),因此称为动态绑定

class Animal{
    protected String name;//继承访问权限

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

    public void eat(String food){
        System.out.println("我是一只小动物");
        System.out.println(this.name + "正在吃" + food);
    }
}

class Bird extends Animal{

    public Bird(String name) {
        super(name);
    }
    public void eat(String food){
        System.out.println("我是一只小鸟");
        System.out.println(this.name+"正在吃"+food);
    }
}

public class Test {
    public static void main(String[] args) {
        Animal animal1=new Animal("圆圆");
        animal1.eat("谷子");
        Animal animal2=new Bird("边边");
        animal2.eat("谷子");
    }
}
//执行结果
我是一只小动物
圆圆正在吃谷子
我是一只小鸟
边边正在吃谷子

虽然animal1和animal2虽然都是Animal类型的引用,但是animal1是指向Animal类型的实例,animal2是指向Bird类型的实例。因此animal1调用Animal类中的eat方法,animal2调用的是Bird类中的eat方法。

11.方法重写

发生在有继承关系的类之间,子类中定义了与父类方法名称相同,参数的个数与类型也相同的一组方法,一般来说,方法的返回值也相同,这样的一组方法叫做重写方法。

注意事项:

1.普通方法可以重写,static修饰的静态方法不能重写。

2.一般来说,要定义重写方法,子类和父类一般都是pubilc权限,最起码protected,子类方法的访问权限要大于等于父类方法的权限。

3.子类的返回值必须与父类保持一致(向上转型除外)。

class Animal{
    protected String name;//继承访问权限

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

    public void eat(String food){
        System.out.println("我是一只小动物");
        System.out.println(this.name + "正在吃" + food);
    }
}

class Bird extends Animal{

    public Bird(String name) {
        super(name);
    }
    @Override
    public void eat(String food){//重写父类中的eat方法
        System.out.println("我是一只小鸟");
        System.out.println(this.name+"正在吃"+food);
    }
}

 重载和重写的区别

 12.多态

当一个引用在不同场景下调用相同方法表示出来不同的行为,称为多态。

多态的本质 :方法重写+向上转型

因为父类和子类都定义了一组重写方法,根据向上转型的特点,所有子类对象都使用类的引用来调用一组重写方法从而表示出不同的行为。

使用多态的好处:

(1)类的调用者对类的使用成本进一步降低

       · 封装让类的调用者不需要知道类的实现细节。

       · 多态能让类的调用者连这个类的类型是什么都不需要知道,只需要知道这个对象具有              某个方法即可。

      因此,多态也理解成是封装的更进一步,让类的调用者对类的使用成本进一步降低。

(2)能够降低代码的“圈复杂度”,避免使用大量的if-else

      圈复杂度是一种描述一段代码复杂程度的方式。一段代码中条件语句和循环语句出现的          个数,这个个数就称为“圈复杂度” 。

(3)可扩展能力强

      如果要新增一种新的形状,使用多态的方式代码改动成本也比较低。

13.向下转型

需要强制类型转换:子类 子类引用=(子类) 父类引用;

class Animal{
    protected String name;//继承访问权限

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

    public void eat(String food){
        System.out.println("我是一只小动物");
        System.out.println(this.name + "正在吃" + food);
    }
}

class Bird extends Animal{

    public Bird(String name) {
        super(name);
    }
    @Override
    public void eat(String food){//重写父类中的eat方法
        System.out.println("我是一只小鸟");
        System.out.println(this.name+"正在吃"+food);
    }
    public void fly(){
        System.out.println(this.name+"正在飞");
    }
}

public class Test {
    public static void main(String[] args) {
        Animal animal=new Bird("边边");
        animal.eat("谷子");
        animal.fly();
    }
}
//编译出错
找不到fly方法

对于这行代码,编译器检查有纳西方法存在,看的是Animal这个类型,但是执行时究竟执行父类方法还是子类方法,看的时Bird这个类型。

此时,若想调用fly方法,就需要向下转型。

class Animal{
    protected String name;//继承访问权限

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

    public void eat(String food){
        System.out.println("我是一只小动物");
        System.out.println(this.name + "正在吃" + food);
    }
}

class Bird extends Animal{

    public Bird(String name) {
        super(name);
    }
    @Override
    public void eat(String food){//重写父类中的eat方法
        System.out.println("我是一只小鸟");
        System.out.println(this.name+"正在吃"+food);
    }
    public void fly(){
        System.out.println(this.name+"正在飞");
    }
}

public class Test {
    public static void main(String[] args) {
        Animal animal=new Bird("边边");
        animal.eat("谷子");
        Bird bird=(Bird)animal;
        bird.fly();
    }
}
//执行结果
我是一只小鸟
边边正在吃谷子
边边正在飞

 但是向下转型比较容易出错,因此有两种方式避免向下转型的错误:

1.使用java提供的instanceof关键字进行检车,去掉假的向下转型。

2.要想使用向下转型,先得发生向上转型(核心):使用向下转型的对象恰好是当时向上转型new出来的。

eg:

//向上转型

Animal a1=new Bird();

//向下转型

Bird bird=(Bird)a1;

 Animal animal1=new Cat("橙子");
        if(animal1 instanceof Bird){
            Bird bird=(Bird) animal1;
            bird.fly();
        }

 14.super关键字

super表示父类对象的引用。

· super修饰属性

表示调用父类的属性(private修饰的属性除外)

· super修饰方法

在构造方法中,子类构造方法的this调用不能和父类super调用同时使用,且super调用必须在子类构造方法首行

·super不能直接作为返回值。

15.在构造方法中调用重写方法

我们尽量不要再构造器中调用方法(如果这个方法被子类重写,就会触发动态绑定,但是此时子类对象还没构造完成),就会出现一些问题。下面这个例子说明:

class B { 
 public B() { 
 // do nothing 
 func(); 
 } 
 public void func() { 
 System.out.println("B.func()"); 
 } 
} 
class D extends B { 
 private int num = 1; 
 @Override 
 public void func() { 
 System.out.println("D.func() " + num); 
 } 
} 
public class Test { 
 public static void main(String[] args) { 
 D d = new D(); 
 } 
} 
// 执行结果
D.func() 0 

 在构造 D对象时也会构造B对象,在B的构造方法中调用了func()方法,然后触发了动态绑定,然后调用D中的func()方法,但此时的num处在未初始化的状态,值为0。

16.抽象类

没有实际工作的方法,只是为了让子类拥有共同的方法,这样的方法我们可以设计为抽象方法,包含抽象方法的类我们称为抽象类。

注意事项:

·抽象类使用abstract定义,抽象类是普通类的超集(普通类有的,抽象类全都有),抽象类只是比普通类多了一些抽象方法而已([0...N])。

· 抽象方法没有方法体({}),只有方法的声明。

· 抽象类可以一个抽象方法都没有,抽象类不能直接实例化对象,也无法直接new一个抽象类。

 Sharp sharp=new Sharp() ;
//编译出错
Sharp是抽象的;无法实例化

· 抽象类必须有子类,并且子类必须覆写抽象类中的所有抽象方法或也声明为抽象类

· 抽象类有构造方法,并且遵循对象实例化流程,产生子类时先调用抽象类的构造方法。

·抽象类中可以有非抽象方法。

抽象类的作用:最大的作用就是为了被继承。   抽象类本身不能被实例化,要想使用,只能创建该抽象类的子类,然后让子类重写抽象方法。

17.接口

接口中只有全局常量和抽象方法(较多)。接口是抽象类的更进一步,抽象类中还包含非抽象方法,而接口中都是抽象方法。

接口的子类的命名规范:一般,如果子类实现了一个接口,子类命名:接口名称+impl。接口一般I开头。

注意事项:

· 使用interface定义一个接口。

·  接口中的方法一定是抽象方法,因此可以省略abstract,接口中的方法一定是public,因此可以省略public。

·  使用implements继承接口,此时表达的含义不再是“扩展”,而是“实现”。

·  接口不能单独被实例化

·  子类实现接口,必须实现接口中所有的抽象方法。

· java中允许多实现(同时实现多个接口),但是不允许多继承,多接口之间可以使用extends来表示继承父接口。

·  一个类如果同时继承抽象类,实现接口,请先extends一个类而后implements多个接口。

//接口
 interface USB {
     void set();
     void work();
}

class IMouse implements USB{
    @Override
    public void set() {
        System.out.println("鼠标驱动安装成功");
    }

    @Override
    public void work() {
        System.out.println("鼠标工作正常");

    }
}

class IKeyboard implements USB{
    @Override
    public void set() {
        System.out.println("键盘驱动安装成功");
    }

    @Override
    public void work() {
        System.out.println("键盘工作正常");
    }
}

public class Computer {
     public void plugIn(USB usb){
         usb.set();
         usb.work();
     }

    public static void main(String[] args) {
         //产生一个电脑的对象
         Computer computer=new Computer() ;
         //想把鼠标和键盘连接起来
         USB mouse=new IMouse();
         USB keyboard=new IKeyboard();
         computer.plugIn(mouse);
         computer.plugIn(keyboard);
    }
}

执行结果:

18.接口使用的实例

JDK内置的两种接口:1.Comparable:具备比较的能力

给对象数组排序:

public  class Student implements Comparable<Student> {
    private String name;
    private int score;

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", score=" + score +
                '}';
    }
    public static void main(String[] args) {
        Student[] students = new Student[]{
                new Student("zs", 78),
                new Student("ls", 75),
                new Student("ne", 82),
                new Student("eg", 80),
        };

        Arrays.sort(students);
        for (Student stu : students) {
            System.out.println(stu);
        }
   @Override
    public int compareTo(Student o) {
        if(this.score>o.score){
            return -1;
        }else if(this.score<o.score){
            return 1;
        }
        return 0;
    }
}
//执行结果
Student{name='ne', score=82}
Student{name='eg', score=80}
Student{name='zs', score=78}
Student{name='ls', score=75}

 在sort方法中会自动调用compareTo方法,compareTo的参数是Object,其实就是传入的Student类型的对象,然后通过重写compareTo方法的方式,就可以定义比较规则。

compareTO的比较规则:

如果当前对象应排在参数对象之前,返回小于0的数;

如果当前对象应排在参数对象之后,返回大于0的数;

如果当前对象和参数对象不分先后,返回0;

总结:大的向前排(降序)返回小于0的数,

大的向后排(升序)返回大于0的数。

2.Cloneable:具备克隆的能力

Object类中存在一个clone方法,调用这个方法可以创建一个对象的拷贝,要想使用这个方法,就必须要先实现Clonable接口。

浅拷贝:当一个对象是通过另一个对象克隆出来的,此时这两个对象是两个独立的对象,但是这两个对象的内部包含的其他引用是相同的,这种拷贝称为浅拷贝。

public class A implements Cloneable{
    private C c=new C();
    @Override
    protected A clone() throws CloneNotSupportedException {
        A a= (A)super.clone();
        return a;
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        A a=new A();
        A b=a.clone();
        b.c.num=100;//浅拷贝
        System.out.println(a.c.num);
    }
}
class C {
    int num;
}
//执行结果
100

深拷贝:b是通过a拷贝而来的,此时a和b包含的所有其他引用也是独立的,这种拷贝称为深拷贝。

深拷贝的实现:1.嵌套实现clone()方法

public class A implements Cloneable{
    private C c=new C();
    @Override
    protected A clone() throws CloneNotSupportedException {
        A a= (A)super.clone();
        c=c.clone();
        return a;
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        A a=new A();
        A b=a.clone();
        b.c.num=100;//浅拷贝
        System.out.println(a.c.num);
    }
}
class C implements Cloneable {
    int num;
    
    @Override
    protected C clone() throws CloneNotSupportedException {
        return (C)super.clone();
    }
}
//执行结果
0

2.序列化(常用做法):现在开发中的序列化 对象--->String(json).

19.接口与抽象类比较

 20.Object类

1.Object类是java中所有类的默认父类,无需使用extends继承该类,只要是自定义类class开头的类,默认都是Object的子类。

Object类存在就是为了向上转型:Object obj=new 所有类();

2.Object类提供的equals方法:若自定义的类需要具备比较值是否相等,而非简单的比较地址,就在类中覆写这个equals方法。

所有对象的比较是否相等,不要用==比地址,使用该对象的equals()方法,包括String类

public  class Student implements Comparable<Student> {
    private String name;
    private int score;

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", score=" + score +
                '}';
    }
public boolean equals(Object o){
        //看到引用先判空
        if(o==null){
            return false;
        }else if(o instanceof Student){
            //向下转型,需要将Object类型还原为Student类型,进行Student属性的比较。
            Student stu=(Student) o;
            if(this.name.equals(stu.name)&&this.score== stu.score){
                return true;
            }else{
                return false;
            }
        }else {
            return false;
        }
}
 public static void main(String[] args) {
        Student stu1=new Student("zs",89);
        Student stu2=new Student("zs",78);
        System.out.println(stu1.equals(stu2));
  }
}
//执行结果
false

3.Java中规定Object不仅是所有类的父类,还可以使用Object来接收数组和接口。

在java中共有两类方法没有方法体,只有函数声明:

1.抽象方法:abstract修饰的方法

2.native方法:本地方法(java去调动c++代码)

21.子类调用父类构造方法

1.子类初始化时会自动调用父类的无参构造方法,若要手动调用无参构造方法,要用super()。不会自动调用父类有参构造方法,如果用到了父类的有参构造方法,则需要手动调用,手动调用父类有参构造方法之后不会调用无参构造方法。

注:不论手动调用父类的有参构造方法或无参构造方法,使用super()必须写在子类构造方法的第一行。

22.代码的执行顺序 

在Java中,静态的(代码块、静态属性 )早于实例的(构造代码块、实例属性),父类的早于子类的,构造方法最晚。括号中的不分先后,哪个 在前就先执行哪个。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值