Java中包、抽象类及接口的探索

2个月前发布 gsjqwyl
11 0 0

文章标题:

Java中包、抽象类与接口的深入剖析

文章内容:

目录

  • 导入包
  • 静态导入
  • 将类放入包
  • 常见的系统包
  • 抽象类
  • 语法规则
  • 注意事项
  • 抽象类的作用
  • 接口
  • 实现多个接口
  • 接口间的继承
  • 接口使用实例
  • (法一)实现Comparable接口的compareTo()方法
  • (法二)实现Comparator比较器的compare()方法
  • Clonable接口和深拷贝
  • 抽象类和接口的区别

包(package)是组织类的一种方式,其核心作用是保证类的唯一性。举个例子,假如你在代码中定义了一个名为Test的类,而你的同事也可能定义一个同名的Test类,若出现两个同名类,就会引发冲突,导致代码无法成功编译。

导入包

Java已经预置了大量可供使用的现成类。例如:

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

上述代码通过java.util.Date的方式引入了java.util包中的Date类。不过这种写法相对繁琐,我们可以借助import语句来导入包。若需要使用java.util中的其他类,可采用import java.util.*的方式,但更建议明确指定要导入的类名,以此避免冲突情况的发生。需要留意的是,import和C++的#include存在显著差异,C++必须通过#include引入其他文件内容,而Java无需如此,import主要是为了让编写代码的过程更为便捷,其更类似于C++的namespaceusing

静态导入

利用import static能够导入包中的静态方法和字段。例如:

import static java.lang.System.*;
public class Test {
    public static void main(String[] args) {
        out.println("hello");
    }
}

借助这种方式能够更便捷地编写代码,比如:

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);
    }
}
将类放入包

基本规则如下:在文件的最上方添加package语句来指定该代码所属的包。包名通常建议采用公司域名的颠倒形式,例如com.bit.demo1。并且包名需要与代码路径相匹配,比如创建com.bit.demo1包,就会对应存在一个com/bit/demo1的路径来存储代码。若一个类没有package语句,那么该类会被放置在默认包中。

常见的系统包
  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编程开发的包。

抽象类
语法规则

对于没有实际功能的方法,我们可以把它设计成抽象方法(abstract method),包含抽象方法的类被称作抽象类(abstract class)。例如:

abstract class Shape {
    abstract public void draw();
}

draw方法前添加abstract关键字,就表示这是一个抽象方法,抽象方法没有方法体(没有{},无法执行具体代码)。对于包含抽象方法的类,必须使用abstract关键字来表明它是一个抽象类。

总结如下:
1. 抽象类由abstract修饰。
2. 被abstract修饰的方法是抽象方法,该方法可以没有具体实现。
3. 当类中存在抽象方法时,该类必须用abstract修饰。
4. 抽象类中可以有与普通类一样的成员变量和成员方法。
5. 抽象类不能直接实例化。
6. 抽象类存在的意义在于被继承。
7. 普通类继承抽象类时,必须重写抽象类中的所有抽象方法。
8. finalabstract不能同时存在,抽象方法不能用privatestatic修饰。
9. 若抽象类A不想被普通类B继承,可将B设为抽象类,当普通类C继承抽象类B后,C需要重写B和A中的所有抽象方法。

注意事项
  1. 抽象类不能直接实例化,例如:
Shape shape = new Shape(); 
// 编译出错:Error:(30, 23) java: Shape是抽象的; 无法实例化
  1. 抽象方法不能是private的,例如:
abstract class Shape { 
    abstract private void draw(); 
} 
// 编译出错:Error:(4, 27) java: 非法的修饰符组合: abstract和private
  1. 抽象类中可以包含非抽象方法和字段,非抽象方法的规则与普通方法一致,可被重写,也能被子类直接调用,例如:
abstract class Shape { 
    abstract public void draw(); 
    void func() { 
        System.out.println("func"); 
    } 
} 
class Rect extends Shape { 
    // 实现draw方法
} 
public class Test { 
    public static void main(String[] args) { 
        Shape shape = new Rect(); 
        shape.func(); 
    } 
} 
// 执行结果:func
  1. 抽象类不一定包含抽象方法,但包含抽象方法的类必定是抽象类。
  2. 抽象类可以拥有构造方法,用于子类创建对象时初始化父类的成员变量。
