Java入门(一)——类、抽象类和接口
Java是一门面向对象语言,可以看出“对象”在Java有着举足轻重的位置。那么,“对象”从何而来呢?那必须是丈母娘造出来的,下面我们就先来说说这个丈母娘——类。
Java类
- 对象: 对象具有状态和行为。 例如:一只狗的状态有:颜色,名称,品种,它的行为有:摇尾巴,吠叫,吃东西。 对象是类的实例
- 类: 类是一个模板,它描述一类具有相同状态和行为的对象。比如人类,都具有思考这个行为,而植物没有。
类可以看成是创建Java对象的模板,下面简单定义一个类:
public class People{
String name; // 成员变量
int age;
String weight;
static final double weeks = 9.5; // 类变量
void eat(){
}
void sleep(){
}
void play(){
}
Public People {
// 这是一个构造方法
int count = 0; // 局部变量
}
}
一个类可以包含以下类型变量:
- 局部变量: 在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁
- 成员变量:成员变量定义在类中,方法体之外的变量。 这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问
- 类变量:类变量也声明在类中,方法体之外,但必须声明为
static
类型 - 类方法:概念同上面的类变量
构造方法
每个类都有构造方法。如果没有显示地为类定义构造方法,Java编译器将会为该类提供一个默认构造方法。
在创建一个对象的时候,至少要调用一个构造方法。构造方法的名称必须与类同名,一个类可以有多个构造方法。
public class Puppy{
public Puppy(){
}
public Puppy(String name){
// 这个构造器仅有一个参数:name
}
}
实例化对象
对象时根据类创建的。在Java中,使用关键字new
来创建一个新的对象。创建对象需要以下三步:
- 声明:声明一个对象,包括对象名称和对象类型
- 实例化:使用关键字
new
来创建一个对象 - 初始化:使用
new
创建对象时,会调用构造方法初始化对象
下面是一个创建对象的例子:
public class Puppy{
public Puppy(String name){
//这个构造器仅有一个参数:name
System.out.println("小狗的名字是 : " + name );
}
public static void main(String[] args){
// 下面的语句将创建一个Puppy对象
Puppy myPuppy = new Puppy( "tommy" );
}
}
编译并运行上面的程序,会打印出下面的结果:
小狗的名字是:tommy
下面的例子展示如何访问实例变量和调用成员方法:
public class Puppy{
int puppyAge;
public Puppy(String name){
// 这个构造器仅有一个参数:name
System.out.println("小狗的名字是 : " + name );
}
public void setAge( int age ){
puppyAge = age; // 当形参`age`也是puppyAge时,需要用this:this.puppyAge = puppyAge
}
public int getAge( ){
System.out.println("小狗的年龄为 : " + puppyAge );
return puppyAge;
}
public static void main(String[] args){
/* 创建对象 */
Puppy myPuppy = new Puppy( "tommy" );
/* 通过方法来设定age */
myPuppy.setAge( 2 );
/* 调用另一个方法获取age */
myPuppy.getAge( );
/*你也可以像下面这样访问成员变量 */
System.out.println("变量值 : " + myPuppy.puppyAge );
}
}
编译并运行上面的程序,产生如下的结果:
小狗的名字是 : tommy
小狗的年龄为 : 2
变量值 : 2
Java继承
Java的继承是一种机制,表示为一个对象获取父对象的所有属性和行为。
继承是类与类之间的关系,是一个很简单很直观的概念,与现实世界中的继承(例如儿子继承父亲财产)类似。
Java中的继承,可以创建基于现有类构建新的类。 当你从现有类继承时,就可以重复使用父类的方法和字段,也可以在继承的新类中添加新的方法和字段。
为什么在Java中使用继承
对于方法覆盖(因此可以实现运行时的多态性),提高代码可重用性。在Java中,子类可继承父类中的方法,而不需要重新编写相同的方法。但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写(覆盖)。
Java类继承的语法示例:
class Super {
}
class Sub extends Super {
}
extends
关键字表示从现有类派生创建的新类。extends
的含义是增加功能。在Java的术语中,被继承的类称为父类或超类,新类称为子类。
Java继承示例
如上图所示,Programmer
是子类,Employee
是超类。 两个类之间的关系是Programmer IS-A Employee
. 它表示 Programmer
是一种 Employee
的类型。
参考下面示例代码实现:
class Employee {
float salary = 40000;
}
class Programmer extends Employee {
int bonus = 10000;
public static void main(String args[]) {
Programmer p = new Programmer();
System.out.println("Programmer salary is:" + p.salary);
System.out.println("Bonus of Programmer is:" + p.bonus);
}
}
执行上面代码得到以下结果:
Programmer salary is:40000.0
Bonus of programmer is:10000
在上面的例子中,Programmer
对象可以访问自身类以及Employee
类的字段,即提高了代码可重用性。
默认的: 不使用任何修饰符声明的属性和方法,对同一个包内的类是可见的。接口里的变量都隐式声明为
public static final
,而接口里的方法默认情况下访问权限为public
Java继承类型
在类的基础上,在Java中可以有三种类型的继承:单一,多级和分层继承。在Java的编程中,仅能通过接口支持多继承和混合继承。
注意:Java中的类不支持多继承
当一个类扩展多个类,即被称为多继承。例如:
问题:为什么在Java中不支持多继承?
为了降低复杂性并简化语言,Java中不支持多重继承。想象一个:A,B和C是三个类。 C类继承A和B类。 如果A和B类有相同的方法,并且从子类对象调用它,A或B类的调用方法会有歧义。
因为编译时错误比运行时错误好,如果继承2个类,Java会在编译时报告错误。 所以无论子类中是否有相同的方法,都会有报告编译时错误。
注意:构造方法不能被继承,掌握这一点很重要。 一个类能得到构造方法,只有两个办法:编写构造方法,或者根本没有构造方法,类有一个默认的构造方法。
抽象类
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
在Java中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。
由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。
抽象类
在Java中使用abstract
关键字来定义抽象类。如下示例:
//example of abstract class that have method body
abstract class Bike {
Bike() {
System.out.println("bike is created");
}
abstract void run();
void changeGear() {
System.out.println("gear changed");
}
}
class Honda extends Bike {
void run() {
System.out.println("running safely..");
}
}
class TestAbstraction2 {
public static void main(String args[]) {
Bike obj = new Honda();
obj.run();
obj.changeGear();
}
}
注意到该Bike
类没有什么不同,尽管该类是抽象类,但是它仍然有成员变量,抽象方法,方法体,构造函数甚至main()
方法。
执行上面的代码:
bike is created
running safely..
gear changed
抽象方法
如果你想设计这样一个类,该类包含一个特别的成员方法,该方法的具体实现由它的子类确定,那么你可以在父类中声明该方法为抽象方法。
Abstract 关键字同样可以用来声明抽象方法,抽象方法只包含一个方法名,而没有方法体。
抽象方法没有实现,方法名后面直接跟一个分号,而不是花括号。
public abstract class Employee
{
private String name;
private String address;
private int number;
public abstract double computePay();
//其余代码
}
声明抽象方法会造成以下两个结果:
- 如果一个类包含抽象方法,那么该类必须是抽象类
- 任何子类必须重写父类的抽象方法,或者声明自身为抽象类
继承抽象方法的子类必须重写该方法。否则,该子类也必须声明为抽象类。最终,必须有子类实现该抽象方法,否则,从最初的父类到最终的子类都不能用来实例化对象。
如果Salary
类继承了Employee
类,那么它必须实现computePay()
方法:
/* 文件名 : Salary.java */
public class Salary extends Employee
{
private double salary; // Annual salary
public double computePay()
{
System.out.println("Computing salary pay for " + getName());
return salary/52;
}
//其余代码
}
注意: 抽象类中不能有抽象构造方法或抽象静态方法
参考: Java抽象类
抽象类和类的区别
抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
-
抽象类不能被实例化,如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以被实例化
-
普通类和抽象类都可以被继承,但是抽象类被继承后子类必须重写继承的方法,除非自类也是抽象类
-
抽象类中不一定含有抽象方法,但是有抽象方法的类必定时抽象类
-
抽象类中的抽象方法只是声明,不包含方法体,即不能给出方法的具体实现
-
构造方法、类方法(用
static
修饰的方法)不能声明为抽象方法
总之一句话,抽象类只是一种比较特殊的普通类。我觉得这篇文章解释的不错,大概意思就是:抽象类严格规定了几个你必须做的事情,比如:不能实例化、子类必须实现父类,除非子类也是抽象类。你不这样做就会出错,这种严谨的做法比人为规定的更加有效。
接口
定义:接口在Java中是一个抽象类型,是抽象方法的集合。一个类通过继承接口的方式,从而继承接口的抽象方法。
接口通常以interface
来声明,可以看做是一种特殊的抽象类。一个类通过继承接口的方式,从而来继承接口的抽象方法。
接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
接口的声明
接口的声明语法格式如下:
public interface 接口名称 [extends 其他的接口名] {
// 声明变量
// 抽象方法
}
接口有以下几个特征:
- 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为
public abstract
(只能是public abstract
,其他修饰符都会报错) - 接口中可以含有变量,但是接口中的变量会被隐式的指定为
public static final
变量(并且只能是public
,用private
修饰会报编译错误) - 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法
注:JDK 1.8 以后,接口里可以有静态方法和方法体
接口的实现
当类实现接口的时候,类要实现接口中的所有的方法。否则,类必须声明为抽象类
类使用implements
关键字实现接口。在类声明中,Implements
关键字放在class
声明后面。
实现一个接口的语法,可以使用这个公式:
...implements 接口名称[, 其他接口名称, 其他接口名称..., ...] ...
下面是一个Java的接口示例:
interface printable {
void print();
}
class A6 implements printable {
public void print() {
System.out.println("Hello, Interface");
}
public static void main(String args[]) {
A6 obj = new A6();
obj.print();
}
}
执行上面的代码,结果如下:
Hello,Interface
接口与类的相似点
- 一个接口可以有很多方法
- 接口文件保存在
.java
结尾的文件中,文件名使用接口名 - 接口的字节码文件保存在
.class
结尾的文件中 - 接口相应的字节码文件必须在与包名称相匹配的目录结构中
接口与类的区别
- 接口不能用于实例化对象
- 接口没有构造方法
- 接口中所有方法必须为抽象方法
- 接口不能包含成员变量,除了
static
和final
变量 - 接口不是被类继承了,而是要被类实现
- 接口支持多继承
接口和抽象类的区别
- 抽象类中的方法可以不是抽象方法,但接口中的方法必须都是抽象方法
- 抽象类中的非抽象方法可以有方法体,就是实现方法的具体功能,避免在子类中重复实现这些方法。但接口中的方法不行(JDK 1.8以后可以有方法体,作为默认实现)
- 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是
public static final
类型的 - 抽象类可以有具体的方法和属性, 接口只能有抽象方法和不可变常量(final)
- 一个类只能继承一个抽象类,而一个类却可以实现多个接口
- 抽象类里面的抽象方法必须全部被子类实现,如果子类不实现,那么子类必须也是抽象类。接口里面的方法也必须全部被子类实现,如果子类不能实现,那么子类必须是抽象类
- 抽象类主要用来抽象类别,接口主要用来抽象方法功能。当你关注事物的本质的时候,请用抽象类;当你关注一种操作的时候,用接口
- 抽离类可以实现接口,接口不能实现抽象类
参考: