------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
1.继承
1.1继承的概述
继承是指一个对象直接使用另一对象的属性和方法1.2继承的特点
1.提高了代码的复用性
2.继承让类与类之间产生了关系,有了这个关系才有了多态的特性
注意:千万不要为了获取其他类的功能,简化代码而继承;必须是类与类之间有所属关系才可以继承.所属关系 is `a
Java语言中:java只支持单继承,不支持多继承(因为多继承容易带来安全隐患)
当多个父类中定义了相同的功能,当功能内容不同时,子类对象不确定运行哪个
但是java保留这种机制,并用另一种体现形式来完成表示,称为多实现
示例:Java实现多层继承
class A{
<span style="white-space:pre"> </span>public void show(){
<span style="white-space:pre"> </span>System.out.println("hello");
<span style="white-space:pre"> </span>}
}
class B extends A{
<span style="white-space:pre"> </span>public void show(){
<span style="white-space:pre"> </span>System.out.println("dodo");
<span style="white-space:pre"> </span>}
}
class C extends A,B{//错误代码,类不能多继承
<span style="white-space:pre"> </span>
}
C继承力A,B两个类,但是输出show方法时,不知道输出的是谁的
1.3如何使用一个继承体系的功能呢?
想要使用体系,先查阅体系父类的描述,因为父类中定义的是该体系中共性功能
通过了解共性功能,就可以知道该体系的基本功能
那么这个体系已经可以基本使用了
那么在具体调用时,要创建最子类的对象,为什么呢?
1. 有可能父类不能创建对象
2. 创建子类对象可以使用更多的功能,包括基本的,也包括特有的
简单一句话:查阅父类功能,创建子类对象使用功能
1.4子父类出现后,类成员的特点:
1.变量:
父类中和子类中有相同的非私有的成员变量时:
子类要访问本类中的变量,用this
子类要访问父类的同名变量,用super
super 的使用和this的使用几乎一致
this 代表的是本类对象的引用
super代表的是父类对象的引用
注意:一般父类中定义了一个变量后,子类是不需要定义此变量的,因为继承以后,子类中有了父类的变量,在定义就重复了
2.函数:
当子类出现和父类一模一样的函数时
当子类对象调用该函数,会运行子类函数的内容
如同父类的函数被覆盖一样
这种情况是函数的另一种特性:重写(覆盖)
当子类继承了父类,沿袭了父类的功能到子类中,但是子类也具备该功能,但是功能的内容却和父类不一致,这是,没有必要定义新功能,而是使用覆盖特性,保留父类的功能定义,重写父类的内容
子类覆盖父类:
1.必须保证子类权限大于等于父类权限,才可以覆盖,否则编译失败
2.静态只能覆盖静态
重载和重写:
重载:只看同名函数的参数列表
重写:字符类方法要一模一样
3.构造函数:
class Fu
{
Fu(){
System.out.println("Fu run");
}
}
class Zi extends Fu
{
Zi(){
System.out.println("Zi run");
}
}
class ExtendsDemo2
{
public static void main(String[] args){
Zi z=new Zi();
}
}
执行结果:
注意: 子类的构造函数在函数的第一句有一条隐式的语句,为super()
super(): 会访问父类中空参数的构造函数,而且子类中所有的构造函数默认第一行都是super();
为什么子类一定要访问父类的构造函数?
1. 因为父类中的数据子类可以直接获取,所以子类对象在建立时需要先查看父类是如何对这些数据进行初始化的.所以子类在对象初始化时,要先访问一下父类中的构造函数.
2. 如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定
注意:super语句一定定义在子类构造函数的第一行
父类中的构造方法已经写好,子类直接拿过来用:
class Person
{
String name;
Person(String name){
this.name=name;
}
}
class Student extends Person
{
Student(String name){
super(name);//调用父类的非空的构造函数
}
}
注意事项:
class Fu
{
Fu(){
System.out.println("Fu run");
}
}
class Zi extends Fu
{
Zi(){
//super() 因为子类一定会调用父类的构造函数,所以空构造函数会调用子类的构造函数
System.out.println("zi kong");
}
Zi(int x){
this(); //因为this和super只能放在第一行,所以此时调用的是空构造函数
System.out.println("Zi run");
}
}
class ExtendsDemo2
{
public static void main(String[] args){
Zi z=new Zi(3);
}
}
执行结果:
fu run
zi kong
zi run
1.5子类的实例化过程:
结论:
子类所有的构造函数,默认都会访问父类中空参数的构造函数
因为子类每一个构造函数内的每一行都有一句隐式super()
当父类中没有空参数的构造函数时,子类必须手动通过super语句形式来指定要访问父类的构造函数
当然: 子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数,但是子类中至少会有一个构造函数会访问父类中的构造函数
1.6 final关键字
继承的弊端是打破了封装性
final可以修饰类,方法,变量
final修饰的类不可以被继承
final修饰的方法不可以被覆盖
final修饰的变量是一个常量,只能被赋值一次
既可以修饰成员变量,也可以修饰局部变量
当在描述事物时,一些数据的出现值是固定的,那么这时为了增强阅读性,都给这些值起个名字,方便于阅读
而这个值不需要改变,所以加上final修饰
作为常量(书写规范):常量的书写规范所有的字母都大写,如果由多个单词组成,下划线链接
内部类定义在类中的局部位置上时,只能访问被final修饰的局部变量
2.抽象类与接口
2.1抽象类:
2.1.1抽象类的特点:
1. 抽象方法一定定义在抽象类中
2. 抽象方法和抽象类都必须被abstract关键字修饰
3. 抽象类不可以用new创建对象,因为调用抽象方法没意义
4. 抽象类中的抽象方法要被使用,必须由子类复写其所有的抽象方法后,建立子类对象调用
注:如果子类只覆盖部分抽象方法,那么该子类还是一个抽象类
2.1.2抽象类和一般类的区别:
抽象类和一般类没有太大的不同
该如何描述事物,就如何描述事物,只不过,该事物出现了一些看不懂的东西.
这些不确定的部分,也是该事物的功能,需要明确出现,但是无法定义主体,通过抽象方法来表示.
抽象类比一般类多了抽象方法,就是在抽象类中可以定义抽象方法
抽象类不可以实例化
特殊:抽象类中可以不定义抽象方法,这样做仅仅是不让改类建立对象
需求:获取一段程序运行时间
原理:获取程序开始和结束的时间并相减即可
获取时间:System.currentTimeMillis();
当代码完成优化后,就可以解决这类问题,这种方式叫做模板方法设计模式
什么是模板方法呢?
在定义功能时,功能的一部分是确定的,但是有一部分是不确定的,而确定的部分在使用不确定的部分,那么这时就将不确定的部分暴露出去
abstract class GetTime
{
public final void getTime(){
long start=System.currentTimeMillis();
runCode();
long end=System.currentTimeMillis();
System.out.println("程序运行毫秒数:"+(end-start));
}
public abstract void runCode();
}
class RunDemo extends GetTime
{
public void runCode(){
for(int i=0;i<4000;i++){
System.out.print(i);
}
}
}
class ExtendsFinalDemo
{
public static void main(String[] args){
RunDemo rd=new RunDemo();
rd.getTime();
}
}
2.2接口:
2.2.1格式:
interface 接口名{
}
2.2.2接口中的成员修饰符是固定的.
1. 成员常量: public static final
2. 成员函数: public abstract
接口的出现将”多继承”通过另一种形式体现出来,即”多实现”
接口: 初期理解,可以认为是一个特殊的抽象类
当抽象类中的方法都是抽象的,那么该类可以通过接口的形式来表示.
关键字: interface
注意: 接口中的成员都是public
接口:是不可以创建对象的,因为有抽象方法
需要被子类实现,子类对接口中的抽象方法全部覆盖后,子类才可以实例化,否则子类是一个抽象类
2.2.3接口可以被类多实现:
不同接口的同名方法都没有方法体,实现谁都一样,但是类不能多继承,是因为不同的类可能有同名的方法名,但是方法体的内容有可能不同
一个类在继承一个类的同时还可以实现多个接口:
2.2.4接口可以继承多个接口:
但是这样的方法不可以:两个接口中方法名相同,但是返回值类型不同interface A
{
int show();
}
interface B
{
boolean show();
}
interface C extends A,B //接口C继承了A,B两个接口,但是两个方法的返回值不同
{
}
class InterfaceDemo implements C
{
public static void main(String[] args){
InterfaceDemo i=new InterfaceDemo();
i.show(); //在调用时系统不知道调用哪个,所以这样不行
}
}
2.2.5接口的特点
接口是对外暴露的规则
接口是程序的功能扩展
接口可以用来多实现
类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口
接口与接口之间可以有继承关系
基本功能定义在类中,扩展功能定义在接口中
3.内部类
将一个类定义在另一个类的里面,对里面的那个类就称为内部类(内置类,嵌套类)
访问特点:
内部类可以直接访问外部类中的成员,包括私有成员,之所以可以直接访问外部类中的成员,是因为内部类中持有了一个外部类的引用,格式 : 外部类.this
而外部类要访问内部类中的成员必须要建立内部类的对象
内部类可以被private修饰,因为内部类属于外部类的成员
如果变量名称相同,想访问外部的变量的值: 类名.this.变量名
class Outer
{
int x=3;
class Inner
{
int x=4;//访问内部类的变量,使用this
void function(){
int x=6;
System.out.println("x:"+Outer.this.x);
}
}
void method(){
Inner in=new Inner();
in.function();
}
}
class InnerClassDemo
{
public static void main(String[] args){
Outer out=new Outer();
Outer.Inner in=new Outer().new Inner();
in.function();
//out.method();
}
}
3.1访问格式:
1.当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类(没有内部类的外部类)中,可以直接建立内部类对象
格式: 外部类名.内部类名 变量名=new 外部类名().new 内部类();
2.当内部类在成员位置上,就可以被成员修饰符所修饰
比如:private:将内部类在外部类中进行封装
static :内部类就具备了静态的特性,当内部类被静态修饰后,只能直接访问外部类中static成员,出现访问局限,当外部类中成员基本都为静态成员,可以使用静态内部类
class Outer
{
static int x=3;
static class Inner
{
void function(){
System.out.println("x:"+x);
}
}
void method(){
Inner in=new Inner();
in.function();
}
}
class InnerClassDemo
{
public static void main(String[] args){
new Outer.Inner().function(); //静态内部类声明的方式访问非静态成员方法
当访问静态内部类的静态成员:Outer.Inner.funtion();
}
注意:
当内部类中定义了静态成员,该内部类必须是静态的
当外部类中的静态方法访问内部类时,内部类必须是静态的
3.2内部类定义原则
当描述事物时,事物的内部还有事物,该事物用内部类来描述.因为内部事物在使用外部事物的内容
外部类的方法中,也可以定义内部类,为局部内部类,不能被静态修饰,不能被成员修饰符修饰(static ,private)
非静态,没对象,不运行
3.3局部内部类及调用方法:
class Outer
{
static int x=3;
void method(){
class Inner
{
void function(){
System.out.println(x);
}
}
new Inner().function();
}
}
class InnerClassDemo
{
public static void main(String[] args){
new Outer().method();
}
}
局部内部类访问成员方法中的局部变量,需要对变量进行修饰,即final(如果想用方法中参数的值,参数也许要被final修饰)
class Outer
{
static int x=3;
void method(){
final int y=7;
class Inner
{
void function(){
System.out.println(y);
}
}
new Inner().function();
}
}
class InnerClassDemo
{
public static void main(String[] args){
new Outer().method();
}
}
3.4匿名内部类
1. 匿名内部类其实就是内部类的简写格式
2. 定义匿名内部类的前提
内部类必须是继承一个类或者实现接口
普通内部类:
abstract class Demo
{
abstract void show();
}
class Outer
{
void method(){
class Inner extends Demo
{
void show(){
System.out.println("hello");
}
}
new Inner().show();
}
}
class InnerClassDemo
{
public static void main(String[] args){
new Outer().method();
}
}
匿名内部类:
abstract class Demo
{
abstract void show();
}
class Outer
{
void method(){
new Demo()//是Demo类的子类对象,因为只有子类才能覆写Demo中的抽象方法
{
void show(){
System.out.println("匿名内部类");
}
}.show();
}
}
class InnerClassDemo
{
public static void main(String[] args){
new Outer().method();
}
}
匿名内部类的格式:
new 父类或者接口(){
定义子类的内容
}
其实匿名内部类就是一个匿名子类对象,是把定义类和建立对象封装为一体的表现形式,而且这个对象有点胖,可以理解为有内容的对象
匿名对象自定义方法,也可以调用自己的方法,但是只能调用一次
匿名内部类可以起名字:
abstract class Demo
{
abstract void show();
}
class Outer
{
void method(){
<span style="color:#ff0000;"><strong>Demo d=new Demo()//首先,抽象类不能写方法,只要写了,就是子类对象;然后,父类的引用指向了子类的对象(这是多态)</strong></span>
{
void show(){
System.out.println("匿名内部类");
void abc(){
System.out.println("abc");
}
}
};
<span style="color:#ff0000;">d.show();//可以调用,因为父类中有
d.abc();//不可以调用,因为父类中没有子类的对象</span>
}
}
class InnerClassDemo
{
public static void main(String[] args){
new Outer().method();
}
}
匿名内部类的特点
1. 简化书写
2. 不能做强转动作
3. 当一个类中方法过多,代码量会很大,阅读量会很大(方法不超过3个)
练习:不全代码interface Inter
{
void method();
}
class Test
{
//不全代码:
}
class InnerClassTest
{
public static void main(String[] args){
Test.function().method();//一切思路从这里开始:
1. 在看到Test.function(),确定function是静态的
2. 在funtion()方法结束后,还能调用方法,说明返回的是一个对象
}
}
实现:
interface Inter
{
void method();
}
class Test
{
static Inter function(){
return new Inter(){
public void method(){
System.out.println("hello");
}
};
}
}
class InnerClassTest
{
public static void main(String[] args){
Test.function().method();
}
}
什么时候用:
当参数里有接口,可以使用匿名内部类