Java抽象类,接口和多态相关

抽象类

如果一个类中没有包含足够的信息来描述一个具体的对象,这样的类就是抽象类。
抽象类除了不能实例化对象,类的其他功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
由于抽象类不能被实例化,所以必须被继承,通常用于表示 is - a 关系。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/* 文件名 : Employee.java */
public abstract class Employee
{
private String name;
private String address;
private int number;
public Employee(String name, String address, int number)
{
System.out.println("Constructing an Employee");
this.name = name;
this.address = address;
this.number = number;
}
public double computePay()
{
System.out.println("Inside Employee computePay");
return 0.0;
}
public void mailCheck()
{
System.out.println("Mailing a check to " + this.name
+ " " + this.address);
}
public String toString()
{
return name + " " + address + " " + number;
}
public String getName()
{
return name;
}
public String getAddress()
{
return address;
}
public void setAddress(String newAddress)
{
address = newAddress;
}
public int getNumber()
{
return number;
}
}

/* 文件名 : AbstractDemo.java */
public class AbstractDemo
{
public static void main(String [] args)
{
/* 以下是不允许的,会引发错误 */
Employee e = new Employee("George W.", "Houston, TX", 43);

System.out.println("\n Call mailCheck using Employee reference--");
e.mailCheck();
}
}


抽象方法

如果你想设计这样一个类,该类包含一个特别的成员方法,该方法的具体实现由它的子类确定,那么你可以在父类中声明该方法为抽象方法。
Abstract 关键字同样可以用来声明抽象方法,抽象方法只包含一个方法名,而没有方法体。
抽象方法没有定义,方法名后面直接跟一个分号,而不是花括号。

1
2
3
4
5
6
7
8
9
10
public abstract class Employee
{
private String name;
private String address;
private int number;

public abstract double computePay();

//其余代码
}


抽象类总结规定

  1. 抽象类不能被实例化,如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。
  2. 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
  3. 抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。
  4. 构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。
  5. 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。

接口

接口是java中的一个抽象类型,通常以interface声明,一个类通过实现接口的方式从而继承接口的抽象方法。
接口无法被实例化,但可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则必须声明为抽象类,在被实现时通常表示 has - a 关系。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/* 文件名 : Animal.java */
interface Animal {
public void eat();
public void travel();
}

/* 文件名 : MammalInt.java */
public class MammalInt implements Animal{

public void eat(){
System.out.println("Mammal eats");
}

public void travel(){
System.out.println("Mammal travels");
}

public int noOfLegs(){
return 0;
}

public static void main(String args[]){
MammalInt m = new MammalInt();
m.eat();
m.travel();
}
}


接口与类相似点:

  • 一个接口可以有多个方法。
  • 接口文件保存在 .java 结尾的文件中,文件名使用接口名。
  • 接口的字节码文件保存在 .class 结尾的文件中。
  • 接口相应的字节码文件必须在与包名称相匹配的目录结构中。

接口与类的区别:

  • 接口不能用于实例化对象。
  • 接口没有构造方法。
  • 接口中所有的方法必须是抽象方法。
  • 接口不能包含成员变量,除了 static 和 final 变量。
  • 接口不是被类继承了,而是要被类实现。
  • 接口支持多继承。

接口特性

  • 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
  • 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
  • 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。

抽象类和接口的区别

  1. 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
  2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
  3. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
  4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。

标记接口

最常用的继承接口是没有包含任何方法的接口。
标记接口是没有任何方法和属性的接口.它仅仅表明它的类属于一个特定的类型,供其他代码来测试允许做一些事情。
标记接口作用:简单形象的说就是给某个对象打个标(盖个戳),使对象拥有某个或某些特权。
例如:java.awt.event 包中的 MouseListener 接口继承的 java.util.EventListener 接口定义如下:

1
2
3
package java.util;
public interface EventListener
{}

没有任何方法的接口被称为标记接口。标记接口主要用于以下两种目的:

  • 建立一个公共的父接口:
    正如EventListener接口,这是由几十个其他接口扩展的Java API,你可以使用一个标记接口来建立一组接口的父接口。例如:当一个接口继承了EventListener接口,Java虚拟机(JVM)就知道该接口将要被用于一个事件的代理方案。
  • 向一个类添加数据类型:
    这种情况是标记接口最初的目的,实现标记接口的类不需要定义任何接口方法(因为标记接口根本就没有方法),但是该类通过多态性变成一个接口类型。

多态

同一个行为具有多个不同表现形式或形态的能力,即同一个接口,使用不同的实例而执行不同操作。
便利性:可以使程序有良好的扩展,消除类型之间的耦合关系,并可以对所有类的对象进行通用处理,省去许多重复代码。

