面向对象(下)
继承
继承的方法
类的继承是在现有类的基础上构建一个新的类,构建的新类被称作子类,现有类被称作父类。子类自动继承父类的属性和方法,使得子类具有父类的特征和行为。
用extends关键字声明一个类继承另一个类
class 父类{
}
class 子类 extends 父类{
}
class Animal {
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge(){
return age;
}
public String getName(){
return name;
}
}
class Dog extends Animal{
private String color;
public Dog(String color){
this.color=color;
}
public String getColor(){
return color;
}
}
public class test1 {
public static void main(String[] args) {
Dog d1=new Dog("黑色");
d1.setName("小黑");
d1.setAge(18);
System.out.println("小狗的颜色:"+d1.getColor()+" 名字:"+d1.getName()+" 年龄:"+d1.getAge());
}
}
子类Dog在创建对象后可以调用父类Animal的属性和方法,证明子类在继承父类的时候会自动继承父类的属性和方法。
需要注意的是,子类只能访问父类中用public和protected修饰的属性和方法,父类中默认修饰符default和private修饰的属性和方法不能被子类访问。
1.java中,类只支持单继承,不允许多继承。一个类只能有一个直接父类。
class A{}
class B{}
class C extends A,B{}
这种写法是错误的。
2.多个类可以继承一个父类
3.可以多层继承,一个类的父类可以再继承另外的父类。
class A{}
class B extends A{}
class C extends B{}
方法的重写
class Animal {
void shout(){
**System.out.println("动物发出叫声");**
}
}
class Dog extends Animal{
void shout(){
**System.out.println("汪汪汪");**
}
}
public class test1 {
public static void main(String[] args) {
Dog d1=new Dog();
d1.shout();
}
}
在子类中重写的方法需要和父类中被重写的方法具有相同的方法名、参数列表以及返回值类型。
子类重写方法,不能使用比父类更严格的访问权限。
class Animal {
public void shout(){
**System.out.println("动物发出叫声");**
}
}
class Dog extends Animal{
private void shout(){
**System.out.println("汪汪汪");**
}
public class test1 {
public static void main(String[] args) {
Dog d1=new Dog();
d1.shout();
}
super关键字
子类重写父类方法时,子类对象将无法访问父类中被子类重写的方法。使用super关键字可以在子类中访问父类的非私有方法,非私有属性及构造方法。
使用方法
super.属性(参数1,参数2,…)
class Animal {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
void shout() {
System.out.println("动物发出叫声");
}
}
class Dog extends Animal{
void shout(){
super.shout();
System.out.println("汪汪汪");
}
public void printName(){
super.setName("小白");
System.out.println(super.getName());
}
}
public class test1 {
public static void main(String[] args) {
Dog d1=new Dog();
d1.shout();
d1.printName();
}
}
使用super关键字调用父类中指定的构造方法
super(参数1,参数2)
class Animal {
private String name;
private int age;
public Animal(String name,int age)
{
this.name=name;
this.age=age;
}
public void setName(String name) {
this.name = name;
}
public String getName(){
return name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge(){
return age;
}
public String info(){
return "名字:"+this.getName()+" 年龄:"+this.getAge();
}
// void shout(){
// System.out.println("动物发出叫声");
// }
}
class Dog extends Animal{
private String color;
public Dog(String name,int age,String color){
super(name,age);
this.color=color;
}
public String getColor(){
return color;
}
public String info(){
return super.info()+"颜色:"+this.getColor();
}
// void shout(){
// super.shout();
// System.out.println("汪汪汪");
// }
// public void printName(){
// super.setName("小白");
// System.out.println(super.getName());
// }
}
public class test1 {
public static void main(String[] args) {
Dog d1=new Dog("小黑",3,"黑色");
// d1.setName("小黑");
// d1.setAge(18);
// System.out.println("小狗的颜色:"+d1.getColor()+" 名字:"+d1.getName()+" 年龄:"+d1.getAge());
// d1.shout();
// d1.printName();
System.out.println(d1.info());
}
}
Java的每个类都必须有自己的构造方法,若是没有显式的用代码写出来,则系统会默认一个空的构造方法。在继承中,子类同样要有自己的构造方法,但必须遵循如下规则:
子类的构造过程中必须调用其父类的构造方法;
子类可以在自己的构造方法中使用super(argument_list)调用其父类的构造方法;
子类可以在自己的构造方法中使用this(argument_list)调用自己的其它构造方法;
如果调用super(argument_list),this(argument_list),必须写在子类构造方法的第一行;
如果子类构造方法中没有显式地调用父类构造方法,则系统默认调用基类无参数构造方法;
如果子类构造方法中既没有显式地调用父类构造方法,而父类中又没有无参构造方法,则编译出错;
并不是每个子类的构造方法都必须显式地调用父类构造方法,可以通过this(argument_list)调用自己的有调用父类构造方法的其它构造方法。
public Dog(tring color){
this.color=color;
}
子类构造方法中未调用父类构造方法,程序编译时产生错误。
super和this关键字的作用非常相似,都可以访问属性以及调用方法和构造方法
this和super不能同时出现,因为使用this和super调用构造方法的代码都要求必须放在构造方法的首行
final关键字
在默认情况下,所有的成员变量和成员方法都可以被子类重写。如果父类的成员不希望被子类重写,可以在声明父类的成员时使用final关键字修饰。
1.使用final关键字修饰的类不能有子类。
2.使用final关键字修饰的方法不能被子类重写。
3.使用final关键字修饰的变量是常量,常量不可修改。
final关键字修饰类
final class Animal{
}
class Dog extends Animal{}
public class test1 {
public static void main(String[] args) {
Dog d1=new Dog();
}
}
父类用final关键词修饰,子类无法继承父类。
final关键词修饰方法
class Animal{
public final void shout(){
}
}
class Dog extends Animal{
public void shout(){
}
}
public class test1 {
public static void main(String[] args) {
Dog d1=new Dog();
}
}
Animal类的方法被final修饰,子类Dog无法重写。
final关键词修饰变量
被final修饰的变量为常量,常量只在声明中被赋值一次,在后面的程序中,变量的值不能改变。再次对final修饰的常量赋值,程序在编译时报错。
public class test1 {
public static void main(String[] args) {
final int age=18;
age=20;
}
}
final声明变量要求变量名称全部大写。如果用public static final 声明,则变量将成为全局变量。
public static final String NAME=“萨摩耶”;
抽象类和接口
抽象类
定义一个类,需要定义一些成员方法用于描述类的行为特征,这些方法的实现是无法确定的。例如,前面定义的Animal类中的shout()方法用于描述动物的叫声,不同的动物叫声也不相同,在shout方法中无法准确描述动物的叫声。(与c++中的声明定义类似)
java提供了抽象方法满足这种需求。使用abstract关键字修饰成员方法,抽象方法在定义时不需要实现方法体。
语法格式:
abstract 返回值类型 方法名称(参数列表)
当一个类包含了抽象方法,该类就是抽象类。抽象类和抽象方法一样,必须使用abstract关键字进行修饰。
抽象类的语法格式如下
abstract class 抽象类名称{
属性;
访问权限 返回值类型 方法名称(参数){ //普通方法
return[返回值];
}
访问权限 abstract 返回值类型 抽象方法名称(参数);//抽象方法,无方法体
}
抽象类的定义比普通类多了一个或多个抽象方法。
1.包含抽象方法的类必须是抽象类
2.声明抽象类和抽象方法时都需要使用abstract关键字修饰
3.抽象方法只需要声明而不需要实现
4.如果一个非抽象类继承了抽象类之后,那么类必须重写抽象类中的全部抽象方法。
abstract class Animal{
public abstract void shout();
}
class Dog extends Animal{
public void shout(){
System.out.println("汪汪");
}
}
public class test1 {
public static void main(String[] args) {
Dog d1=new Dog();
d1.shout();
}
}
使用abstract修饰的抽象方法不能使用private修饰,因为抽象方法必须被子类实现,如果使用了private修饰抽象方法,则子类无法实现该方法。
接口
接口是一种用来定义程序的协议,用于描述类或结构的一组相关行为。接口是由抽象类衍生的一个概念,并由此产生了一种编程方式,这种编程方式称为面向对象编程。将程序的不同的业务逻辑分离,以接口的形式对接不同的业务模块。接口中不实现任何业务逻辑,业务逻辑由接口的实现类完成。当业务需求变更时,只需要修改实现类中的业务逻辑,而不需要修改接口中的内容,以减少需求变更对系统产生的影响。
在java中,使用接口的目的是克服单继承的限制,因为一个类只能有一个父类,而一个类可以实现多个父接口。在JDK8之前,接口是由全局变量和抽象方法组成的。JDK8对接口进行了重新定义,接口中除了抽象方法外,还可以定义默认方法和静态方法,默认方法使用default关键字修饰,静态方法使用static关键字修饰,两种方法都允许有方法体。
接口使用interface关键字声明,语法格式如下:
[public] interface 接口名[extends 接口1,接口2,…]{
[public][static][final]数据类型 常量名=常量;
[public][abstract] 返回值数据类型 方法名 [参数列表];
[public] static 返回值数据类型 方法名(参数列表){}
[public] default 返回值的数据类型 方法名 (参数列表){}
}
“extend 接口1,接口2…”表示一个接口可以有多个父接口。接口中的变量默认使用public static final 进行修饰,表示全局变量。接口中定义的抽象方法默认使用public abstract修饰。如果接口中的方法省略了public,它的访问权限不是的default,仍然是public。
接口本身不能实例化,接口中的抽象方法和默认方法只能通过接口实现类的实例对象进行调用。实现类通过implement关键字实现接口,并且实现类必须重写接口中所以的抽象方法。
interface Animal {
String name = "小冰";
int ID=1;
public abstract void shout();
void info();
static int getId(){
return Animal.ID;
}
}
interface Action{
void eat();
}
class Dog implements Animal,Action {
public void shout(){
System.out.println("汪汪");
}
public void eat(){
System.out.println("喜欢照镜子");
}
public void info(){
System.out.print("名称:"+name);
}
}
public class test1 {
public static void main(String[] args) {
System.out.println("编号:"+Animal.getId());
Dog d1=new Dog();
d1.shout();
d1.eat();
d1.info();
}
}
接口的实现类必须实现接口中的所以抽象方法,否则程序编译错误。
上述演示的是类与接口之间的实现关系。如果在开发中一个子类既要实现接口又要实现继承类,按照以下语法格式定义子类:
修饰符 class 类名 extends 父类名 implements 接口1,接口2,…{
}
interface Animal {
String name = "小冰";
int ID=1;
public abstract void shout();
void info();
static int getId(){
return ID;
}
}
abstract class Action{
abstract void eat();
}
//interface Action{
// void eat();
//}
class Dog extends Action implements Animal {//Dog类实现接口
public void shout(){
System.out.println("汪汪");
}
public void eat(){
System.out.println("喜欢照镜子");
}
public void info(){
System.out.print("名称:"+name);
}
}
接口不允许继承抽象类,但是允许接口继承接口,并且一个接口可以同时继承多个接口。
interface Animal {
String name = "小冰";
int ID=1;
public abstract void shout();
void info();
static int getId(){
return ID;
}
}
interface Color{
void black();
}
interface Action extends Animal,Color{
abstract void eat();
}
class Dog implements Action {//Dog类实现接口
public void shout(){
System.out.println("汪汪");
}
public void eat(){
System.out.println("喜欢照镜子");
}
public void info(){
System.out.println("名称:"+name);
}
public void black(){
System.out.println("黑色");
}
}
public class test1 {
public static void main(String[] args) {
System.out.println("编号:"+Animal.getId());
Dog d1=new Dog();
d1.shout();
d1.eat();
d1.info();
d1.black();
}
}
多态
多态继承
多态是指不同类的对象调用一个方法时表现出的多种不同行为。
java中多态有以下两种形式:
1.方法的重载
2.对象的多态(方法的重写)
abstract class Animal{
abstract void shout();
}
class Cat extends Animal{
public void shout(){
System.out.println("喵喵");
}
}
class Dog extends Animal{
public void shout(){
System.out.println("汪汪");
}
}
public class test1 {
public static void main(String[] args) {
Cat c1=new Cat();
Dog d1=new Dog();
c1.shout();
d1.shout();
}
}
对象类型的转换
1.向上转型:子类对象——父类对象
2.向下转型:父类对象——子类对象
对象向上转型
对象向上转型,父类对象可以调用子类重写父类的方法,在需要新添功能时,只需要新增一个子类,在子类中对父类的功能进行扩展,而不用更改父类的代码,保证程序安全性。向上转型程序会自动完成,格式如下:
父类类型 父类对象=子类实例;
abstract class Animal{
abstract void shout();
}
class Cat extends Animal{
public void shout(){
System.out.println("喵喵");
}
}
class Dog extends Cat{
public void shout(){
System.out.println("汪汪");
}
}
public class test1 {
public static void main(String[] args) {
Dog d1=new Dog();
Cat c1=d1;
c1.shout();
}
}
对象向下转型
向下转型一般是为了重新获得因为向上转型而丢失的子类特性。对象在向下转型时前,必须先进行向上转型,否则将出现对象转换异常。
向下转型时,必须指明要转为的子类类型。对象向下转型格式如下:
父类类型 父类对象=子类实例;
子类类型 子类对象=(子类)父类对象;
public class test1 {
public static void main(String[] args) {
Dog d1=new Dog();
Cat c1=d1;
c1.eat();
}
}
abstract class Animal{
abstract void shout();
}
class Cat extends Animal{
public void shout(){
System.out.println("喵喵");
}
}
class Dog extends Cat{
public void shout(){
System.out.println("汪汪");
}
public void eat(){
System.out.println("吃大盘鸡");
}
}
public class test1 {
public static void main(String[] args) {
Dog d1=new Dog();
Cat c1=d1;
c1.eat();
}
}
当子类向上转型时,只能将子类中包含父类的方法或属性赋给父类,这样会产生一些不必要的丢失,父类无法调用子类中不属于自己的方法,因为需要向下转型重新获得丢失的子类特性。
public class test1 {
public static void main(String[] args) {
Cat c1=new Dog();//将子类对象向上转型为父类对象
Dog d1=(Dog)c1;//将向上转为父类对象的子类对象向下转型为子类对象
((Dog) c1).eat();
}
}
在向下转型时,不能直接将父类实例强制转换为子类实例。
Dog dog=(Dog)new Cat();
因为此时的父类还不是向上转换类型。
instanceof关键字
instanceof关键字判断一个对象是否是某个类(或接口)的实例,语法格式如下;
对象 instanceof 类(或接口)
class Animal{
void shout() {
}
}
class Dog extends Animal{
public void shout(){
System.out.println("汪汪");
}
public void eat(){
System.out.println("吃大盘鸡");
}
}
public class test1 {
public static void main(String[] args) {
Animal a1 = new Dog();//通过向上转型实例化Animal对象
System.out.println("Animal a1=new Dog():" + (a1 instanceof Animal));
System.out.println("Animal a1=new Dog():" + (a1 instanceof Dog));
Animal a2 = new Animal();
System.out.println("Animal a2=new Dog():"+(a2 instanceof Animal));
System.out.println("Animal a2=new Dog():"+(a2 instanceof Dog));
}
}
抽象类不能实例化自己的对象,子类通过向上转型可以使父类实例化对象。
Object类
object类是所有类的父类,每个类都直接或间接继承了Object类,因此object类通常被称为超类。当定义一个类时,如果没有使用extends关键词为这个类显示地指定父类,那么该类会默认继承object类。Object类方法如下:
class Animal{
void shout() {
}
}
public class test1 {
public static void main(String[] args) {
Animal a1=new Animal();
System.out.println(a1.toString());
}
}
toString 返回了对象a1的字符串表示形式
虽然Animal类没有定义这个方法,但程序并没有报错。这是因为Animal默认继承Object类,同时继承了Object类的toString()方法。
同样的,可以对Object类中的方法进行重写
class Animal{
public String toString(){
return "母鸡会下蛋";
}
}
public class test1 {
public static void main(String[] args) {
Animal a1=new Animal();
System.out.println(a1.toString());
}
}
内部类
在java开发中,允许在一个类的内部定义类,这样的类称为内部类,内部类所在的类称为外部类。
成员内部类
在一个类中除了定义成员变量,成员方法,还可以定义类,这样的类称作为成员内部类。成员内部类可以访问外部类的所有成员,无论外部类的成员是何种访问权限。如果想通过外部类访问内部类,则需要在外部类创建内部类对象。
创建内部类对象的格式如下:
外部类名 外部类对象=new 外部类名();
外部类名.内部类名 内部类对象=外部类对象.new 内部类名();
class Outer{
int m=0;
//外部类方法test1
void test1() {
System.out.println("外部类成员方法 test1()");
}
//定义成员内部类
class Inner{
int n=1;
void show1(){
//在成员内部类的方法中访问外部类的成员变量m
System.out.println("外部类成员变量 m="+m);
//在成员内部类的方法中访问外部类的成员方法test1()
test1();
}
void show2(){
System.out.println("内部类成员方法 show2()");
}
}
// 外部类方法test2
void test2()
{
Inner inner = new Inner();//实例化内部类对象inner
System.out.println("内部类成员变量 n=" + inner.n);
inner.show2();//访问内部类变量和方法
}
}
public class test1 {
public static void main(String[] args) {
Outer outer=new Outer();
Outer.Inner inner=outer.new Inner();
inner.show1();//在内部类中访问外部类的成员变量m和成员方法test1()
outer.test2();//在外部类创建的外部类对象中访问内部类的成员变量n和成员方法show2()
}
}
在内部类中访问外部类,需要在一个方法体中进行访问,直接写在类体中是无法直接访问的,在创建对象时调用方法体访问外部类。
局部内部类
也称方法内部类,是指定义在某个局部范围中的类,它和局部变量都是在方法中定义的,有效范围只限于方法内部。
局部内部类可以访问外部类的所有成员变量和成员方法,而在外部类中无法直接访问局部内部类的变量和方法。如果要在外部类中访问局部内部类的成员,只能在局部内部类的所属方法中创建局部内部类的对象,通过对象访问局部内部类的变量和方法。
class Outer{
int m=0;
//外部类方法test1
void test1() {
System.out.println("外部类成员方法 test1()");
}
//定义成员内部类
class Inner{
int n=1;
void show1(){
System.out.println("外部类成员变量 m="+m);
test1();
}
}
// 外部类方法test2
void test2()
{
class Inner{
int n=1;
void show(){
System.out.println("外部类成员变量 m="+m);
test1();
}
}
//访问局部内部类中的变量和方法
Inner inner = new Inner();
System.out.println("局部内部类成员变量 n=" + inner.n);
inner.show();
}
}
public class test1 {
public static void main(String[] args) {
Outer outer=new Outer();
outer.test2();//通过外部类对象outer调用创建了局部内部类的方法test2()
}
}
静态内部类
静态内部类,就是使用static关键字修饰的成员内部类。与成员内部类相比,静态内部类只是在内部类前增加了static关键字,在功能上,静态内部类只能访问外部类的静态成员,通过外部类访问静态内部类成员时,因为程序已经提前在静态常量区为静态内部类分配好了内存,所以即使静态内部类没有加载,依然可以通过外部类直接创建一个静态内部类对象。
语法格式如下:
内部类名.静态内部类名 变量名=new 外部类名.静态内部类名();
class Outer{
static int m=0;
int n=1;
static class Inner{
void show(){
System.out.println("m:"+m);
}
}
}
public class test1 {
public static void main(String[] args) {
Outer.Inner inner=new Outer.Inner();
inner.show();
}
}
在静太内部类方法中访问了外部类静态变量
class Outer{
static int m=0;
int n=1;
static class Inner{
void show(){
// System.out.println("m:"+m);
System.out.println("n:"+n);
}
}
}
在内部静态类中访问了外部类非静态变量,程序报错
匿名内部类
这里是匿名内部类的详细描述
在java中调用某个方法时,如果该方法的参数是接口类型,那么在传参时,除了可以传入一个接口实现类,还可以传入接口的匿名内部类作为参数,在匿名内部类中实现接口方法,匿名内部类就是没有名称的内部类,在定义匿名内部类时,其类体作为new语句的一部分。
定义匿名内部类的语法格式:
new 继承的父类和实现的接口名(){
匿名内部类的类体
}
interface Animal{
void shout();
}
public class test1 {
public static void main(String[] args) {
String name="小花";
animalshout(new Animal() {
public void shout() {
System.out.println(name + "喵喵...");
}
});
}
public static void animalshout(Animal an){
an.shout();
}
}