继承
继承的概念:
当我们在开发中,有多个类存在相同属性和行为时,就将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承单独的那个类即可。多个类可以称为子类,单独的这个类称为父类或者超类。子类可以直接访问父类中的非私有的属性和行为。通过extends关键字让类与类之间产生继承关系,例如:
class SubDemo extends Demo{
}
继承的出现提高了代码的复用性,它让类与类之间产生了关系,它为多态提供了前提。
特点:
1.Java只支持单继承,不支持多继承。也就是说一个类只能有一个父类,不可以有多个父类。
2.Java支持多层继承,形成继承体系。例如:
class A{}
class B extends A {}
class C extends B {}
注意:
1.不要仅仅为了获取其他类中的某个功能而去继承。
2.类与类之间要有所属关系,即A是B的一种,才可以让A去继承B。
super关键字
super和this的用法相同,只是this代表本类引用,而super代表父类引用。
在子类中定义成员时与父类成员名相同时,可以用super进行区分。
而当子类要调用父类构造函数时,可以使用super语句。
重写
重写override,也称复写,或者函数覆盖,是当子类中出现与父类相同的方法时,会出现覆盖的操作。
父类中的私有方法不可以被覆盖。
在子类的覆盖方法中,继续使用被覆盖的方法可以通过super.函数名来获取
注意:
1.只能用静态方法去覆盖另一个静态方法。
2.覆盖时,子类方法权限一定要大于等于父类方法权限,权限修饰符的大小顺序:public > protected > default > private (其中default是方法的默认权限,不必写出来)
应用:
当子类需要父类的功能,而功能主体子类有自己特有的内容时,可以复写父类中的方法,这样既沿袭了父类的功能,又定义了子类特有的内容。
子类的实例化过程
当我们对子类对象进行初始化时,父类的构造函数也会运行,那是因为子类的构造函数默认第一行有一条隐式的语句super();这条语句会访问父类中空参数的构造函数,而且子类中所有的构造函数默认的第一行都是super();
为什么子类一定要访问父类中的构造函数?
因为父类中的数据可以在子类中直接获取,所以当子类对象建立时,需要先查看父类是如何对这些数据进行初始化的。
如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定。
注意:super语句一定定义在子类构造函数的第一行。
当然,我们在子类的构造函数第一行也可以手动指定this语句来访问本类中的其他构造函数,但至少会有一个子类访问到父类的构造函数,导致其他子类也访问到父类的构造函数。
public class thisClass extends superClass{
static String name = "这是儿子";
thisClass(){
this(0);
System.out.println("儿子");
}
thisClass(int num){
super(num);
System.out.println("这是子类哦哦哦哦哦哦"+num);
}
protected void getName(){
System.out.println("hello this is your son "+name);
}
class superClass{
String name = "我是父亲";
superClass(int num){
System.out.println(num);
}
void getName(){
System.out.println(name+"hello好久不见");
}
}
class Extends {
public static void main(String[] args){
thisClass tc = new thisClass();
tc.getName();
}
}
在这段代码中,子类thisClass继承了父类superClass,父类中没有默认的空参数构造函数,这时在子类的构造函数中必须指定继承了父类的哪个构造函数,或者用this语句来指定创建了同类中哪一个构造函数,否则,编译无法通过。
final关键字
final可以修饰类,方法和变量。
被final修饰的类不可以被继承。
被final修饰的方法不可以被覆盖。
被final修饰的变量只是一个常量,只能被赋值一次。
内部类只能访问被final修饰的局部变量。
抽象类
抽象类和一般类没有太大的不同。该如何描述事物,就如何描述事物,只不过,该事物出现了一些看不懂的东西。这些不确定的部分,也是该事物的功能,需要明确出现,但是无法定义主体,通过抽象方法来表示。
抽象类比一般类多个了抽象函数,所以类中可以定义抽象方法,但不可以实例化。
抽象定义:
抽象就是从多个事物中将共性的,本质的内容抽取出来。
例如,狼和狗共性都是犬科,犬科就是抽象出来的概念。
抽象类:
Java中可以定义没有方法体的方法,该方法的具体实现由子类去完成,该方法称为抽象方法,包含抽象方法的类就是抽象类。
抽象方法的由来
多个对象都具有相同的功能,但是功能具体内容有所不同,所以我们在抽取过程中,只抽取了功能定义,并不抽取功能主体,然后这些只有功能声明而没有功能主体的方法就称为抽象方法。
例如:狼和狗都有吼叫的方法,可是吼叫的内容是不一样的。所以抽象出来的犬科虽然有吼叫的功能,但是并不明确吼叫的细节。
抽象类的特点
抽象类和抽象方法必须用abstract关键字来修饰。在类中只要定义了一个abstract方法,这个类也要被定义为abstract。抽象类通过其子类实例化,而子类需要去覆盖掉抽象类中的抽象方法后才可以创建对象,否则该子类也是抽象类。抽象方法只有方法声明,没有方法体,定义在抽象中。格式:
修饰符 abstract 返回值类型 函数名(参数列表);
抽象类不可以被实例化,也就是不可以用new来创建对象。原因如下:
1.抽象类是具体事物抽取出来的,本身是不具体的,没有对应的实例。例如:犬科就是一个抽象的概念,真正存在的是狼和狗。
2.而且抽象类即使创建了对象,调用抽象方法也没有意义。
注意:
1.抽象类中也可以没有抽象方法,这样子仅仅是为了不让该类创建对象。
2.abstract关键字不可以与final关键字共存,被final修饰的类不能有子类,而被abstract修饰的类一定是一个父类。所以它们是无法共存的。
3.abstract关键字不可以与private关键字共存,抽象类中的私有的抽象方法,不被子类所知,就无法被复写。 而抽象方法是必须被复写的。
4.abstract关键字不可以与static关键字共存,如果用static修饰抽象方法,那么连对象都省了,直接类名调用就可以了。可是抽象方法没有运行意义。
5.抽象类也需要构造函数,虽然本身无法被实例化,但它要为它的子类的提供实例的初始化。
抽象类示例:
abstract class Student
{
abstract void study();
//抽象类中也可以有不抽象的方法:
void sleep()
{
System.out.println("躺着");
}
}
class ChongCiStudent extends Student
{
void study()
{
System.out.println("chongci study");
}
}
class BaseStudent extends Student
{
void study()
{
System.out.println("base study");
}
}
class AdvStudent extends Student
{
void study()
{
System.out.println("advanced study");
}
}
class AbstractDemo
{
public static void main(String[] args)
{
new BaseStudent().study();
}
}
在父类Student中我们是不需要描述study方法的,具体是个怎样的study就交由子类去重写。
接口
接口定义
我们可以把接口理解为一个特殊的抽象类,当抽象类中的方法都是抽象的时候,那么该类可以通过接口的形式来表示。
class用于定义类
interface 用于定义接口。
在定义接口时,接口中的成员都有固定修饰符,应遵循以下格式:
常量:public static final
方法:public abstract
接口的特点:
1.接口是对外暴露的规则,是程序的功能扩展。
2.类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口。
3.接口与接口之间可以有继承关系,而且一个接口可以继承多个接口。
注意
1.接口是不可以创建对象的,因为有抽象的方法,它需要被子类实现implement,子类对接口中的抽象方法全都覆盖后,子类才可以实例化。否则子类是一个抽象类。
2.一个类可以实现多个接口,也是对多继承不支持的转换形式,Java支持多实现。
interface Inter
{
public static final int NUM = 3;
public abstract void show();
}
interface InterA
{
public abstract void show();
}
class Demo
{
public void function(){}
}
class Test extends Demo implements Inter,InterA //Java支持单继承多实现。
{
public void show(){} //在实现接口时要复写接口中所有的抽象方法。
}
interface A
{
void methodA();
}
interface B
{
void methodB();
}
interface C extends B,A //接口可以多继承
{
void methodC();
}
class D implements C
{
public void methodA(){} //要复写三个方法
public void methodB(){}
public void methodC(){}
}
class InterfaceDemo
{
public static void main(String[] args)
{
Test t = new Test();
System.out.println(t.NUM);
System.out.println(Test.NUM);
System.out.println(Inter.NUM);
}
}
多态
多态定义:
可以理解为事物存在的多种体现形态。例如:动物中的猫,狗,多态的出现大大的提高程序的扩展性和后期可维护性。
猫这个对象对应的类型是猫类型。
猫 x = new 猫();
同时猫又是动物中个的一种,也可以把猫称为动物:
动物 y = new 猫();
动物是猫和狗具体事物中抽取出来的父类型,父类的引用指向了自己的子类对象。另外父类的引用也可以接收自己的子类对象。
多态的前提
1.必须是类与类之间有关系。要么继承,要么实现。
2.通常还有一个前提:存在覆盖。
4,多态的弊端:
提高了扩展性,但是只能使用父类的引用访问父类中的成员。
多态的特点
在多态中成员函数的特点:
在编译时期:参阅引用型变量所属的类中是否有调用的方法。如果有,编译通过,如果没有编译失败。
在运行时期:参阅对象所属的类中是否有调用的方法。
简单总结就是:成员函数在多态调用时,编译看左边,运行看右边。
在多态中,成员变量的特点:
无论编译和运行,都参考左边(引用型变量所属的类)。
在多态中,静态成员函数的特点:
无论编译和运行,都参考做左边。
注意
1.当我们使用父类的引用指向自己的子类对象时,它隐式的将类型提升了,我们称之为向上转型。比如:
Animal a = new Cat(); //类型提升,向上转型。
这时候如果要调用子类的特有方法时,要强制将父类的引用转成子类类型,我们称之为向下转型。比如:
<pre name="code" class="java">Animal a = new Cat();
Cat c = (Cat)a;
一般在做这个操作前,会先判断这个父类引用是否指向了这一个子类对象,用instanceOf,如果是,才可以向下转型,否则编译失败。例如:
<pre name="code" class="java"><pre name="code" class="java">Animal a = new Cat();
if(a instanceof Cat){
Cat c = (Cat)a;
System.out.println("成功的向下转型");}
2.多态自始至终都是子类对象在做着变化
class animal {
void eat() {
}
}
class dog extends animal {
void eat() {
System.out.println("啃骨头");
}
void kanMen() {
System.out.println("看门ing");
}
}
class tiger extends animal {
void eat() {
System.out.println("吃动物");
}
void catchAnimal() {
System.out.println("抓动物来吃");
}
}
class cat extends animal {
void eat() {
System.out.println("吃鱼");
}
void catchMouse() {
System.out.println("抓老鼠");
}
}
public class duotai {
public static void main(String[] args) {
animal xiaomao = new cat();
xiaomao.eat();
function(xiaomao);
animal xiaogou = new dog();
xiaogou.eat();
function(xiaogou);
toEat(xiaomao);
toEat(xiaogou);
}
public static void toEat(animal a ){
a.eat();
}
public static void function(animal a) {
if (a instanceof cat) {
cat c = (cat) a;
c.catchMouse();
} else if (a instanceof dog) {
dog c = (dog) a;
c.kanMen();
} else if (a instanceof tiger) {
tiger c = (tiger) a;
c.catchAnimal();
}
}
}
内部类
内部类的定义
将一个类定义在另一个类里面,对里面那个类就称为内部类,也称内置类或者嵌套类。
当描述事物时,事物的内部还有事物,该事物用内部类来描述,因为内部事务在使用外部事物的内容。
内部类的访问规则:
1,内部类可以直接访问外部类中的成员,包括私有。
之所以可以直接访问外部类中的成员,是因为内部类中持有了一个外部类的引用,格式 外部类名.this
2,外部类要访问内部类,必须建立内部类对象。
内部类的特点
(注意,这里的outer和Iner分别指的是外部类和内部类)
1、内部类可以直接访问外部类的成员,包括私有。
2、外部类要访问内部类的话,要先创建内部类的对象。
3、内部类可以被private修饰。
4、在其他类中想访问内部类,要用此格式:
Outer.Iner.xxx()
或者
new Outer().new Iner();
4、其他类访问内部类的非静态成员:
new Outer.Iner().run();
5、其他类访问内部类的静态成员:
Outer.Iner.run();
(也即不用创建对象即可访问静态方法。)
6、匿名内部类一定是一个匿名子类对象。格式:
new 父类或者接口(){定义子类的内容}。
注意
1.当内部类中定义了静态成员,该内部类必须是static的。
2.注意:用外部类的静态方法访问内部类时,该内部类也必须是静态的。
3.当内部类定义在成员位置上时,它可以被private static成员修饰符修饰,但被static修饰的内部类只能访问外部类中的静态成员。
4.当内部类定义在局部位置上时,它不可以被成员修饰符修饰,可以直接访问外部类中的成员,因为还持有外部类中的引用。但是不可以访问它所在的局部中的变量。只能访问被final修饰的局部变量。
public class OuterAndIner {
public static void main(String[] args) {
Outer.Iner in = new Outer().new Iner();
in.method();
}
}
class Outer {
private int x = 3;
class Iner {
int x = 5;
void method() {
System.out.println("x=" + x);
}
}
void method() {
Iner in = new Iner();
in.method();
}
}
匿名内部类
匿名内部类是内部类的一种简化写法。它其实是一个匿名的子类对象,只是这个对象有点胖,你可以理解为带内容的对象,或者是外部类或者接口的一个带内容的子类匿名对象。(好像更复杂了。。)
定义匿名内部类的前提:
内部类必须是继承一个类或者实现接口。
匿名内部类的格式
new 父类或者接口(){定义子类的内容}
注意
匿名内部类中定义的方法最好不要超过3个。
class neibu {
void show() {
System.out.println("匿名内部类 run");
}
}
class user {
public static void main(String[] args) {
new neibu(){
void heihei(){
super.show();
System.out.println("哈哈");
}
}.heihei(); //匿名内部类的使用方法1
function().show();
}
static neibu function(){ //匿名内部类的使用方法2
return new neibu(){
void show(){
super.show();
System.out.println("不仅用了父类方法而且还复写了哦");
}
};
}
}