多态的优点

  1. 消除类型之间的耦合关系
  2. 可替换性
  3. 可扩充性
  4. 接口性
  5. 灵活性
  6. 简化性

    多态存在的三个必要条件

  • 继承
  • 重写
  • 父类引用指向子类对象

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
abstract class Animal {  
abstract void eat();
}

class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
public void voice() {
System.out.println("喵喵");
}
}

class Dog extends Animal {
public void eat() {
System.out.println("吃肉");
}
public void voice() {
System.out.println("汪汪");
}
}

public class Test{
public static void main(String[] args){
Cat cat = new Cat();
cat.eat();
cat.voice();
Dog dog = new Dog();
dog.eat();
dog.voice();
}
}


虚函数

虚函数的存在是为了多态。
Java 中其实没有虚函数的概念,它的普通函数就相当于 C++ 的虚函数,动态绑定是Java的默认行为。如果 Java 中不希望某个函数具有虚函数特性,可以加上 final 关键字变成非虚函数。

Java面向对象相关知识点

Java面向对象的三大特征为:封装、继承和多态。

1. 封装

Java中的封装是指一个类把自己内部的实现细节进行隐藏,只暴露对外的接口(比如setter和getter方法)。封装又可以分为属性和方法的封装。把属性定义为private,使它们只能被setter和getter方法来进行设定和获取。或将方法定义为private,使他们只能在所在类内部被调用。下面是例子:

lang: java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Person {
private in id;
private String name;
Private Person person;

public int getId(){
return id;
}

public String getName(){
return name;
}

public Person getPerson(){
return person;
}

public void setId(int id){
this.id = id;
}

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

public void setPerson(Person person){
this.person = person;
}
}

在Person类中,定义了三个成员变量,分别为id name person,它们的访问修饰都是private私有的,通过setter和getter方法对这些变量进行设置以及取值。这么做的好处就是增强类的信息隐藏与模块化,可以防止该类的代码和数据被外部类定义的代码随机访问,减少耦合性,便于代码的理解和维护,也增加了代码的安全性。

2. 继承

Java中的继承是指在一个现有类的基础上再构建一个新类,其中现有类即父类,新类即子类。子类可以拥有父类的成员变量以及方法(但没有父类的构造器)。对于父类中的public和protected成员,子类可以访问、使用和重写。继承可以降低代码的重复性,但是会提高代码的耦合性。

父类:

lang: java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.company.test

public class Person{
private int a = 1;
private String name;
private int 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;
}

@Override
public String toString() {
return "Person ["name=" + name + ", age=" + age + "]";
}


public void say() {
System.out.println("person say..");
}

//父类的私有方法
private void show() {
System.out.println("person show...");
}

}

子类:

lang: java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.company.test

public class Student extends Person{
@Override
public void say(String content){
super.say();
System.out.println(content);
}

public static void main(String[] args){
Student stu = new Student();
//sut.a=2;//子类对象对父类的私有成员变量使用,报错。
//stu.show();//子类对象调用父类的私有方法,报错。
stu.say();
}
}

继承可以实现代码的复用和扩展,通过对父类代码的复用,不需要重新声明父类中的成员变量,对父类的方法进行重写体现了扩展。

多态

多态即多种状态,就是说同一个行为具有多个不同表现形式或形态的能力。
多态就是同一个借口,使用不同的实例而执行不同操作。

多态存在的三个必要条件

  • 继承
  • 重写
  • 父类引用指向子类对象
    比如:
    1
    Parent p = new Child();

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
多态的好处:可以使程序由良好的扩展,并可以对所有类的对象进行通用处理。
例子:

lang: java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class Test{
public static void main(String[] args){
show(new Cat()); //以Cat对象调用show方法
show(new Dog()); //以Dog对象调用show方法

Animal a = new Cat();
a.eat(); //此处调用的是Cat的eat
Cat c = (Cat)a;
c.work();
}

public static void show(Animal a){
a.eat();
if(a instanceof Cat){
Cat c = (Cat)a;
c.work();
}else if (a instanceof Dog){
Dog c = (Dog)a;
c.work();
}
}
}

abstract class Animal{
abstract void eat();
}

class Cat extends Animal{
public void eat(){
System.out.println("吃鱼");
}
public void work(){
System.out.println("抓老鼠");
}
}

class Dog extends Animal{
public void eat(){
System.out.println("吃骨头");
}
public void work(){
System.out.println("看家");
}
}