抽象类的作用

抽象类存在的最主要意义是被继承。由于抽象类本身不能实例化,所以要使用它,只能创建该抽象类的子类,然后让子类重写抽象类中的抽象方法。


接口

接口是更为进阶的抽象类。抽象类中能够包含非抽象方法和字段,而接口中包含的方法全是抽象方法,字段只能是静态常量。

语法规则如下:

interface IShape { 
    void draw(); 
} 
class Cycle implements IShape { 
    @Override 
    public void draw() { 
        System.out.println("○"); 
    } 
} 
public class Test { 
    public static void main(String[] args) { 
        IShape shape = new Cycle(); 
        shape.draw(); 
    } 
}
  1. 使用interface定义接口。
  2. 接口中的方法默认是抽象方法,因此可以省略abstract
  3. 接口中的方法默认是public的,所以可以省略public
  4. Cycle通过implements继承接口,这里表达的含义并非“扩展”,而是“实现”。
  5. 调用时同样可以创建接口的引用,指向子类的实例。
  6. 接口不能单独实例化。

接口中只能包含抽象方法,对于字段而言,接口中只能是静态常量(final static)。例如:

interface IShape { 
    void draw(); 
    public static final int num = 10; 
}

其中publicstaticfinal关键字都可以省略,省略后的num依然表示public的静态常量。

总结:
1. 使用interface定义接口。
2. 接口中的成员变量默认是public static final的,一般情况下可不显式写出。
3. 接口中的成员方法默认是public abstract的,通常也可不显式写出。
4. 接口中不存在普通方法。
5. Java8开始允许在接口中定义default方法,该方法可以有具体实现。
6. 接口中如果是static修饰的方法,可以有具体实现。
7. 接口不能通过new关键字实例化。
8. 类和接口之间通过implements关键字实现接口。
9. 接口也能发生向上转型和动态绑定。
10. 类实现接口中的方法时,方法不能没有public修饰。
11. 接口中没有构造方法和代码块。
12. 接口也会生成独立的字节码文件。


实现多个接口

有时需要让一个类同时继承多个父类,在有些编程语言中通过多继承的方式实现,但Java只支持单继承,一个类只能extends一个父类,但可以同时实现多个接口,达到类似多继承的效果。下面通过类来表示一组动物:

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

再提供一组接口,分别表示“会飞的”“会跑的”“会游泳的”:

interface IFlying { 
    void fly(); 
} 
interface IRunning { 
    void run(); 
} 
interface ISwimming { 
    void swim(); 
}

接着创建几个具体的动物:
– 猫,是会跑的:

class Cat extends Animal implements IRunning { 
    public Cat(String name) { 
        super(name); 
    } 
    @Override 
    public void run() { 
        System.out.println(this.name + "正在用四条腿跑"); 
    } 
}
  • 鱼,是会游的:
class Fish extends Animal implements ISwimming { 
    public Fish(String name) { 
        super(name); 
    } 
    @Override 
    public void swim() { 
        System.out.println(this.name + "正在用尾巴游泳"); 
    } 
}
  • 青蛙,既能跑,又能游(两栖动物):
class Frog extends Animal implements IRunning, ISwimming { 
    public Frog(String name) { 
        super(name); 
    } 
    @Override 
    public void run() { 
        System.out.println(this.name + "正在往前跳"); 
    } 
    @Override 
    public void swim() { 
        System.out.println(this.name + "正在蹬腿游泳"); 
    } 
}
  • 鸭子,水陆空三栖:
class Duck extends Animal implements IRunning, ISwimming, IFlying {
    public Duck(String name) {
        super(name);
    }
    @Override
    public void fly() {
        System.out.println(this.name + "正在用翅膀飞");
    }
    @Override
    public void run() {
        System.out.println(this.name + "正在用两条腿跑");
    }
    @Override
    public void swim() {
        System.out.println(this.name + "正在漂在水上");
    }
}

