JAVA面向对象(下)

Java面向对象(下)

一、继承

​ 在现实生活中,继承一般指的是子女继承父辈的财产。在程序中,继承描述的是事物之间的所属关系,通过继承可以使多种事务之间形成一种关系。例如猫和狗都属于动物,程序中便可以描述为猫和狗继承自动物。

​ 在Java中,类的继承是指在一个现有的类的基础上去构造一个新的类,构建出来的信类被称作子类,现有类被称作父类,子类会自动拥有父类所有可继承的属性和方法。在程序中,如果想声明一个类继承另一个类,需要使用extends关键字。

//定义一个父类
class Animal{
	String name;//定义name的属性,如果是私有的属性则不能被子类继承
	//定义动物叫的方法
	public void shout(){
		System.out.println("发出叫声");
	}
}
//定义一个狗类,使用关键字extends用于继承
class Dog extends Animal{
	//定义一个打印name属性的方法
	public void printName(){
		System.out.println("name="+name);
	} 
}

//主函数内
Dog dog = new Dog();	//创建一个Dog类的对象,子类的对象
dog.name = "沙皮狗";	  //为Dog类的name属性赋值
dog.printName();		//调用Dog类输出名字的方法
dog.shout();			//调用Dog类继承的父类的方法
>>>
name=沙皮狗
发出叫声

在类的继承中,需要注意一些问题,具体如下:

​ 1.在Java中,类只支持单继承,不允许多重继承,也就是说一个类智能有一个直接父亲,例如下面的方法是不合法的

class A{}
class B{}
class C extends A,B{} 

​ 2.多个类可以继承一个父亲。

class A{}
class B extends A{}
class C extends A{}

​ 3.在Java中,多层继承是可以的,即一个类的父类可以再去继承另外的父类。

class A{}
class B extends A{}
class C extends B{}

​ 4.在Java中,一个类可以即是一个类的父类同时还可以是另外类的子类。例如上例的B。

二、重写

​ 在继承中,子类会自动继承父类中定义的方法,但有时在子类中需要对继承方法进行一些修改,即对父类的方法进行重写。需要注意的是,在子类中重写方法需要和父类被重写的方法具有相同的方法名、参数列表以及返回值类型,并且保证子类方法的权限必须大于或者等于父类方法权限

​ 例如前面的动物叫声可以经过重写达到我们预期的目的:

//定义一个父类
class Animal{
	String name;//定义name的属性,如果是私有的属性则不能被子类继承
	//定义动物叫的方法
	public void shout(){
		System.out.println("发出叫声");
	}
}
//定义一个狗类,使用关键字extends用于继承
class Dog extends Animal{
	//定义一个打印name属性的方法
	public void printName(){
		System.out.println("name="+name);
	}
	public void shout(){
		System.out.println("汪汪汪");
	}
}

//主函数内
Dog dog = new Dog();	//创建一个Dog类的对象,子类的对象
dog.name = "沙皮狗";	  //为Dog类的name属性赋值
dog.printName();		//调用Dog类输出名字的方法
dog.shout();			//调用Dog类继承的父类的方法
>>>
name=沙皮狗
汪汪汪

三、super关键字

​ 当子类重写父类的方法后,子类对象将无法访问父类被重写的方法,为了解决这个问题,在Java中专门提供了一个super关键字用于访问父类的成员。例如访问父类的成员变量、成员方法和构造方法。

​ 1.使用super关键字访问父类的成员变量和成员方法,格式如下:

super.成员变量
super.成员方法{{参数1,参数2....}}

例:

//定义Animal作为父类
class Animal{
    String name = "动物";
    //定义动物叫的方法
    public void shout(){
        System.out.println("动物发出叫声");
    }
}

//定义Dog继承Animal
class Dog extends Animal{
    String name = "犬类";
    public void shout(){
        //访问父类的方法shout
        super.shout();
        System.out.println("汪汪汪");
    }
    //定义打印name属性的方法
    public void printName(){
        System.out.println("name="+super.name);//访问父类的成员变量name
    }
}

//主函数内
Dog dog = new Dog();//创建dog类对象
dog.shout();
dog.printName();
>>>
动物发出叫声
汪汪汪
name=动物

​ 2.使用super关键字访问父类的构造方法:

super{{参数1,参数2....}}

​ 在子类的构造方法必须要访问父类的构造方法,子类的构造方法第一行有一行super关键字,如果父类的构造方法是有参的,则子类会报错。前提:必须是子类的构造方法才能调用父类的构造方法;

//定义Animal类作为父类
class Animal{
	//定义Animal类的构造方法
	public Animal(){
		System.out.println("父类的构造方法");
	}
}

//定义Dog类继承父类
class Dog{
	public Dog(){
		System.out.println("子类的构造方法");
	}
}

//主函数内
Dog dog = new Dog();//创建类的对象
>>>
父类的构造方法
子类的构造方法

四、final关键字

​ Java中的类被final关键字修饰后,该类将不可以被继承,也就是不能够派生子类。

final关键字可用于修饰类、变量和方法,它有“无法改变”或者“最终”的含义,因此被final修饰的类、变量和方法具有以下特性:

  • final修饰的类不能被继承
  • final修饰的方法不能被子类重写
  • final修饰的变量(成员变量和局部变量)是常量,只能赋值一次
//final修饰类
final class Animal{}
class Dog extends Animal{}//则Dog不能继承Animal类
//final修饰方法
class Animal{
	public final  void shout(){}//使用final关键字修饰的shout方法后,如果子类重写这个方法,编译报错,但是可以调用
}
class Dog extends Animal{
	public void shout(){}
}//则shout不能重写
//final修饰局部变量
final int num = 2;//第一次可以赋值,但是不能进行第二次赋值,可以被输出
num = 4;//会报错
//final修饰成员变量
class Student{
    final String name = 'summer';//为成员变量赋值,不允许再次改变
}

五、抽象类-abstract

​ 当定义一个类时,常常需要定义一些方法来描述该类的行为特征,但有时这些方法的实现方式是无法确定的。例如前面的Animal类时,shout方法用于表示动物的叫声,但是针对不同的动物,较深也是不同的,因此在shout方法中我无法准确的描述动物的叫声。

​ 针对上面的描述情况,Java允许定义方法时不写方法体,不包含方法体的方法为抽象方法,抽象方法必须使用abstract关键字来修饰,具体如下:abstract void shout();//定义抽象方法shout()

abstract class Animal{//定义抽象类
	public abstract void shout();//定义抽象方法shout()
}

​ 在定义抽象类时需要注意,包含抽象方法的类必须声明为抽象类,但抽象类可以不包含任何抽象方法,只需要用abstract关键字来修饰即可。另外,抽象类是不可以被实例化的,因为抽象类中可能有包含抽象方法,抽象方法是没有方法体的,不可以被调用如果想调用抽象类中定义的方法,则需要创建一个子类,在子类中将抽象类的抽象方法进行实现。

abstract class Animal{//定义抽象类
    int age;
    public void eat(){};
	public abstract void shout();//定义抽象方法shout()
}

//创建子类去继承抽象类
class Dog extends Animal{
    //重写父类的抽象方法,实现抽象方法
    public void shout(){
        System.out.println("汪汪汪")
    }
}

//主函数内
new Ainmal();//会报错。
Dog dog = new Dog();
dog.shout();
>>>
汪汪汪

六、接口-interface+implements

​ 如果一个抽象类中的所有方法都是抽象类,则可以将这个类用另外一种方式老赖定义,即接口。接口是由常量和抽象方法组成的特殊类,是对抽象类的进一步抽象。

​ 在定义接口时,需要使用interface关键字来声明,其语法格式如下:

[public] interface 接口名 [extends 接口1,接口2...]{
	[public] [static] [final] 数据类型 常量名= 常量值;
    [public] [abstract] 返回值 抽象方法名(参数列表);
}

​ 在上面语法中,一个接口可以有多个父接口,他们之间用逗号隔开。Java使用接口的目的是为了克服单继承的限制,因为一个类只能有一个父类,而一个类可以实现多个接口。接口中的变量默认使用“public static final"来修饰,即全局常量;接口中定义的方法默认使用”public abstract"来修饰,即抽象方法。如果接口声明public,则接口中的变量和方法全部为public。

​ 由于接口中的方法都是抽象方法,因此不能通过实例化对象的方法来调用接口中的方法。此时需要定义一个类,并使用implements关键字来实现接口中所有的方法。一个类可以在继承的同时实现多个接口,在implements子句中用逗号隔开。接口的实现类声明格式如下:

[<修饰符>] class<类名> [extends <超类名>] [implements <接口1>,<接口2>,...]

// 接口的定义和使用
// 定义接口Animal
interface Animal{
	// 定义全局常量,有自己固定的写法。默认的修饰符为public static final
    public static final String ANIMAL_BEHAVIOR = "动物的行为";
    // 定义抽象方法breathe(),修饰符默认为public abstract
    public abstract void breathe();
    // 定义抽象方法run()
    public abstract void run();
}

// 定义Dog类实现接口
class Dog implements Animal{
    // 重写接口中所有的方法
    public void breathe(){
        System.out.println(Animal.ANIMAL_BEHAVIOR+":狗在呼吸");
    }
    public void run(){
        System.out.println(ANIMAL_BEHAVIOR+":狗在奔跑");
    }
}

// 主函数内
Dog dog = new Dog();// 创建类,是Animal接口的实现类的对象
// 使用对象名.常量名可以输出接口中的常量,不推荐
//System.out.println(dog.ANIMAL_BEHAVIOR);
//接口名.常量名的方式输出接口中的常量
//System.out.println(Animal.ANIMAL_BEHAVIOR);
dog.breath();
dog.run();
>>>
动物的行为:狗在呼吸
动物的行为:狗在奔跑

​ 为了加深记忆,进行总结归纳:

  • 接口中的方法都是抽象的,不能实例化对象。
  • 接口中的属性只能是常量
  • 当一个类实现接口时,如果这个类是抽象类,则实现接口中的部分方法即可,否则需要实现接口中的所有方法。
  • 一个类通过implements关键字实现接口时,可以实现多个接口,被实现的多个接口之间要用逗号隔开。
interface Run{}
interface Fly{}
class Bird implements Run,Fly{}
  • 接口之间可以通过extends关键字继承多个接口,接口之间用逗号隔开。
interface Run{}
interface Fly{}
interface Eat extends Run,Fly{}
  • 一个类在继承另一个类的同时还可以实现接口。此时,extends关键字必须位于implements关键字之前
class Dog extends Canidae implements Animal{
    //先继承,在实现
}
//接口之间的继承
// 定义接口Animal
interface Animal{
	// 定义全局常量,有自己固定的写法。默认的修饰符为public static final
    public static final String ANIMAL_BEHAVIOR = "动物的行为";
    // 定义抽象方法breathe(),修饰符默认为public abstract
    public abstract void breathe();
    // 定义抽象方法run()
    public abstract void run();
}

//定义接口LandAnimal接口阶乘Animal接口
interface LandAnimal extends Animal{
    public abstract void liveOnLand();
}

// 定义Dog类实现接口
class Dog implements LandAnimal{
    // 重写接口中所有的方法
    public void breathe(){
        System.out.println(Animal.ANIMAL_BEHAVIOR+":狗在呼吸");
    }
    public void run(){
        System.out.println(ANIMAL_BEHAVIOR+":狗在奔跑");
    }
    public void liveOnLand(){
        System.out.println("狗是陆地上的动物");
    }
}

// 主函数内
Dog dog = new Dog();// 创建类,是Animal接口的实现类的对象
dog.breath();
dog.run();
dog.liveOnLand();
>>>
动物的行为:狗在呼吸
动物的行为:狗在奔跑
狗是陆地上的动物

七、多态

​ 在设计一个方法时,通常希望该方法具备一定的通用性。例如要实现一个动物叫的方法,由于每种动物的叫声是不同的,因此可以在方法中接受一各动物类型的参数,当传入猫类对象是就发出猫类的叫声,传入犬类时就发出犬类的叫声。在同一个方法中。这种由于参数类型不同而导致执行效果各异的现象就是多态。继承是多态得意实现的基础。

​ 在Java中为了实现多态,允许使用一个父类类型的变量来引用一个子类类型的对象,根据被引用子类对象特征的不同,得到不同的运行结果。

// 对象的多态性:父类或接口的引用 = new 子类的对象
interface Animal{
    public abstract void shout();// 定义好抽象方法
    
}

// 定义Cat类实现Animal接口
class Cat implements Animal{
    public void shout(){
        System.out.println("喵喵喵");
    }
}

// 定义Dog类实现Animal接口
class Dog implements Animal{
    public void shout(){
        System.out.println("汪汪汪");
    }
}

// 主函数内
// 定义测试类,多态
Animal an1 = new Cat();// 创建Cat对象,使用Animal类型的变量an1引用
Animal an2 = new Dog();
//an1.shout();
//an2.shout();
animalShout(an1);//调用animalShout方法,将an1作为参数传递
animalShout(an2);

//定义一个静态的animalShout方法,接受一个animal类型的参数
//参数,可以接受animal接口的任何实现类的对象
public static void animalShout(Animal an){
    an.shout();//调用实际参数的shout方法,
}

八、多态的类型转化

​ 在多态学习中,涉及到将子类作为对象当做父类类型使用的情况,此种情况在java语言环境中称之为“向上转型”,例如下面两行代码

Animal an1 = new Cat();
Animal an2 = new Dog();

​ 将子类对象当做使用时不需要任何显示地声明,需要注意的是,此时不能通过父类变量去调用子类中的特有方法。

//多态转型
interface Animal{
	public abstract void shout();
}

class Cat implements Animal{
    //实现抽象方法
    public void shout(){
        System.out.println("喵喵喵");
    }
    public void sleep(){
        System.out.println("猫睡觉");
    }
}

// 定义测试类
Cat cat = new Cat();
animalShout(cat);//调用animalShout,将caat作为参数传入

// 定义静态方法animalShout,接受一个Animal类型的参数
public static void animalShout(Animal animal){
    animal.shout();// 调用传入参数animal的shout方法
    //animal.sleep();// 会报错,不能调用子类的特有方法。
}

如果想调用实现类或者是子类中的特有方法,需要类型的向下转型,数据类型的强制转换

Cat cat = (Cat)animal

// 定义静态方法animalShout,接受一个Animal类型的参数
public static void animalShout(Animal animal){
    animal.shout();// 调用传入参数animal的shout方法
    //animal.sleep();// 会报错,不能调用子类的特有方法。
    Cat cat = (Cat)animal;
    cat.sleep();
}

如果是其他实现类传入animalShout,则会出现异常。

class Dog implements Animal{
    public void shout(){
        System.out.println("汪汪汪");
    }
}

//主函数内
Dog dog = new Dog();
animalShout(dog);
>>>
汪汪汪
异常

​ 如果要进行可行的话,需要进行判断(instanceof)

public static void animalShout(Animal animal){
    if (animal instanceof Cat){
        Cat cat = (Cat)animal;
        cat.shout();
        cat.sleep();
    }else {
        System.out.println("this animal is not cat");
    }
}

九、Object类

​ 在JDK中提供了一个Object类,它是层次结构的根类,每个类都直接或间接继承自该类,所有对象(包括数组)都实现了这个类的方法。Obiect类中的常用方法如下。

方法名称 方法说明
equals() 指示其他某个对象是否一次对象“相等“
getClass() 返回此Object的运行时类
hashCode() 返回该对象的哈希码值
toString() 返回该对象的字符串表示
//Object类中方法toString
class Animal{
    public void shout(){
        System.out.println("动物叫:");
    }
}

Animal animal= new Animal();
String s = animal.toString();
Ststem.out.println(s);

​ 在Object类中定义了toString方法,在该方法中输出了对象的基本信息,具体方法如下

getClass().getName() +"@"+Integer.toHexString(hashCode());

//重写toString方法
class Animal{
	//重写父类的toString方法
    public String toString(){
        return "这是一个动物"
    }
}


Animal animal = new Animal();
String s = animal.toString();
Ststem.out.println(s);

十、匿名内部类

​ 在编写java程序时,在类里面定义的类称之为内部类,内部类是外部类的一个成员。java内部类可以分为:成员内部类、方法内部类和匿名内部类等。

​ 在前面多态中,如果方法的参数被定义为一个接口类,那么就需要定义一个类来实现接口,并根据类进行对象实例化。除此之外,还可以使用匿名内部类来实现接口。所谓匿名内部类就是没有名字的内部类,表面上看起来似乎有名字,实际那不是它的名字。当程序总使用匿名内部类时,在定义匿名内部类的地方往往直接创建该类的一个对象。

//正常方法
interface Animal{
	public static void shout();//定义抽象方法
}

//定义一个内部类cat实现Animal接口
class Cat implements Animal{
    //实现shout方法
    public void shout(){
        System.out.println("喵喵喵");
    }
}

//调用animalShout方法并传入对象
animalShout(new Cat());


//定义静态方法animalShout
public static void animalShout(Animal an){
    an.shout();//调用传入对象的an的shout方法
}
//匿名内部类方法
interface Animal{
	public static void shout();//定义抽象方法
}

animalShout(new Animal(){
    // 实现shout方法
    public void shout(){
        System.out.println("喵喵..");
    }
});


//定义静态方法animalShout
public static void animalShout(Animal an){
    an.shout();//调用传入对象的an的shout方法
}

十一、异常

​ 针对程序会出现各种状况,java中引入了异常,以异常类的形式对这些非正常情况进行封装,通过异常处理机制对程序运行时发生各种问题进行处理。

int result=divide(4,2);
System.out.println(result);


public static int divide(int x,int y){
	int result = x/y;
    return result;
}

11.1 Error

​ error类称为错误类,它表示java运行时产生的系统内部错误或资源耗尽的错误,是比较严重的,仅靠修改程序本身是不能恢复执行的。举一个例子,在盖楼的过程中因为偷工减料,导致大楼坍塌,这就相当于一个Error。使用java命令去运行一个不存在的类就会出现error错误。

11.2 Exception

​ exception类称为异常类,它表示程序本身可以处理的错误,在开发java程序中进行的异常处理,都是针对exception类的众多子类中有一个特殊的RuntimeException类,该类及其子类用于表示运行时异常,除了此类,Exception类下所有其他的子类都用于表示便于时异常。

十二、try…catch和finally

​ java中提供了一种对异常进行处理的方式–异常捕获,异常捕获通常使用try…catch语句,具体语句如下:

try{
    //code
}catch(ExceptionType(Exception类及其子类) e){
    //code
}

​ 其中在try代码中编写可能发生异常的java语句,catch代码块中编写正对一场进行处理的代码,当tey代码块中的程序发生了异常,系统就会将这个异常的信息封装成一个异常对象,并将这个对象传递给catch代码块。catch代码块需要一个参数指明它所能接受的异常类型,这个参数的类型必须是Exception类或其子类。

