final的使用介绍
1.final的基本思想:
final有“这是无法改变的”或者“终态的”含义,它可以修饰非抽象类、非抽象类成员方法和变量。
1)final类不能被继承,没有子类,final类中的方法默认是final的。
2)final方法不能被子类的方法覆盖,但可以被继承。
3)final成员变量表示常量,只能被赋值一次,赋值后值不再改变。
4)final不能用于修饰构造方法。
注意:父类的private成员方法是不能被子类方法覆盖的,因此private类型的方法默认是final类型的。
2.final类
final类不能被继承,因此final类的成员方法没有机会被覆盖,默认都是final的。在设计类时候,如果这个类不需要有子类,类的实现细节不允许改变,并且确信这个类不会载被扩展,那么就设计为final类。
3.final方法
如果一个类不允许其子类覆盖某个方法,则可以把这个方法声明为final方法。使用final方法的原因有二:
第一、把方法锁定,防止任何继承类修改它的意义和实现。
第二、高效。编译器在遇到调用final方法时候会转入内嵌机制,大大提高执行效率。
4.final变量(常量)
用final修饰的成员变量表示常量,值一旦给定就无法改变,final修饰的变量有三种:静态变量、实例变量和局部变量,分别表示三种类型的常量。另外,final变量定义的时候,可以先声明,而不给初值,这中变量也称为final空白,无论什么情况,编译器都确保空白final在使用之前必须被初始化。但是,final空白在final关键字final的使用上提供了更大的灵活性,为此,一个类中的final数据成员就可以实现依对象而有所不同,却有保持其恒定不变的特征。
示例:
package com.ambow.test;
public class FinalDemo {
/*
* 1.final用来修饰常量的,一旦赋值就不能改变
* 2.final通常与static一起修饰常量
* */
public static final String NAME="张三";
public static final String NAME1="李四";
public static void main(String[] args) {
//FinalDemo ff=new FinalDemo();
//报错:final修饰的常量一旦赋值就不可改变
//ff.NAME="123";
//ff.NAME1="123";
System.out.println(FinalDemo.NAME);
}
}
6.7.9 类与类之间的关系
1.依赖:
对于两个相对独立的对象,当一个对象负责构造另一个对象的实例,或者依赖另一个对象的服务时,这两个对象之间主要体现为依赖关系。
1)依赖关系也是类与类之间的联结
2)依赖总是单向的。
3)依赖关系在 Java 或 C++ 语言中体现为局部变量、方法的参数或者对静态方法的调用。
具体表现:依赖关系表现在局部变量,方法的参数,以及对静态方法的调用
实列:比如说你要去拧螺丝,你是不是要借助(也就是依赖)螺丝刀(Screwdriver)来帮助你完成拧螺丝(screw)的工作
2.关联:
对于两个相对独立的对象,当一个对象的实例与另一个对象的一些特定实例存在固定的对应关系时,这两个对象之间为关联关系。
1)关联关系是类与类之间的联结,它使一个类知道另一个类的属性和方法。
2)关联可以是双向的,也可以是单向的。双向的关联可以有两个箭头或者没有箭头,单向的关联有一个箭头。
3)在 Java 中,关联关系是通过使用成员变量来实现的。
具体表现:关联关系是使用实例变量来实现
实例:比如客户和订单,每个订单对应特定的客户,每个客户对应一些特定的订单;再例如公司和员工,每个公司对应一些特定的员工,每个员工对应一特定的公司
3.泛化:
表示类与类之间的继承关系,接口与接口之间的继承关系,或类对接口的实现关系。一般化的关系是从子类指向父类的,与继承或实现的方法相反。
继承
7.1.1 继承的概念
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。多个类可以称为子类,单独这个类称为父类或者超类。子类可以直接访问父类中的非私有的属性和行为。通过 extends 关键字让类与类之间产生继承关系。
继承的基本思想:继承是面向对象最显著的一个特性。继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。这种技术使得复用以前的代码非常容易,能够大大缩短开发周期,降低开发费用。
7.1.2 继承的优点
1.继承的出现提高了代码的复用性。
2.继承的出现让类与类之间产生了关系,提供了多态的前提。
7.1.3 继承的定义
继承定义语法:
[类的修饰符] class <子类名> extends <父类名>{
<属性定义> ;
<方法定义>;
}
说明:在定义子类时用extends关键字指明新定义类的父类,就在两个类之间建立了继承关系。新定义的类称为子类,它可以从父类那里继承所有非private的属性和方法作为自己的属性。 子类对象可以视作是一个父类的对象。(子类就是父类)
示例(父类):
package com.ambow.test;
public class SuperDemo {
/**
* 继承关系中的属性或者方法受访问修饰符的限制
* */
//private修饰的私有属性不能被继承
private String wife="小芳";
String car="奔驰";
public String money="一千亿";
protected String house="别墅";
}
示例(子类):
package com.ambow.test;
/**
* 使用extends关键字来实现类与类之间的继承关系
* */
public class SubDemo extends SuperDemo{
public static void main(String[] args) {
SubDemo bb=new SubDemo();
System.out.println(bb.car);
//报错:私有的属性或者方法不能被继承
//System.out.println(bb.wife);
System.out.println(bb.money);
System.out.println(bb.house);
}
}
示例(不同包子类):
package com.ambow.test1;
import com.tjzs.a.SuperDemo;
public class SubDemo extends SuperDemo {
// String car="";
// String money="";
public static void main(String[] args) {
SubDemo bb=new SubDemo();
//受访问修饰符的限制,对默认修饰的属性或者方法,在不同包下不能被访问
//System.out.println(bb.car);
System.out.println(bb.money);
System.out.println(bb.house);
}
}
7.1.4 单继承和多继承
1.Java只支持单继承,不支持多继承,通过另一个方式体现(接口)。
2. Java支持多层继承(继承体系)
3. 定义继承需要注意:
1)不要仅为了获取其他类中某个功能而去继承
2)类与类之间要有所属( " is a " )关系,xx1是xx2的一种。
示例:
package com.ambow.test;
/**
* 定义继承的格式:
* [访问修饰符] class 子类类名 extends 父类类名(){
* 属性
* 方法
* }
* */
public class Sub extends SubDemo {
public static void main(String[] args) {
//java中支持多层继承,不支持多继承
Sub b=new Sub();
System.out.println(b.car);
}
}
注意:此案例关联继承定义的案例。
继承的基本特征说明:
1.继承关系是传递的。若类C继承类B,类B继承类A,则类C既有从类B那里继承下来的属性与方法,也有从类A那里继承下来的属性与方法,还可以有自己新定义的属性和方法。继承来的属性和方法尽管是隐式的,但仍是类C的属性和方法。继承是在一些比较一般的类的基础上构造、建立和扩充新类的最有效的手段。
2.继承简化了人们对事物的认识和描述,能清晰体现相关类间的层次结构关系。
3.继承提供了软件复用功能。若类B继承类A,那么建立类B时只需要再描述与基类(类A)不同的少量特征(数据成员和成员方法)即可。这种做法能减小代码和数据的冗余度,大大增加程序的重用性。
4.提供多重继承机制。从理论上说,一个类可以是多个一般类的特殊类,它可以从多个一般类中继承属性与方法,这便是多重继承。Java出于安全性和可靠性的考虑,仅支持单重继承,而通过使用接口机制来实现多重继承。
5.继承通过增强一致性来减少模块间的接口和界面,大大增加了程序的易维护性。
7.2 super
7.2.1 super的使用
- super代表父类的内存空间的标识。
2.当子父类出现同名成员时,可以用super进行区分
3.子类要调用父类构造函数时,可以使用super语句。
示例(父类):
package com.ambow.test;
public class SuperDemo {
/**
* 继承关系中的属性或者方法受访问修饰符的限制
* 说明:object(java自带的类):他是所有类的超类
* */
//private修饰的私有属性不能被继承
private String wife="小芳";
String car="奔驰";
public String money="一千亿";
protected String house="别墅";
public SuperDemo(){
System.out.println("这是父类的无参构造器");
}
public SuperDemo(int i){
System.out.println("这是父类的有参构造器");
}
}
示例(子类):
package com.ambow.test;
/**
* 使用extends关键字来实现类与类之间的继承关系
* */
public class SubDemo extends SuperDemo{
/*
* 子类中默认调用父类中的构造器(前提是父类的构造器必须是无参构造器)
* 如果父类中为有参构造器,那么在子类中必须手动调用
* 而且super使用是必须放在子类构造器中的第一行
* */
/*
* super的两个作用
* 1.用来区分子父类中同名的变量
* 2.表示父类的内存空间
* */
public SubDemo(){
//super(15);
//使用super必须放在子类构造器中的第一行
//只能使用(调用)一次
super();
System.out.println("这是子类的构造器");
}
//利用super来区分子父类中的同名变量只能利用方法
public String getCar(){
//super用来区分子父类中的同名变量
return super.car;
}
//子类同变量
String car="法拉利";
// String money="";
public static void main(String[] args) {
SubDemo bb=new SubDemo();
System.out.println(bb.car);
//利用super来访问父类中同名变量,不能够直接在main方法里面直接写
// super.car;
// System.out.println(bb.super.car);
//报错:私有的属性或者方法不能被继承
//System.out.println(bb.wife);
System.out.println(bb.money);
System.out.println(bb.house);
//调用方法区分父类和子类的同名变量
System.out.println(bb.getCar());
}
}
7.2.2 super和this的异同
1.super(参数):调用基类中的某一个构造函数(应该为构造函数中的第一条语句)
2.this(参数):调用本类中另一种形成的构造函数(应该为构造函数中的第一条语句)
3.super: 它引用当前对象的直接父类中的成员(用来访问直接父类中被隐藏的父类中成员数据或函数,基类与派生类中有相同成员定义时如:super.变量名super.成员函数据名(实参)
4.this:它代表当前对象名(在程序中易产生二义性之处,应使用this来指明当前对象;如果函数的形参与类中的成员数据同名,这时需用this来指明成员变量名)
5.调用super()必须写在子类构造方法的第一行,否则编译不通过。每个子类构造方法的第一条语句,都是隐含地调用super(),如果父类没有这种形式的构造函数,那么在编译的时候就会报错。
6.super()和this()类似,区别是,super()从子类中调用父类的构造方法,this()在同一类内调用其它方法。
7.super()和this()均需放在构造方法内第一行。
8.尽管可以用this调用一个构造器,但却不能调用两个。
9.this和super不能同时出现在一个构造函数里面,因为this必然会调用其它的构造函数,其它的构造函数必然也会有super语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。
10.this()和super()都指的是对象,所以,均不可以在static环境中使用。包括:static变量,static方法,static语句块。
11.从本质上讲,this是一个指向本对象的指针, 然而super是一个Java关键字。
7.3 继承初始化
一般来说,在Java中,每个类产生的编译代码都存在于它自己的独立文件中,该文件只有在使用程序代码时才会被加载,也就是说,类的代码只有在初次使用时才加载。但是,如果存在static的话,就不一样了,当访问static字段或static方法时,也会发生加载。最常见的static方法是构造器方法了,虽然没有显示写明static关键字。所以,更准确地讲,Java的类应该是在其任何static成员被访问时加载的。
继承关系中各方法的执行顺序:
- 执行父类的静态代码块
- 执行子类的静态代码块
- 执行父类的构造代码块
- 执行父类的构造函数
- 执行子类的构造代码块
- 执行子类的构造函数
示例(父类):
package com.ambow.test2
public class Demo {
public Demo() {
System.out.println("这是父类中的无参构造器");
}
static{
System.out.println("这是父类中的静态代码块");
}
public void a(){
System.out.println("这是父类中的实列方法");
}
// {
// System.out.println("这是父类中的方法块");
// }
}
示例(子类):
package com.ambow.test2;
public class Demo1 extends Demo {
public Demo1(){
super();
System.out.println("这是子类中的无参构造器");
}
static{
System.out.println("子类的静态代码块");
}
public void b(){
System.out.println("子类的实列方法");
}
// {
// System.out.println("子类的方法体");
// }
}
示例(测试类);
package com.ambow.test2;
public class Demo2 {
public static void main(String[] args) {
//父类中的静态代码块最先运行,子类中的静态代码块,父类中构造器,子类构造器
Demo1 d=new Demo1();
//实列方法的先后顺序根据你调用的先后顺序进行
d.b();
d.a();
}
}
7.4 多态
7.4.1 多态的概念
多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,就是同一种事物表现出的多种形态。编程其实就是一个将具体世界进行抽象化的过程,多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进行对话。
对不同类的对象发出相同的消息将会有不同的行为。比如,你的老板让所有员工在九点钟开始工作, 他只要在九点钟的时候说:“开始工作”即可,而不需要对销售人员说:“开始销售工作”,对技术人员说:“开始技术工作”, 因为“员工”是一个抽象的事物, 只要是员工就可以开始工作,他知道这一点就行了。至于每个员工,当然会各司其职,做各自的工作。
多态允许将子类的对象当作父类的对象使用,某父类型的引用指向其子类型的对象,调用的方法是该子类型的方法。这里引用和调用方法的代码编译前就已经决定了,而引用所指向的对象可以在运行期间动态绑定。
再举个比较形象的例子:比如有一个函数是叫某个人来吃饭,函数要求传递的参数是人的对象,可是来了一个美国人,你看到的可能是用刀和叉子在吃饭,而来了一个中国人你看到的可能是用筷子在吃饭,这就体现出了同样是一个方法,可以却产生了不同的形态,这就是多态
7.4.2 多态在java中的体现
1)接收参数为父类,实际执行为子类
2)方法的重写
示例(父类):
package com.ambow.test;
public class H2O {
//如果一个父类有多个子类的话,那么他们就会存在一个集中形态(共同的特征)
/*多态存在的条件:
* 1.具有继承关系
* 2.上溯造型:父类引用指向子类对象
* 3.重写父类中的方法
* */
public void say(){
}
}
示例(子类1):
package com.ambow.test;
public class Water extends H2O {
@Override
public void say() {
// TODO Auto-generated method stub
System.out.println("我是水,有水分子构成");
super.say();
}
}
示例(子类2);
package com.ambow.test;
public class ShuiZhengQi extends H2O {
@Override
public void say() {
// TODO Auto-generated method stub
System.out.println("我是水蒸汽,有水分子构成");
super.say();
}
}
示例(子类3):
package com.ambow.test;
public class Ice extends H2O {
@Override
public void say() {
// TODO Auto-generated method stub
System.out.println("我是冰,我是由水分子构成");
super.say();
}
}
示例(测试类):
package com.ambow.test;
public class Test {
public static void main(String[] args) {
//创建对象
H2O h2o=new H2O();
h2o.say();
//System.out.println("=========================");
//多态:父类的引用指向子类的对象
//只有在运行后才能确定父类具体指向的是哪一个子类的对象
H2O h2oo=new Water();
h2oo.say();
System.out.println("==========================");
H2O h2ooo=new Ice();
h2ooo.say();
System.out.println("==========================");
H2O h2oooo;
h2oooo=new ShuiZhengQi();
h2oooo.say();
/*
* 多态与之前方法进行比较的好处:(也是为什么使用多态的原因)
* 1.方面修改
* 2.可以节省代码
* */
}
}
7.5 上溯造型(向上转型)
7.5.1 上溯造型的概念
上溯造型指将衍生类的句柄赋给基础类的句柄(即是将子孙类的句柄赋给祖先类的句柄),说白了就是子转父,可以直接转,会丢掉子类自有的特征,不能调用子类的私有函数或者可以理解为指子类的对象指向父类的引用。因为它是从一个更特殊的类型到一个更常规的类型,所以上溯造型肯定是安全的。
7.5.2 下溯造型的概念(了解)
在进行下溯造型时,我们不能确定下溯的是基础类的哪一个衍生类,所以是不安全的。说白了就是父转子,因为我们不确定转换的类,因此在做下溯造型时有条件强制转化,使用instanceof 判断为true 才能转化。
示例:
package com.ambow.test;
public class Test {
public static void main(String[] args) {
//上溯造型:父类的引用指向子类的对象
H2O h2oo=new Water();
h2oo.say();
//下塑造型:子类引用指向父类对象
//Water water=(Water) new H2O();
//water.say();
}
}
说明:该案例接上面案例。
重写
1.概念
重写只能出现在继承关系之中。当一个类继承它的父类方法时,都有机会重写该父类的方法。一个特例是父类的方法被标识为final则不能重写。重写的主要优点是能够定义某个子类型特有的行为,说白了就是指子类重新定义从父类继承来的一个同名方法,此时子类将清除父类方法的影响。子类可以通过重新定义与父类同名的方法,实现自身的行为。
2.使用重写的注意事项
1)在继承关系中
2)方法名相同
3)参数列表相同
4)返回值类型相同
5)访问修饰符大于等于父类的
示例(父类1):
package com.ambow.test;
/**
* 表示父类
*
* */
public class Father {
public void myMedth1() {
}
}
示例(子类1):
package com.ambow.test;
/**
* 表示子类
*
* */
public class Child extends Father {
@Override
public void myMedth1() {
// TODO Auto-generated method stub
super.myMedth1();
}
}
示例(父类2):
package com.ambow.test;
public class SuperDemo {
/*
* 重写要注意的:
* 1.必须有继承关系
* 2.方法名相同
* 3.参数列表相同
* 4.返回值类型相同
* 5.子类重写的访问修饰符必须大于等于父类的
*
* */
public void myMedth() {
System.out.println("这是父类中的实列方法");
}
public int myMedth(int i){
return i;
}
//father父类
public Father myMedth1(){
Father ff=new Father();
return ff;
}
}
示例(子类2+测试):
package com.ambow.test;
public class SubDemo extends SuperDemo {
//Override表示子类在重写父类的同名方法
@Override
public void myMedth() {
// TODO Auto-generated method stub
System.out.println("这是子类的实列方法");
super.myMedth();
}
@Override
//报错:private int myMedth(int i):子类重写的访问修饰符必须大于等于父类的
public int myMedth(int i) {
// TODO Auto-generated method stub
i+=100;
return super.myMedth(i);
}
/*如果你的返回值类型为类类型时,
* 当你在子类中重写这个方法的时候,可以是父类里面那个类的子类
* child是father的子类
* */
@Override
public Child myMedth1() {
// TODO Auto-generated method stub
Child c=new Child();
return c;
}
//测试
public static void main(String[] args) {
SubDemo sd=new SubDemo();
sd.myMedth();
int i=sd.myMedth(23);
System.out.println(i);
}
}
3.重写的意义
重写方法可以实现多态,用父类的引用来操纵子类对象,但是在实际运行中对象将运行其自己特有的方法。
4.重写的原则
使用了什么引用,编译器就会只调用引用类所拥有的方法。如果调用子类特有的方法,编译器会抱怨的。也就是说,编译器只看引用类型,而不是对象类型。