上述代码展示了Java面向对象编程中常见的用法:一个类继承一个父类,同时实现多种接口。继承体现的是is – a语义,而接口体现的是具有xxx特性。比如猫是一种动物,具有会跑的特性;青蛙也是一种动物,既能跑又能游泳;鸭子也是一种动物,既能跑、游还能飞。


接口间的继承

接口可以继承另一个接口,以实现复用效果,使用extends关键字。例如:

interface IRunning {
    void run();
}
interface ISwimming {
    void swim();
}
// 两栖的动物,既能跑,也能游
interface IAmphibious extends IRunning, ISwimming {

}
class Frog implements IAmphibious { 
    // 需实现run和swim方法
}

通过接口继承创建新接口IAmphibious表示“两栖的”,此时实现该接口的Frog类需要实现run方法和swim方法。


接口使用实例

给对象数组排序

class Student{
    private String name;
    private int score;
    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
    @Override
    public String toString() {
        return "[" + this.name + ":" + this.score + "]";
    }
}
public class test {
    public static void main(String[] args) {
        Student[] students = new Student[] {
                new Student("张三", 95),
                new Student("李四", 96),
                new Student("王五", 97),
                new Student("赵六", 92),
        };
        Arrays.sort(students);
        System.out.println(Arrays.toString(students));
    }
}

运行时会抛出异常,原因是对学生对象进行排序不像对整数那样能直接比大小,所以需要实现Comparable接口并实现其compareTo方法。

(法一)实现Comparable接口的compareTo()方法
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 "[" + this.name + ":" + this.score + "]";
    }
    @Override
    public int compareTo(Student o) {
        return this.score - o.score;
    }
}
public class test {
    public static void main(String[] args) {
        Student[] students = new Student[] {
                new Student("张三", 95),
                new Student("李四", 96),
                new Student("王五", 97),
                new Student("赵六", 92),
        };
        Arrays.sort(students);
        System.out.println(Arrays.toString(students));
    }
}

运行结果会按分数排序输出。

(法二)实现Comparator比较器的compare()方法
class Student{
    public String name;
    public int score;
    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
    @Override
    public String toString() {
        return "[" + this.name + ":" + this.score + "]";
    }
}
class AgeComparator implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {
        return o1.score - o2.score;
    }
}
class NameComparator implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {
        return o1.name.compareTo(o2.name);
    }
}
public class test {
    public static void main(String[] args) {
        Student student1 = new Student("zhangsan",10);
        Student student2 = new Student("lisi",15);
        AgeComparator ageComparator = new AgeComparator();
        System.out.println(ageComparator.compare(student1, student2));
        NameComparator nameComparator = new NameComparator();
        System.out.println(nameComparator.compare(student1,student2));
    }
}

运行会根据不同的比较器输出相应的比较结果。


Clonable接口和深拷贝

Java中内置了一些有用的接口,Clonable就是其中之一。Object类中有一个clone方法,调用该方法可以创建一个对象的拷贝,但要合法调用clone方法,必须先实现Clonable接口,否则会抛出CloneNotSupportedException异常。

class Money{
    public double money = 19.9;
}
class Person implements Cloneable{
    public int age;
    public Money m;
    public Person(int age) {
        this.age = age;
        this.m = new Money();
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    @Override
    public String toString() {
        return "Person{" +
               " age=" + age +
               '}';
    }
}
public class Test2 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person(10);
        Person person2 = (Person)person1.clone();
        System.out.println(person1.m.money);
        System.out.println(person2.m.money);
        System.out.println("==========================");
        person2.m.money = 99.99;
        System.out.println(person1.m.money);
        System.out.println(person2.m.money);
    }
}

运行结果与预期不符,这是因为这是浅拷贝,修改person2m会影响person1m,所以需要对m实现深拷贝。

实现深拷贝后:

“`java
class Money implements Cloneable{
public double money = 19.9;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Person implements Cloneable{
public int age;
public Money m;
public Person(int

© 版权声明

相关文章

没有相关内容!

暂无评论

none
暂无评论...