一、类
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
- 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进行改动。而辐射式设计,比如某个电梯都装了某种报警器,一旦要更新报警器,就必须全部更新。也就是说对于抽象类,如果需要添加新的方法,可以直接在抽象类中添加具体的实现,子类可以不进行变更;而对于接口则不行,如果接口进行了变更,则所有实现这个接口的类都必须进行相应的改动。