面向对象(二)
一、继承。
1、继承概述。
请先看一个例子:在现实生活中,人们有各种各样的分类,比如有的人是学生,有的人是教师,有的人是工人......这些都是现实世界可以存在的事物,而具体到某一个人则是该事物的对象了。但是我们发现,在这么多的类中,它们都有一种共性和联系——它们都是人。
所谓继承,就是当多个类中存在相同的属性和行为时,将这些属性和行为单独提取出来构成一个类,那么多个类都无需再定义这些属性和行为,而只需要继承单独的那个类。多个类可以称其为父类,单独的这个可以叫做父类或者超类,子类可以访问父类中的非私有属性和行为。
子类是通过关键字extends产生与父类的关系的,例:
package com.itheima;那么继承的出现有什么好处呢?
class Person{
private String name;
private int age;
Person(String name,int age){
this.age=age;
this.name=name;
}
}
class Student extends Person{
private int studentidcard;
Student(String name,int age,int studentidcard){
super(name,age);
this.studentidcard=studentidcard;
}
}
继承出现提高了代码的复用性,而且让类与类之间产生了联系,为多态提供了前提。但是,注意千万不要单纯的为了代码的复用性来使用继承,子类和父类之间一定要有逻辑上的继承关系才能使用继承。
2、继承的特点。
在java体系当中,它只支持单继承,不支持多继承。因为多继承容易带来安全问题,当多个父类定义了相同的功能,但是功能内容不同时,子类对象不确定调用哪一个,但是java中保留这种多继承机制,在这里称之为多实现。
class Student extends Person//right在java体系中还支持多层继承——C继承了B,B继承了A,这样就构成了一个继承体系,在javaapi中这是最常见的现象,将一个个有共同属性和行为的事物抽象作为其继承类,然后将该继承类再抽象变成最根本的抽象类(一般都会成为接口)。
class Student extends Person,HeiMa//Error
class Personclass Student extends Personclass HeiMaStudent extends Student如上,Person类是这个继承体系的最上级类,然后一层一层的被继承下来,到现实中的具体事物。
在javaapi中经常遇到这样的继承体系,我们如何使用它呢?
要想使用体系,先查阅体系的父类描述,因为父类中定义了该体系中的共性功能。通过了解共性功能,我们就可以知道该体系的基本功能,然后这个体系基本上就可以使用了。注意:在具体调用时,要创建最子类的对象,这是因为体系中的父类基本上都是抽象类和接口,不能创建对象,而且子类对象有父类所没有的独特功能。
package com.itheima;
class Person
{
void show()
{
System.out.println("这是一个最上级类-人");
}
}
class Student extends Person
{
void show()
{
System.out.println("我继承了人,我是人中的学生");
}
}
class HeiMaStudent extends Student
{
void show()
{
System.out.println("我继承了学生,我现在是黑马训练营的学生啦!");
}
}
public class ExtendsDemo
{
public static void main(String[] args)
{
HeiMaStudent s1 = new HeiMaStudent();
s1.show();
}
}
3、super关键字和继承后成员特点
super关键字和this关键字相对应。
this代表本类引用,super代表父类引用。
当子父类出现后,类成员的特点:
成员变量的特点:
如果子父类中出现了同名的非私有成员变量,子类访问本类中变量用this,要访问父类变量则用super,父类中的私有变量是没办法被子类访问的。
package com.itheima;
class Student
{
int x=4;
void show()
{
System.out.println("我继承了人,我是人中的学生");
}
}
class HeiMaStudent extends Student
{
int x=5;
void showHeiMa()
{
System.out.println("我继承了学生,我现在是黑马训练营的学生啦!"+"我要求我的父类的x"+super.x);
}
}
public class ExtendsDemo
{
public static void main(String[] args)
{
HeiMaStudent s1 = new HeiMaStudent();
s1.showHeiMa();
System.out.println(s1.x+"-----"+s1.x);//注意,用对象。的方式调用成员变量只能是子类的对象。
}
}
子父类成员函数的特点: 当子父类出现了相同函数名和相同的参数个数和参数类型的——即一模一样的函数时,如果子类对象调用该函数,会调用子类函数的内容,如同父类该函数被覆盖了一样,这就是函数的另一个特性:复写(覆盖)——子类继承了父类的功能但是功能却与父类不一样,这时没比较重新定义函数,而是使用函数覆盖,保留父类的函数功能定义,但是重写其内容。 注意:子类覆盖父类,必须保证子类该函数的权限大于父类权限;静态方法只能覆盖静态,这是因为静态存在于方法区中,且优先于对象存在,要覆盖静态方法只能是静态的才行。 与重载的区别:重载函数是只有函数名相同但是参数个数和参数类型都不相同,而覆盖函数是要函数名和参数个数和参数类型全部相同才行,所以在子类中覆盖一个函数的时候请一定要注意父类该函数是如何定义的,注意其参数的个数和类型,特别是覆盖Object类的时候,比如equals()方法,其内部的参数是Object类型,若写错了类型则会变成函数的重载。
package com.itheima;子父类构造函数的特点: 在子类对象初始化的时候,父类的构造函数也会运行。对于一般子类是默认的构造函数的时候,该函数的第一行有一句super()——调用父类的空参数构造函数,子类的每一个构造函数中若没有调用父类构造函数则会默认其调用了super()。若父类没有默认构造函数,则必须在定义子类构造函数的时候将父类的自主定义构造函数放在该函数的第一行。 为什么子类一定要调用父类的构造函数? 这是因为子类中继承了父类的成员,在子类对象初始化的时候需要借鉴父类对这些继承成员的初始化内容 注意:在构造函数中,调用了This()就不能调用super()因为它俩都是用在构造函数第一行的,但是功能不同,前者是调用本类构造函数,后者是调用父类构造函数,在有多个构造函数的时候,一定会有一个构造函数调用super()然后可以让其他构造函数互相调用。
class Student
{
int x=4;
void show()
{
System.out.println("我继承了人,我是人中的学生");
}
}
class HeiMaStudent extends Student
{
int x=5;
void show()//该函数会覆盖Student类中的show()方法!
{
super.show();
System.out.println("我继承了学生,我现在是黑马训练营的学生啦!"+"我要求我的父类的x"+super.x);
}
void show(int x)//这个就是函数的重载了!
<span style="white-space:pre"></span>{
<span style="white-space:pre"></span>this.x=x;
<span style="white-space:pre"></span>}
}
public class ExtendsDemo
{
public static void main(String[] args)
{
HeiMaStudent s1 = new HeiMaStudent();
s1.show();
System.out.println(s1.x+"-----"+s1.x);//注意,用对象。的方式调用成员变量只能是子类的对象。
}
}
package com.itheima;
class Student
{
String name;;
int age;
Student(String name ,int age)
{
this.age=age;
this.name=name;
}
void show()
{
System.out.println("这个人的属性有:"+name+","+age);
}
}
class HeiMaStudent extends Student
{
int studentid;
HeiMaStudent(String name,int age,int studentid)
{
super(name,age);//此处一定要定义父类构造函数,因为父类没有默认构造空函数
this.studentid=studentid;
}
void show()//该函数会覆盖Student类中的show()方法!
{
super.show();
System.out.println("我现在是黑马训练营的学生啦!我的属性有"+age+","+name+","+studentid);
}
void show(String name)//这个就是函数的重载了!
{
super.name=name;//此处修改父类的成员也就是修改子类的成员!
System.out.println("我现在是黑马训练营的学生啦!我的属性有"+age+","+this.name);//在子类中调用父类的成员(该成员不和子类的重名)的时候使用this和super是一样的。
}
}
public class ExtendsDemo
{
public static void main(String[] args)
{
HeiMaStudent s1 = new HeiMaStudent("张三",22,20141121);
s1.show();
s1.show("李四");
}
}
4、final关键字
final关键字的意思是最终,它是一个修饰符,其特点如下: 1、它可以修饰类,成员(包括成员函数和成员变量) 2、被final修饰的类是不能被继承的,这是为了防止一些类被继承或者其某些功能被复写3、被final修饰的成员函数不能被复写 4、被final修饰的成员变量只能赋值一次,可以修饰成员变量也可以修饰局部变量 5、内部类被定义在局部上,该内部类只能访问该类被final修饰的变量。
package com.itheima;
public class FinalDemo
{
public static void main(String[] args)
{
Demoer dm = new Demoer();
dm.show();
}
}
class Demoer
{
final String x = "张三";//只能赋值一次
public final void show()
{
System.out.println("这个成员函数是无法被复写的");
final class Inner//无法被继承
{
int x=2;
public void inDemo()
{
System.out.println(x+","+this.x+","+Demoer.this.x);
}
}
Inner i=new Inner();
i.inDemo();
}
}
5、抽象类
所谓抽象,就是将共性的本质的内容抽取出来,在java中有一些没有方法体的方法,这些方法的具体实现由子类完成(通过子类的复写),这种方法称为抽象方法,包含抽象方法的类就是抽象类。当多个类出现相同的功能但是功能主题不同,这时可以向上抽取,但是不能抽取具体方法,只能抽取功能定义。 抽象类的特点: 1、抽象方法一定在抽象类中 2、抽象类和抽象方法都必须被abstract关键字修饰 3、抽象类是不可以被实例化的,所以有一些类尽管不是抽象类也可以加上关键字abstract来表示这个类是没法实例化的,另一种方法就是给构造函数私有化,这种方法在单例设计模式中涉及到了! 4、抽象类中抽象方法要被使用就必须在子类中进行复写,建立子类对象调用,若子类只是覆盖了一部分抽象类,则这个 子类也是抽象类,简单的说,在一个类中只要有一个方法是抽象的,整个类都是抽象的。 其实抽象类和一般类没有什么不同,该如何描述事物就如何描述事物,但是如果事物出现了一些看不懂的东西,这些不确定的部分就用抽象方法来描述。 注意:abstract类不能和一下这几个关键字搭配修饰: final:被final修饰的类不能被继承,而abstract修饰的类必须要被继承,冲突;而被final修饰的成员函数不能被复写,也是和abstract冲突 private:被private修饰的成员方法只能在本类对象内部调用,所以没法被继承和复写,所以也是和abstract冲突的 static:被static修饰的成员函数会直接在方法区中存放,被类直接调用,必须是具体的方法,若是抽象方法没有具体内容没意义。、 抽象类是又构造函数的,用于子类的构造。package com.itheima;
public class AbstractDemo
{
}
abstract class Process
{
abstract public void konwCustomerIdea();//作为一个程序员,要编程首先要了解用户需求
abstract public void designAlgorithm();//设计算法
abstract public void makeprocess();//开始编程
abstract public void deBugging();//调试程序
public void startDesignPrecess()
{
this.konwCustomerIdea();
this.designAlgorithm();
this.makeprocess();
this.deBugging();
}
}
class Myprocess extends Process
{
public void konwCustomerIdea() {}
public void designAlgorithm() {}
public void makeprocess() {}
public void deBugging() {}
}
6、模板设计模式。
在定义功能的时候,有一部分是确定的,这些方式可以定义成一个类的方法,但是有一些是不确定的,那么我们就定义这个不确定的成一个抽象方法,该类也编程了抽象类,这样,在使用其子类的时候复写那些不确定的方法就完整描述了整个事物
package com.itheima;
public class AbstractDemo
{
public static void main(String[] args)
{
MyProcess mp=new MyProcess();
mp.startDesignPrecess();
}
}
abstract class Process
{
abstract public void konwCustomerIdea();//作为一个程序员,要编程首先要了解用户需求
abstract public void designAlgorithm();//设计算法
abstract public void makeprocess();//开始编程
abstract public void deBugging();//调试程序
public void startDesignPrecess()
{
this.konwCustomerIdea();
this.designAlgorithm();
this.makeprocess();
this.deBugging();
}
}
class MyProcess extends Process
{
public void konwCustomerIdea()
{
System.out.println("开始了解用户的需求!");
}
public void designAlgorithm()
{
System.out.println("开始设计算法!");
}
public void makeprocess()
{
System.out.println("开始编写程序!");
}
public void deBugging()
{
System.out.println("开始进行程序调试!");
}
}
如以上代码,这就是一个典型的模板设计模式,所谓模板就是我们已经了解到的这一部分方式,这些方式一般来说就是做某些事的一些流程,而这些流程的某一步具体怎么做我们不确定,这样就可以定义一个抽象方法来描述,然后在子类中复写这些流程的具体步骤,这样就完整的表述了该事物。
7、接口。
接口就是一个抽象类,该抽象类内部全是抽象方法。
接口用interface定义,class定义类
接口定义时需要注意以下问题:接口中常见定义的是成员常量和抽象方法。
定义成员常量格式为:public static final;定义抽象方法格式为:public abstract
接口中的所有成员都是public的,这是为了让这些成员都能被继承,函数被复写。
接口的特点:接口是对外暴露的规则,是对程序功能的扩展,程序多一个接口就多了若干可以复写的功能,充实了程序的功能性。
接口是用来多实现的,一个类可以实现多个接口,这就是java中的“多继承”,用implements关键字来实现。
接口和接口之间是可以继承的!
二、多态
1、概述
在现实生活中,总有那么一些事物有多重形态。例如:动物中的猫和狗,猫这个事物建立对象可以是猫 cat = new 猫();同时,猫也是动物的一种,所以也就可以这样定义:动物 cat=new 猫();其中动物明显是猫的父类。所以,所谓多态,就是父类的引用指向了子类对象。
要实现多态,很明显的需要类和类之间存在继承或者类和接口之间存在实现关系,而且必须存在覆盖操作。
多态有很多好处:它大大提高了程序的扩展性,但是只能使用父类引用调用父类的成员。
public class DuoTaiDemo
{
public static void main(String[] args)
{
Animal cat = new Cat();
Animal ani = new Animal();
Cat c=new Cat();
cat.show();
ani.show();
c.show();
}
}
class Animal
{
public void show()
{
System.out.println("这是一个动物!");
}
}
class Cat extends Animal
{
public void show()
{
System.out.println("这是一只猫!");
}
}
2、在多态中如何让父类引用来调用子类中特有方法。
我们知道,在多态中,可以让父类引用调用子类中覆盖了父类的方法,这也是多态最常见的引用,但是当我们需要调用子类特有的方法的时候,该怎么办呢?
这时,我们需要将该多态的类型转换成子类类型,比如,对动物 cat = new 猫();来说我们需要调用cat对象的特有方法,就需要猫 cat1=(猫)cat,这时强制类型转换!
package com.itheima;
public class DuoTaiDemo
{
public static void main(String[] args)
{
Animal cat = new Cat();
Animal ani = new Animal();
Cat c=new Cat();
cat.show();
ani.show();
c.show();
show(cat);
}
public static void show(Animal a)
{
a.show();
}
}
class Animal
{
public void show()
{
System.out.println("这是一个动物!");
}
}
class Cat extends Animal
{
public void show()
{
System.out.println("这是一只猫!");
}
}
3、多态的特点
在成员函数来说:编译时,参阅引用类型的类中是否有此函数,若有则编译通过;在运行时则是运行子类的函数。
在变量来说:不论编译还是运行时都是查阅的引用类型的类成员。
在静态成员变量来说:不论编译还是运行都是查阅引用类型的类的静态成员。
package com.itheima;
abstract class Fu
{
int x=3;
abstract public void show();
public static void show1()
{
System.out.println("这里一个父类的静态方法");
}
}
class Zi extends Fu
{
int x=4;
public void show()
{
System.out.println("这是一个子类");
}
public static void show1()
{
System.out.println("这里是子类中的静态方法!");
}
}
public class DuoTai2
{
public static void main(String[] args)
{
Fu z=new Zi();
System.out.println("x="+z.x);//这里调用到的是x=3
z.show();//这里调用的是子类的show()方法,被复写了
z.show1();//这里调用的是父类的静态方法。
}
}
三、内部类
1、概述。
内部类就是在一个类的内部在定义一个类,在描述一个现实事物中,若它内部还有事物,则用内部类来描述,因为内部事物使用了外部事物的内容。它也被叫做嵌套类或者内置类,它的访问特点是:内部类可以直接访问外部类成员,之所以能够直接访问外部类对象是因为内部类有一个外部类的引用——外部类名.this,而外部类要访问内部类则需要在外部建立对象。
2、访问格式。
当内部类定义在类的成员位置时:
如果不是私有化的,则可以再外部其他类中直接建立内部类对象,其格式为:Outer.Inner in=new Outer().new Inner();
如果被static和private修饰
被private修饰,则内部类只能在外部类中建立对象,被外部类所封装。
被static修饰,整个类存在于方法区中,则内部类中只能访问外部类的静态成员,访问权限受到限制。此时,在外部其他类中可以直接访问该内部类静态成员:Outer.Inner.function();也可以访问该静态内部类的非静态成员:new Outer.Inner() ;
注意:静态函数内部一定是静态的,但是静态内部类内部有静态的也有非静态的!
当内部类定义在局部位置时:它依然能够直接访问外部类成员,因为它的外部类名.this引用依然存在,但是它只能访问带有final修饰的局部变量。
package com.itheima;
class Outer
{
int x=3;
static int y=2;
class Inner1
{
int x=4;
public void show()
{
int x=5;
System.out.println(Outer.this.x+","+this.x+","+x);//5,4,3
}
}
static class Inner2//不能访问外部内的非静态成员
{
int z=1;
public void show()
{
System.out.println(Outer.y+","+this.z);
}
public static void show1()
{
System.out.println(Outer.y);
}
}
}
public class InnerDemo
{
public static void main(String[] args)
{
Outer.Inner1 in = new Outer().new Inner1();//非私有定义在成员位置上,在其他外部类中创建内部类对象
in.show();
new Outer.Inner2().show();//成员位置上的静态内部类访问非静态成员
Outer.Inner2.show1();//成员位置上的静态内部类访问静态成员
}
}
3、匿名内部类。
匿名内部类其实就是内部类的简写格式,它的使用前提是它必须继承了一个类或者实现了一个接口。
它的格式是:new 父类或者接口名(){定义子类的内容}。
匿名内部类就是一个子类对象,可以理解为带内容的对象,这个对象有些胖。
注意:匿名内部类中子类内容最好不要超过三个成员函数,不然阅读性非常的差。
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------