一、类

Java是一门面向对象的编程语言,除了基本数据类型以外,Java要求每一个数据类型必须都是一个类。

类是对某一类事物的描述,是抽象的、概念上的定义;对象是实际存在的该类事物的个体,因而也称实例(Instance)。类和对象就如同概念和实物之间的关系一样,类就好比是一个模板,而对象就是该模板下的一个实例。

(一)类的基本用法

这部分应该大家都会有所涉猎,大家可以选择性看一下

1.类的定义

class 类名称 {
//声明成员变量
//声明成员方法
}

2.对象声明

类定义完成之后,肯定无法直接使用。如果要使用,必须依靠对象,那么由于类属于引用数据类型,所以对象的产生格式(两种格式)如下:
(1)格式一:声明并实例化对象

类名称 对象名称 = new 类名称 () ;

(2)格式二:先声明对象,然后实例化对象:

类名称 对象名称 = null ;
对象名称 = new 类名称 () ;

引用数据类型与基本数据类型最大的不同在于:引用数据类型需要内存的分配和使用。所以,关键字new的主要功能就是分配内存空间,也就是说,只要使用引用数据类型,就要使用关键字new来分配内存空间

当一个实例化对象产生之后,可以按照如下的方式进行类的操作:
对象.属性:表示调用类之中的属性;
对象.方法():表示调用类之中的方法。

3. 存储位置

在java中的内存存储方式是这样的:
(1)堆内存:保存对象的属性内容。堆内存需要用new关键字来分配空间;
(2)栈内存:保存的是堆内存的地址(在这里为了分析方便,可以简单理解为栈内存保存的是对象的名字)。

(二)类的特性

类的三大特征为:继承、多态、封装。另外他还具有其他一些属性。

1.继承

在下面代码中,Person为父类,Man为继承了Person的子类。继承的方法是在子类声明时加上一个关键词extends 父类。

class Person {
    public Person() {
         
    }
}
 
class Man extends Person {
    public Man() {
         
    }
}

继承后子类与父类的关系包括:
(1)子类拥有父类非 private 的属性、方法。
(2)子类可以拥有自己的属性和方法,即子类可以对父类进行扩展,即重写函数。(若子类要调用父类中的同名函数,可以使用super代指父类)

重写的条件:

a.参数列表与被重写方法的参数列表必须完全相同。
b.返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5及更早版本返回类型要一样,java7 及更高版本可以不同)。
c.访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
d.父类的成员方法只能被它的子类重写。
e.声明为 final 的方法不能被重写。
f.声明为 static 的方法不能被重写,但是能够被再次声明。
g.子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final的方法。 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
h.重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
i.构造方法不能被重写。

(3)子类可以用自己的方式实现父类的方法。
(4)Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 A 类继承 B 类,B 类继承 C 类,所以按照关系就是 C 类是 B 类的父类,B 类是 A 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。
(5)提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。

2.多态

多态是指,同一个方法的调用,因为对象的不同有不同的行为。

Java实现多态有三个必要条件:继承、重写、向上转型。

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。

多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。

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是animal的子类,调用的是 Cat 的 eat
      Cat c = (Cat)a;        // 向下转型  
      c.work();        // 调用的是 Cat 的 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("看家");  
    }  
}

3.封装

封装是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法,利用的是不同函数的权限设置。

封装具有以下优点:
a. 良好的封装能够减少耦合。

b. 类内部的结构可以自由修改。

c. 可以对成员变量进行更精确的控制。

d. 隐藏信息,实现细节。

对于函数的权限控制有以下三种关键词:public private protected
public:所有用户均可调用
protected:包内可见
private:仅自己类可见

附赠几个常见的函数修饰词区分:
public、private、protected、default、static、final

  1. final:定义不可修改的变量、方法和类
    常量修饰符
    修饰变量不可变
    修饰方法,则该方法不能被子类重写
    修饰类,不能被继承
    2.private、protected、public:
    修饰函数的可见权限
    3.static:静态,修饰函数、方法存在的周期
    被修饰的函数、方法属于类,而非对象,只有当整个类都被丢弃时,才会释放。

4.抽象

抽象就是只有定义,没有实现的类或方法。关键字是abstract。
注意:
(1)包含抽象方法的类必须是抽象类,但是抽象类中也可以有普通的方法。