执行后的输出结果为:
1
2
3
4
5
6
吃鱼
抓老鼠
吃骨头
看家
吃鱼
抓老鼠

多态的实现方式

方式一:重写

方式二:接口

    1. 生活中的接口最具代表性的就是插座,例如一个三接头的插座都能接在三孔插座中,因为这是每个国家都有各自规定的接口规则,有可能到国外就不行,那是因为国外自己定义的接口类型。
    1. java中的接口类似于生活中的接口,就是一些方法特征的集合,但没有方法的实现。

方式三:抽象类和抽象方法

Java解决在nextInt()后使用nextLine()的方法

Ref: Scanner is skipping nextLine() after using next() or nextFoo()?

如果在nextInt()方法之后立即使用nextLine()方法,请记住nextInt()读取整数标记; 因此,该行整数输入的最后一个换行符仍在输入缓冲区中排队,下一个nextLine()将读取整数行的剩余部分(为空)。

  • 在调用了nextInt()后,我们可以先调用一次nextLine(),将该行剩下的内容抛弃;

    1
    2
    3
    int i = input.nextInt();
    input.nextLine();//抛弃输入整数时最后的换行符
    String str = input.nextLine();
  • 全部都使用nextLine()读入,然后将其读入的数据转换为Integer。

    1
    2
    3
    4
    5
    6
    7
    int i = 0;
    try {
    i = Interger.prseInt(input.nextLin());
    } catch (NumberFormatException e){
    e.printStackTrace();
    }
    String str = input.nextLine();

Java基本语法及例子

Java程序可以看成是一系列对象的集合,他们之间彼此调用,完成工作。下面简单介绍类、对象、方法和实例变量的概念。

  • 对象:对象是类的一个实例,有状态和行为。
  • 类:类是一个模板,它描述一类对象的行为和状态。
  • 方法:方法就是行为,一个类可以有很多方法。所有动作都是在方法中完成的。
  • 实例变量:每个对象都有独特的实例变量,对象的状态由这些实例变量的值决定。

第一个Java程序

下面的程序将打印字符串 Hello World

1
2
3
4
5
6
 
public class HelloWorld{
public static void main(String []args){
System.out.println("Hello World");
}
}

基本语法

编写Java程序时,应注意以下几点:

  • 大小写敏感:Java对大小写敏感,即Hello与hello不同。
  • 类名:类名的首字母应该大写。若类名含有若干单词,那每个首字母应该大写,例如HelloWorld。
  • 方法名:所有的方法名都应该以小写字母开头。若方法名含有若干单词,则后面每个单词首字母大写。
  • 源文件名:必须和类名相同,后缀为.java。(若类名和文件名不同会导致编译错误)。
  • 主方法入口:所有的Java程序由 public static void main(String []args) 方法开始执行。

Java标识符

Java所有的组成部分都需要名字。类名、变量名以及方法名都被成为标识符。
一下有几点需要注意:

  • 所有的标识符都应该以字母(A-Z 或者 a-z),美元符($)、或者下划线(_)开始
  • 首字母之后可以是字母、美元符、下划线或数字的任何字符组合
  • 关键字不能用作标识符
  • 标识符大小写敏感
  • 非法标识符举例:123abc、-dog

Java修饰符

Java可以使用修饰符来修饰类中方法和属性。主要有两类修饰符:

  • 访问控制修饰符:default, public, protected, private
  • 非访问控制修饰符:final, abstract, static, synchronized

其中对于访问控制修饰符,可以列出如下表格:

publicprotecteddefaultprivate
同一个类
同一个包×
子父类××
不同包×××

Java变量

Java中主要有如下几种类型的变量

  • 局部变量
  • 类变量(静态变量)
  • 成员变量(非静态变量)

Java数组

数组是储存在堆上的对象,可以保存多个同类型变量。

Java枚举

枚举限制变量只能是预先设定好的值。使用枚举可以减少程序出现错误的可能。
比如服装店中有S、M、L、XL四个尺码,设计一个程序限制顾客只能从这四个尺码中选。

实例

1
2
3
4
5
6
7
8
9
10
11
 
class Clothes{
enum ClothesSize{S,M,L,XL}
ClothesSize size;
}
public class SizeTest{
public static void main(String []args){
Clothes t_shirt = new Clothes();
t_shirt.size = Clothes.ClothesSize.L;
}
}

注意:枚举可以单独声明或者声明在类里面。方法、变量、构造函数也可以在枚举中定义。

Java注释

类似于C/C++,Java支持单行以及多行注释,注释中的字符将被Java编译器忽略。

1
2
3
4
//这是一个单行注释

/*这是一个
多行注释*/