Java面向对象编程

目录

导入包中的类

静态导入

常见的系统包

继承

语法规则

protected 关键字

 final 关键字

多态

向上转型

 方法重写

 动态绑定

 理解多态

向下转型

抽象类

语法规则:

 抽象类的作用

 接口

语法规则

 Comparable和Clonable 接口

 Comparable

 Clonable


文件夹,包(package) 是组织类的一种方式,使用包的主要目的是保证类的唯一性.

导入包中的类

导入一个类:import 包名.类名

导入包所有的类:import 包名.*

静态导入

使用 import static 可以导入包中的静态的方法和字段
例如:

import static java.lang.Math.*;

public class Test {
    public static void main(String[] args) {
        double x = 30;
        double y = 40;
        // 静态导入的方式写起来更方便一些.
        // double result = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
        double result = sqrt(pow(x, 2) + pow(y, 2));
        System.out.println(result);
    }
}

导入类中的静态域,使用这种方式可以更方便的写一些代码。

常见的系统包

  1. java.lang:系统常用基础类(String、Object),此包从JDK1.1后自动导入;
  2. java.lang.reflect:java 反射编程包;
  3. java.net:进行网络编程开发包;
  4. java.sql:进行数据库开发的支持包;
  5. java.util:是java提供的工具程序包;(非常重要)
  6. java.io:I/O编程开发包。

包访问权限: default(不需要写出来),当前包下的所有类中可见。

继承

语法规则

class 子类 extends 父类 {
}
  • 使用 extends 指定父类.
  • Java 中一个子类只能继承一个父类 (而C++/Python等语言支持多继承).
  • 子类会继承父类的所有 public 的字段和方法.
  • 对于父类的 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();
    }
}

Cat 和 Bird 继承自 Animal 类,Cat和Bird就是Animal的子类,Animal是Cat和Brid的父类。

继承访问权限:不同包下具有继承关系的类之间可见。

protected 关键字

如果把字段设为 private, 子类不能访问. 但是设成 public, 又违背了我们 "封装" 的初衷.两全其美的办法就是 protected 关键字.

  • 对于类的调用者来说, protected 修饰的字段和方法是不能访问的.
  • 对于类的 子类同一个包的其他类 来说, protected 修饰的字段和方法是可以访问的.

Java 中对于字段和方法共有四种访问权限:

  • private: 类内部能访问, 类外部不能访问.
  • 默认(也叫包访问权限): 类内部能访问, 同一个包中的类可以访问, 其他类不能访问.
  • protected: 类内部能访问, 子类和同一个包中的类可以访问, 其他类不能访问.
  • public : 类内部和类的调用者都能访问.

 final 关键字

修饰一个变量或者字段的时候, 表示 常量 (不能修改).final 关键字也能修饰类, 此时表示被修饰的类就不能被继承(即该类不会有子类).

final public class Animal {
}
public class Bird extends Animal {
}
// 编译出错
Error:java: 无法从最终Animal进行继承

多态

向上转型

举个栗子:

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

 这行代码也可以这样写:

Bird bird = new Bird("圆圆");
Animal bird2 = bird;
// 或者写成下面的方式
Animal bird2 = new Bird("圆圆");

此时 bird2 是一个父类 (Animal) 的引用, 指向一个子类 (Bird) 的实例. 这种写法称为 向上转型.

语法:

父类名称  父类引用  =  new  子类()

向上转型发生的时机:

  • 直接赋值
  • 方法传参
  • 方法返回

 方法重写

子类实现父类的同名方法, 并且参数的类型和个数完全相同, 这种情况称为 覆写/重写/覆盖(Override).

注意事项:

  1.  子类中覆写的权限 >= 父类的权限.
  2. 子类的方法返回值必须与父类保持一致(向上转型除外).
  3. 普通方法可以重写, static 修饰的静态方法不能重写.
  4. 针对重写的方法, 可以使用 @Override 注解来显式指定.

 动态绑定

 当子类和父类中出现同名方法的时候, 再去调用会出现什么情况呢?

// Animal.java
public 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);
    }
}
// Bird.java
public class Bird extends Animal {
    public Bird(String name) {
        super(name);
    }
    public void eat(String food) {
        System.out.println("我是一只小鸟");
        System.out.println(this.name + "正在吃" + food);
    }
}
// Test.java
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 和 animal2 分别调用 eat 方法, 发现animal1.eat()实际调用了父类的方法, 而animal2.eat()实际调用了子类的方法。
因此,只看new的是谁,就调用的是谁的方法。

 理解多态

多态:当一个引用在不同场景下调用相同方法表现出不同行为。

 举个栗子:

class Shape {
    public void draw() {
        // 啥都不用干
    }
}
class Cycle extends Shape {
    @Override
    public void draw() {
        System.out.println("○");
    }
}
class Rect extends Shape {
    @Override
    public void draw() {
        System.out.println("□");
    }
}
class Flower extends Shape {
    @Override
    public void draw() {
        System.out.println("♣");
    }
}
/我是分割线//
// Test.java
public class Test {
    public static void main(String[] args) {
        Shape shape1 = new Flower();
        Shape shape2 = new Cycle();
        Shape shape3 = new Rect();
        drawMap(shape1);
        drawMap(shape2);
        drawMap(shape3);
    }
    // 打印单个图形
    public static void drawShape(Shape shape) {
        shape.draw();
    }
}