public abstract class Employee
{
   private String name;
   private String address;
   private int number;
   
   public abstract double computePay();
   
   //其余代码
}

(2)抽象方法在父类中虽然没有实现,但是在其子类中必须实现。

public class Salary extends Employee
{
   private double salary; // Annual salary
  
   public double computePay()
   {
      System.out.println("Computing salary pay for " + getName());
      return salary/52;
   }
 
   //其余代码
}

抽象类的使用具有以下要求:
a. 抽象类不能被实例化(初学者很容易犯的错),如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。

b. 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。

c. 抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。

d. **构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。**因为它不能被继承

e. 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。

二、接口

(一)什么是接口

接口是比抽象类更抽象的类,其中不包含任何的函数实现。关键字为Interface,不用class。接口实现了规范与具体实现的分离。

/* 文件名 : NameOfInterface.java */
import java.lang.*;
//引入包
 
public interface NameOfInterface
{
   //任何类型 final, static 字段
   //抽象方法
}

接口具有以下特性:
1.接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字
2.接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。
3.接口中的方法都是公有的

(二)接口的实现

当类实现接口的时候,类要实现接口中所有的方法。否则,类必须声明为抽象的类。
类使用implements关键字实现接口。在类声明中,Implements关键字放在class声明后面
实现一个接口的语法,可以使用这个公式:

...implements 接口名称[, 其他接口名称, 其他接口名称..., ...] ...

接口中的变量会被隐式地指定为public static final变量(并且只能是public static final变量,用private修饰会报编译错误),而方法会被隐式地指定为public abstract方法且只能是public abstract方法(用其他关键字,比如private、protected、static、 final等修饰会报编译错误)

重写接口中声明的方法时,需要注意以下规则:
1.类在实现接口的方法时,不能抛出强制性异常,只能在接口中,或者继承接口的抽象类中抛出该强制性异常。
2.类在重写方法时要保持一致的方法名,并且应该保持相同或者相兼容的返回值类型。
3.如果实现接口的类是抽象类,那么就没必要实现该接口的方法。

在实现接口的时候,也要注意一些规则:
1.一个类可以同时实现多个接口。
2.一个类只能继承一个类,但是能实现多个接口。
3.一个接口能继承另一个接口(接口之间是可继承的,这与类是一样的),这和类之间的继承比较相似。

***三、抽象类与接口的区别

1.语法层面上的区别

1)抽象类可以提供某些非abstract的成员方法的实现细节,而接口中只能存在public abstract 方法;

2)抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的;

3)接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;

4)一个类只能继承一个抽象类,而一个类却可以实现多个接口。

2.设计层面上的区别

1)抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类**局部(行为)**进行抽象。举个简单的例子,飞机和鸟是不同类的事物,但是它们都有一个共性,就是都会飞。那么在设计的时候,可以将飞机设计为一个类Airplane,将鸟设计为一个类Bird,但是不能将 飞行 这个特性也设计为类,因此它只是一个行为特性,并不是对一类事物的抽象描述。此时可以将 飞行 设计为一个接口Fly,包含方法fly( ),然后Airplane和Bird分别根据自己的需要实现Fly这个接口。然后至于有不同种类的飞机,比如战斗机、民用飞机等直接继承Airplane即可,对于鸟也是类似的,不同种类的鸟直接继承Bird类即可。从这里可以看出,继承是一个 "是不是"的关系,而 接口 实现则是 "有没有"的关系。如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是有没有、具备不具备的关系,比如鸟是否能飞(或者是否具备飞行这个特点),能飞行则可以实现这个接口,不能飞行就不实现这个接口。

2)设计层面不同,抽象类作为很多子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计。什么是模板式设计?最简单例子,大家都用过ppt里面的模板,如果用模板A设计了ppt B和ppt C,ppt B和ppt C公共的部分就是模板A了,如果它们的公共部分需要改动,则只需要改动模板A就可以了,不需要重新对ppt B和ppt C进行改动。而辐射式设计,比如某个电梯都装了某种报警器,一旦要更新报警器,就必须全部更新。也就是说对于抽象类,如果需要添加新的方法,可以直接在抽象类中添加具体的实现,子类可以不进行变更;而对于接口则不行,如果接口进行了变更,则所有实现这个接口的类都必须进行相应的改动。