try{
    int result=divide(4,0;
	System.out.println(result);
}catch(Exception e){
    System.out.println("捕获的输出异常",e.getMessage());
}
System.out.println("程序继续执行");


public static int divide(int x,int y){
	int result = x/y;
    return result;
}
try{
    int result=divide(4,0;
	System.out.println(result);
}catch(Exception e){
    System.out.println("捕获的输出异常",e.getMessage());
    return ;//结束整个方法
}finally{
    System.out.println("进入finally代码块");
}
System.out.println("程序继续执行");


public static int divide(int x,int y){
	int result = x/y;
    return result;
}

十三、throws关键字

​ 上由于调用自己写的divide方法,因此很清楚该方法可能会发生异常。但是如果去调用别人写的方法时,是否能知道别人写的异常呢?针对这种情况,java允许在方法的后面使用throws关键字对外声明该方法有可能发生异常,这样调用者在调用时,就明确的知道该方法有异常,并且必须在程序中对异常进行处理,否则编译无法通过。

​ throws关键字什么抛出异常的语法格式如下:

修饰符 返回值类型 方法名([参数1,参数2...]) throws ExceptionType1[,ExceptionType2....]{}

int resylt = divade(4,2);
System.out.println(result);


// 下面方法实现两个整数相处,并使用throws关键字声明
public static int divide(int x,int y) throws Exception{
    int result = x/y;
    return result;
}
//下面的代码定义了try chat语句用于 捕获异常
try{
    int resylt = divade(4,2);
	System.out.println(result);
}chat(Exception e){//对捕获的一次进行处理
    r.printStackTrace();//打印捕获的异常信息
}


// 下面方法实现两个整数相处,并使用throws关键字声明
public static int divide(int x,int y) throws Exception{
    int result = x/y;
    return result;
}
//在main方法上写throws Exception
int resylt = divade(4,2);
System.out.println(result);


// 下面方法实现两个整数相处,并使用throws关键字声明
public static int divide(int x,int y) throws Exception{
    int result = x/y;
    return result;
}

十四、运行时异常与编译时异常

​ 在实际开发中,经常会在程序编译时产生一些异常,而这些异常必须要进行处理,这种异常被称为编译时异常,也称checked异常。另外还有一种异常是在程序运行时产生的,这种异常即使不编写异常处理代码,依然可以通过编译,因此被称为运行时异常。也成为了unchecked异常。

14.1编译时异常

在java中,exception类中除了runtimeException类及其子类都是编译时异常。编译时异常的特点是java编译器会对其进行检查,如果出现异常就必须对异常进行处理,否则程序无法通过编译。处理编译时期的异常有两种方式:

使用try catch语句对异常进行捕获、使用rhrows关键字声明抛出异常,调用者对其处理

14.2 运行时异常

RuntimeException类及其子类都是运行时异常。运行时异常的特点是java编译器不会对其进行检查,也就是说当程序中出现这类宜昌市,及时没有使用try catch语句捕获或使用throws关键字声明抛出,程序也能编译通过。运行时异常一般是有程序中对的逻辑错误引起的,在程序运行时无法恢复。

十五、自定义异常

​ jdk中定义了大量的异常类,虽然这些异常类可以描述吧编程时出现的发部分异常情况,但是在程序开发中有可能需要描述程序中特有的异常情况。为了解决这个问题,在java中允许用户自定义异常,但自定义的异常必须继承自exception或其子类。

语法格式:throw Exception异常对象

public class DivideException extends Exception{
    public DivideException(){
        super(messgae);
    }
	public DivideException(String message){
        super(messgae);
    }
}
try{
    int result = divide(4,-2);//调用divide方法,传入一个负数作为被除数
	System.out.println(result);
}catch(DivideException e){
    //对捕获到的异常进行处理
    System.out.println(e.getMessage());
}

//实现两个数相处,并使用throws关键字声明抛出异常
public static int divide(int x,int y) throws DivideException{
    if(y<0){
        throw new DivideException("除数是负数")//使用throw关键字抛出异常对象
    }
    int result = x/y;
    return result;
}

十六、访问控制

​ 在java中正对类、成员方法和属性提供了四种访问级别,分别是private、default、protected和public。并且四种控制级别是有小到大依次列出的。

发布了106 篇原创文章 · 获赞 21 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/jiangSummer/article/details/105202821
今日推荐