1.类的继承
1.1继承的概念
类的继承指在一个现有类的基础上构建一个新的类,构建出来的类称为子类,现有类称为父类。
使用extends关键字声明继承关系,语法格式如下:
class 父类{
...
}
class 子类 extends 父类{
...
}
例如:
class Animal{
private String name;
public String getName(){
return name;
}
public String setName(String name){
this.name = name;
}
}
class Dog extends Animal{
}
public class Test{
public static void main(String[] args){
Dog dog = new Dog();
dog.setName("牧羊犬");
System.out.println(dog.getName());
}
}
子类没有定义任何属性和方法,但在继承父类的时候会继承父类的成员,但不能直接访问父类中的私有成员。
子类也可以定义自己的属性和方法,例如:
class Animal{
private String name;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
}
class Dog extends Animal{
private String color;
public String getColor(){
return color;
}
public void setColor(String color){
this.color = color;
}
}
public class Test{
public static void main(String[] args){
Dog dog = new Dog();
dog.setName("牧羊犬");
dog.setColor("黑色");
System.out.println(dog.getName(),dog.getColor());
}
注意:
Java中一个类只有一个直接父类,多个类可以继承一个父类,一个类的父类可以继承另一个父类。
1.2方法的重写
子类对继承的方法进行修改,即对父类的方法进行重写。
重写的方法不能改变方法名、参数列表和返回值类型,且不能拥有比父类更加严格的访问权限。
class Animal{
void shout(){
System.out.println("动物叫声");
}
}
class Dog extends Animal{
void shout(){
System.out.println("汪汪汪...");
}
}
public class Test{
public static void main(String[] args){
Dog dog = new Dog();
dog.shout();
}
最终的输出结果为“汪汪汪...”。
注意:重写父类的方法后,子类对象无法直接访问父类被重写的方法,可以用super关键字解决。
2.super关键字
super关键字可以在子类中调用父类的普通属性、方法和构造方法,具体格式如下:
super.成员变量
super.成员方法(参数1,参数2...)
super(参数1,参数2...)
(1)子类可以通过super关键字访问父类中的成员变量和成员方法,如:
class Animal{
String name = "牧羊犬";
void shout(){
System.out.println("动物叫声");
}
}
class Dog extends Animal{
public void shout(){
super.out();
}
public void printName(){
System.out.println(super.name);
}
}
public class Test{
public static void main(String[] args){
Dog dog = new Dog();
dog.shout();
dog.printName();
}
(2)子类可以通过super关键字访问父类中的构造方法,如:
class Animal{
private String name;
private int age;
public Animal(String name, int age){
this.name = name;
this.age = age;
}
//setter和getter方法...
}
class Dog extends Animal{
private String color;
public Dog(String name, int age, String color){
super(name,age);
this.setColor(color);
}
//getter方法...
public void setColor(String color){
this.color = color;
}
}
注意:通过super调用父类构造方法的代码必须位于子类构造方法第一行,且只能出现一次。
this和super不可以同时出现再构造方法中,因为二者都要求在首行。
3.final关键字
使用final修饰的类不能有子类,修饰的方法不能被子类重写,修饰的变量是常量,不可修改。
使用final声明变量时要求全部的字母大写。
如果变量使用public static final声明,则该变量将成为全局变量,如:
public static final String NAME = "Zhang";
4.抽象类和接口
4.1抽象类
抽象方法是使用abstract关键字修饰的成员方法,只需声明不需实现。包含抽象方法的类是抽象类,使用abstract关键字修饰,例如:
abstract class Animal{
abstract void shout();
}
class Dog extends Animal{
void shout(){
System.out.println("汪汪汪...");
}
}
注意:如果一个非抽象类继承了抽象类,该子类必须实现抽象类中全部抽象方法。
使用abstract关键字修饰的抽象方法不能使用private修饰,因为必须被子类实现。
4.2接口
所有方法都是抽象的抽象类可以被定义为接口。
Java使用接口的目的是克服单继承的限制,一个接口可以有多个父接口,但不允许继承抽象类。
接口使用interface关键字声明接口,语法格式如下:
public interface 接口名 extends 接口1,接口2...{
public static final 数据类型 常量名 = 常量值;
public abstract 返回值类型 抽象方法名称(参数列表);
}
接口除了可以包括抽象方法外,还可以包括默认方法和静态方法,这两种方法都允许有方法体。
接口中的变量默认使用“public static final”修饰,即全局常量,定义的方法默认使用“public abstract”修饰,即静态方法。
注意:如果接口声明为public,则其中所有的常量和方法全部为public。
使用implements关键字实现接口和其中的所有抽象方法,语法格式如下:
修饰符 class 类名 implements 接口1, 接口2,...{
...
}
interface Animal{
void shout();
}
interface Action{
void eat();
}
class Dog implements Animal,Action{
public void shout(){
System.out.println("汪汪汪...");
}
public void eat(){
System.out.println("骨头");
}
}
也可以实现一个类既实现接口又继承抽象类,如:
interface Animal{
void shout();
}
abstract class Action{
public abstract void eat();
}
class Dog extends Action implements Animal{
public void shout(){
System.out.println("汪汪汪...");
}
public void eat(){
System.out.println("骨头");
}
}
5.多态
多态有两种形式:方法的重载(方法多态),方法的重写(对象多态)。
5.1对象类型转换
对象类型转换主要有两种情况:
(1)向上转型:子类对象->父类对象
父类类型 父类对象 = 子类实例;
Dog dog = new Dog();
Animal an = dog;
an.shout();
注意:发生向上转型后,调用的方法为被子类重写后的方法。
父类方法无法调用只在子类中定义,没有在父类中定义过的方法。
(2)向下转型:父类对象->子类对象
父类类型 父类对象 = 子类实例;
子类类型 子类对象 = (子类) 父类对象;
不能直接写为:
Dog dog = (Dog) new Animal;
注意:向上转型程序会自动完成,向下转型要指名转型的子类类型。
5.2instanceof关键字
使用instanceof关键字判断一个对象是否是某个类的实例,如果是,则返回true,否则返回false,语法格式如下:
对象 instanceof 类
class Animal{
...
}
class Dog extends Animal{
...
}
public class Test{
public static void main(String[] args){
Animal a1 = new Dog();
System.out.println(a1 instanceof Animal);
System.out.println(a1 instanceof Dog);
}
最终运行结果都为true。
6.Object类
定义一个类时,如果没有使用extends关键字显式指定父类,则默认继承Object类。
Object类是所有类的父类,因此通常被称为超类,其中定义了一些方法,常用的如下:
7.内部类
在一个类的内部定义的类称为内部类,根据内部类的位置、修饰符和定义方式的不同可分为成员内部类、静态内部类、局部内部类(方法内部类)和匿名内部类。
7.1成员内部类
成员内部类可以访问外部类的所有成员。
如果想通过外部类访问成员内部类,需要通过外部类创建内部类对象,语法格式如下:
外部类名.内部类名 变量名 = new 外部类名().new 内部类名();
class Outer{ //外部类
int m = 0;
class Inner{ //成员内部类
int n = 1;
void show1(){
System.out.println(m);
}
}
Inner inner = new Inner();
System.out.println(n);
}
public class Test{
public static void main(String[] args){
Outer.Inner inner = new Outer().new Inner();
System.out.println(inner.n);
}
}
7.2静态内部类
静态内部类是使用static关键字修饰的成员内部类。
静态内部类只能访问外部类的静态成员。
如果想通过外部类访问静态内部类,可以跳过外部类直接访问,语法格式如下:
外部类名.静态内部类名 变量名 = new 外部类名().静态内部类名();
7.3局部内部类
局部内部类定义在方法中,有效范围只限于方法内部。
局部内部类可以访问外部类的所有成员。
局部内部类的成员只能在所述方法中访问,不能被外部类访问。
class Outer{ //外部类
int m = 0;
void test(){
class Inner{ //局部内部类
int n = 1;
void show(){
System.out.println(m);
}
}
}
}
7.4匿名内部类
匿名内部类是没有名称的内部类,是实现接口的一种简便写法,一般用于只使用一次的接口。
创建匿名内部类的基本写法如下:
new 父接口(){
//匿名内部类实现部分
}
interface Animal{
void shout();
}
public class Test{
public static void main(String[] args){
interface Aniaml = new interface Animal(){ //匿名内部类
@Override
public void shout(){
System.out.println("喵喵喵...");
}
};
}
}
8.异常
Java以异常类的形式对程序运行的过程中的各种非正常情况进行封装,通过异常处理机制对程序运行时发生的各种问题进行处理。
Java提供大量异常类,这些类都继承自java.lang.Throwable类。
Throwable类有两个直接子类Error和Exception。
Errow代表程序中的错误,仅靠修改程序本身不能恢复执行。
Exception代表程序中的异常,程序本身可以处理的错误,RuntimeException及其子类表示运行时异常,其他子类表示编译时异常。
Trowable类中常用的方法如下:
8.1异常捕获
异常捕获是对异常进行处理的方式,使用try...catch语句实现,具体语法格式如下:
try{
//程序代码块
}catch(Exception类及其子类 e){
//对该类的处理
}
catch代码块对异常处理完毕后,程序会向下执行。try代码块中发生异常语句后面的语句不会被执行。
如果希望语句无论程序是否异常都要执行,可在try...catch语句后加finally代码块,如:
public class Test{
public static void main(String args[]){
try{
int result = divide(4, 0);
}catch(Exception e){
System.out.println(e.getMessage());
return;
}finally{
System.out.println("finally代码块");
}
public static int divide(int x, int y){
int result = x / y;
return result;
}
}
finally代码块中的代码不受return语句的影响,因此程序设计时常用 finally代码块处理完成必须要做的事,例如释放系统资源。
注意:如果在try...catch中执行了System.exit(0)语句,finally中的代码不会执行,因为System.exit(0)语句代表退出当前Java虚拟机。
8.2throws关键字
在方法后使用throws关键字声明抛出异常,这样调用者在调用方法时就明确知道该方法是否有异常,并且必须在程序中对异常进行处理,否则编译不通过,语法格式如下:
修饰符 返回值类型 方法名(参数1, 参数2...)throws 异常类1, 异常类2...{
//方法体
}
public class Test{
public static void main(String args[]){
try{
int result = divide(4, 0);
}catch(Exception e){
e.printStackTrace();
}
public static int divide(int x, int y)throws Exception{
int result = x / y;
return result;
}
}
如果不知道如何处理声明抛出的异常,也可以使用throws关键字继续将异常抛出,程序也能编译通过。但是程序一旦发生异常,且异常没有被处理,程序就会非正常终止。
8.3编译时异常与运行时异常
1.编译时异常
Exception类中除了RuntimeException类及其子类,其他子类都是编译时异常(checked异常)。
编译时异常的特点是Java编译器会对异常进行检查,如果出现异常就必须进行处理,否则程序无法通过编译。有两种方式处理:
(1)使用try...catch语句对异常进行捕获处理。
(2)使用throws关键字声明抛出异常,调用者对异常进行处理。
2.运行时异常
RuntimeException类及其子类都是运行时异常。
运行时异常的特点是Java编译器不会对异常进行检查,即异常没有处理也能编译通过。
运行时异常一般由程序中的逻辑错误引起,程序运行时无法恢复,例如数组角标越界。
8.4自定义异常
Java允许用户自定义异常,但自定义的异常类必须继承自Exception类或其子类。实际开发中,如果没有特殊要求,自定义的异常类只需继承Exception类,在构造方法中使用super语句调用Exception的构造方法即可,例如:
public class DivideByMinusException extends Exception{
public DivideByMinusException(){
super(); //调用Exception无参的构造方法
}
public DivideByMinusException(String Message){
super(message); //调用Exception有参的构造方法
}
}
自定义异常类中使用throws关键字在方法中声明异常的实例对象,语法格式如下:
public class Test{
public static void main(String args[]){
try{
int result = divide(4, -2);
}catch(DivideByMinusException e){
e.printStackTrace();
}
public static int divide(int x, int y)throws DivideByMinusException{
if(y<0){
throw new DivideByMinusException("除数是负数");
}
int result = x / y;
return result;
}
}