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
只是为了写代码的时候更方便
.
import
更类似于
C++

namespace

using

静态导入

使用 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 = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
        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.final和abstract是不同同时存在的,抽象方法不能被private和static修饰!
9.当一个抽象类A不想被一个普通类B继承,此时可以把B这个类变成抽象类,那么再当一个普通类C继承这个抽象类B之后,C要重写B和A里面所有的抽象方法。 

注意事项:

1)
抽象类不能直接实例化

Shape shape = new Shape(); 

// 编译出错
Error:(30, 23) java: Shape是抽象的; 无法实例化

2)
抽象方法不能是
private

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

// 编译出错
Error:(4, 27) java: 非法的修饰符组合: abstract和private

3)
抽象类中可以包含其他的非抽象方法
,
也可以包含字段
.
这个非抽象方法和普通方法的规则都是一样的
,
可以被重写
, 也可以被子类直接调用

abstract class Shape { 
 abstract public void draw(); 
     void func() { 
     System.out.println("func"); 
 } 
} 
class Rect extends Shape { 
     ... 
} 
public class Test { 
 public static void main(String[] args) { 
     Shape shape = new Rect(); 
     shape.func(); 
 } 
} 

// 执行结果
func

4)抽象类不一定有抽象方法,但有抽象方法的类一定是抽象类。

5)抽象类中可以有构造方法,供子类创建对象时,初始化父类的成员变量。

抽象类的作用

抽象类存在的最大意义就是为了被继承
.

抽象类本身不能被实例化
,
要想使用
,
只能创建该抽象类的子类
.
然后让子类重写抽象类中的抽象方法
.

接口

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

语法规则

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 Rect(); 
         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; 
}

 其中的 public, static, final 的关键字都可以省略. 省略后的 num 仍然表示 public 的静态常量。

总结

1.使用interface来定义一个接口
⒉.接口当中的成员变量默认是public static final的,一般情况下我们不写

3.接口当中的成员方法默认是public abstact ,一般情况下我们不写

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 { 
 ...
}

通过接口继承创建一个新的接口
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(Object o) {
//        Student s=(Student)o;
//        return this.score-s.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);
    }
}

运行结果

与我们预期的19.9    99.99不符,显然是因为这里是浅拷贝,因此我们需要对m实现深拷贝。

原因如下图:

实现深拷贝后

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 age) {
        this.age = age;
        this.m = new Money();
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person tmp = (Person) super.clone();
        tmp.m = (Money) this.m.clone();
        return tmp;
    }

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

运行结果:

实现方法如下图:

抽象类和接口的区别

核心区别: 抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中不能包含普通方法, 子类必须重写所有的抽象方法.

作者:新绿MEHO

物联沃分享整理
物联沃-IOTWORD物联网 » Java之包,抽象类,接口

发表回复