当类的调用者在编写 drawMap 这个方法的时候, 参数类型为 Shape (父类), 此时在该方法内部并不知道, 也不关注当前的 shape 引用指向的是哪个类型(哪个子类)的实例. 此时 shape 这个引用调用 draw 方法可能会有多种不同的表现(和 shape 对应的实例相关), 这种行为就称为 多态。

多态顾名思义, 就是 "一个引用, 能表现出多种不同形态",就是方法重写 + 向上转型,

使用多态的好处:

  1.  类调用者对类的使用成本进一步降低.
  2. 能够降低代码的 "圈复杂度", 避免使用大量的 if - else.
  3. 可扩展能力更强(当有一个新子类产生时,由于多态性,调用者只需创建一个新类的实例就可以了).

向下转型

向上转型是子类对象转成父类对象, 向下转型就是父类对象转成子类对象

语法:

 子类  子类引用  =  (子类)父类引用

注意事项:

向下转型可能会出错,两种方法避免出错:

  1. 使用Java提供的instanceof关键字(instanceof 可以判定一个引用是否是某个类的实例. 如果是, 则返回 true. 这时再进行向下转型就比较安全)
  2. 要使用向下转型,先得发生向上转型(核心就在于使用向下转型的对象恰好是当前向上转型new出来的)
    例如:Animal a1 = new Bird();
               Bird bird = (Bird) a1;

抽象类

语法规则:

在刚才的打印图形例子中, 我们发现, 父类 Shape 中的 draw 方法好像并没有什么实际工作, 主要的绘制图形都是由Shape 的各种子类的 draw 方法来完成的. 像这种没有实际工作的方法, 我们可以把它设计成一个 抽象方法(abstractmethod), 包含抽象方法的类我们称为 抽象类(abstract class).

abstract class Shape {
    abstract public void draw();
}
  • 在 draw 方法前加上 abstract 关键字, 表示这是一个抽象方法. 同时抽象方法没有方法体(没有 { }, 不能执行具体代码).
  • 对于包含抽象方法的类, 必须加上 abstract 关键字表示这是一个抽象类.

注意事项:

  1.  抽象类使用abstract定义,并且抽象类是普通类的超集(普通类有的东西,抽象类都有).
  2. 抽象类只是比普通类多了一个方法而已.
  3. 抽象方法使用abstract定义,只有方法的声明,没有方法体.

 抽象类的作用

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

 接口

接口是抽象类的更进一步. 抽象类中还可以包含非抽象方法, 和字段. 而接口中包含的方法都是抽象方法, 字段只能包含静态常量.

语法规则

  • 使用 interface 定义一个接口.

  • 接口中的方法一定是抽象方法, 因此可以省略 abstract.

  • 接口中的方法一定是 public, 因此可以省略 public.

  • 使用 implements 继承接口. 此时表达的含义不再是 "扩展", 而是 "实现".

  • 在调用的时候同样可以创建一个接口的引用, 对应到一个子类的实例.

  • 接口不能单独被实例化. 

注意事项:

  1.  我们创建接口的时候, 接口的命名一般以大写字母 I 开头.
  2. 接口的命名一般使用 "形容词" 词性的单词.
  3. 阿里编码规范中约定, 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性.
  4. 子类命名:接口名称 + impl.
  5. 多个接口之间可以用extends继承父接口.
  6. 一个类如果同时继承抽象类,实现接口,先extends一个类,然后implements多个接口.
  7. JDK8之后,接口中也允许出现普通方法.

 Comparable和Clonable 接口

Java 中内置了一些很有用的接口, 例如Comparable和Clonable 。

 Comparable

Comparable 接口中的CompareTo方法:返回值int

  • 如果当前对象应排在参数对象之前, 返回小于 0 的数字;
  • 如果当前对象应排在参数对象之后, 返回大于 0 的数字;
  • 如果当前对象和参数对象不分先后, 返回 0;

 Clonable

表示克隆的能力。

浅拷贝:

当一个对象是通过另一个对象clone出来的.此时这两个对象是独立的两个对象,但是这两个对象的内部包含的其他一弄是相同的,这种拷贝成为浅拷贝。

Cloneable 拷贝出的对象是一份 "浅拷贝"
看以下代码:

public class Test {
    static class A implements Cloneable {
        public int num = 0;
        @Override
        public A clone() throws CloneNotSupportedException {
        return (A)super.clone();
    }
}
static class B implements Cloneable {
    public A a = new A();
    @Override
    public B clone() throws CloneNotSupportedException {
        return (B)super.clone();
    }
}
public static void main(String[] args) throws CloneNotSupportedException {
    B b = new B();
    B b2 = b.clone();
    b.a.num = 10;
    System.out.println(b2.a.num);
    }
}
// 执行结果
10

通过 clone 拷贝出的 b 对象只是拷贝了 b 自身, 而没有拷贝内部包含的 a 对象. 此时 b 和 b2 中包含的 a 引用仍然是指向同一个对象. 此时修改一边, 另一边也会发生改变.

深拷贝:

A是通过B拷贝来的,此时A和B包含的所有其他引用也是独立的,这种拷贝成为深拷贝。

深拷贝可以通过序列化或者嵌套实现clone方法来实现。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

澄白易

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值