final关键字的引入
final 最终的意思。常见的他可以修饰类、方法、变量
package miangxiangduixaing04;
public class fu {
public final void show(){
System.out.println("这里是绝密的,任何人都不能改");
}
}
package miangxiangduixaing04;
public class zi extends fu {
/*public void show(){
System.out.println("这是一堆垃圾");
//报错,子中的show无法覆盖父中的show
}*/
public void show1(){
System.out.println("这是一个不同于show方法的show1方法");
}
}
package miangxiangduixaing04;
public class zidemo {
public static void main(String[] args) {
zi z = new zi();
z.show();//因为子类中的方法重写,导致了父类中的方法内容被改变
/*由于继承中方法有一个现象 方法重写,导致了父类中的方法被子类覆盖
* 有些时候,我们不想让子类覆盖掉父类中的功能,只能让他使用
* 针对这种情况,java就提供了一个关键字 final*/
z.show1();
}
}
结果为:
这里是绝密的,任何人都不能改
这是一个不同于show方法的show1方法
final修饰类、方法、变量的特点
final可以修饰类、方法、变量
特点
- final可以修饰类,该类不能被继承
- final可以修饰方法,该方法不能被重写(覆盖,复写)
- final可以修饰变量,该变量不能被重新赋值(因为这个变量其实是常量)
常量
- 字面值常量
‘hello’‘10’‘true’ - 自定义常量
final int x = 10;
package miangxiangduixaing04;
/*public final class fu1 {
public void show(){
System.out.println("123");
}
}
1
*/
/*public class fu1{
public final void show(){
System.out.println("这是final修饰的show方法");
}
}
2*/
public class fu1{
int num = 10;
public final int num2 = 20;
}
package miangxiangduixaing04;
/*public class zi1 extends fu1 {
}
error 父类为final类,不能被继承
1
*/
/*public class zi1 extends fu1 {
public void show(){
System.out.println("这是一个public的show方法");
}
}
error 报错,因为父方法为final修饰,不能被重写
2
*/
public class zi1 extends fu1 {
public void show(){
int num = 100;
System.out.println(num);
int num2 = 200;
System.out.println(num2);
}
}
package miangxiangduixaing04;
public class zidemo1 {
public static void main(String[] args) {
zi1 z = new zi1();
z.show();
}
}
结果为:
100
200
按理说被final修饰的变量不可以在赋值了,为什么这还能打出200????不懂
final关键字修饰局部变量
package miangxiangduixaing04;
public class student {
int age = 18;
}
public class studenttest {
public static void main(String[] args) {
int x = 10;
System.out.println(x);
final int y = 20;
System.out.println(y);
System.out.println("--------------------");
student s = new student();
System.out.println(s.age);
s.age=100;
System.out.println(s.age);
System.out.println("--------------------");
final student ss = new student();
System.out.println(ss.age);
ss.age=200;
System.out.println(ss.age);
}
}
结果为:
10
20
--------------------
18
100
--------------------
18
200
final关键字面试题
- final修饰局部变量
在方法内部,该变量不可以被改变
在方法声明上,分别演示基本类型和引用类型作为参数的情况
基本类型:是值不能被改变
引用类型:是地址值不能被改变
final修饰变量的初始化时机
- 在对象构造完毕前即可
A 被final修饰的变量只能赋值一次
B 在构造方法完毕前(非静态的常量)
多态的概述和前提条件
多态概述
- 某一个事物,在不同时刻表现出来的不同状态
举例:
猫可以是猫的类型。mao m = new mao()
同时,猫也可以是动物类型。dongwu dw = new dongwu()
多态前提和体现
- 有继承关系
(继承让类与类产生了关系,是多态的前提) - 有方法重写
(其实没有也是可以的,但是如果没有就没有意义) - 有父类引用指向子类对象
(fu f = new zi(),new后面跟的是子类)
多态中成员访问特点
A 成员变量
编译看左边,运行看左边
(fu f(左) = new zi(右)())
B 构造方法
创建子类对象的时候,访问父类的构造方法,对父类的数据进行初始化
C 成员方法
编译看左边,运行看右边
D 静态方法
编译看左边,运行看左边
注:变量没有重写,但是方法可以被覆盖,父类方法被子类覆盖掉了
package mianxiangduixiang04;
public class fu {
public int num = 10;
public void show() {
System.out.println("show fu");
}
public static void function() {
System.out.println("function fu");
}
}
package mianxiangduixiang04;
public class zi extends fu {
public int num2 = 20;
public int num = 100;
public void show() {
System.out.println("show zi");
}
public void method() {
System.out.println("show method");
}
public static void function() {
System.out.println("function zi");
}
}
package mianxiangduixiang04;
public class futest {
public static void main(String[] args) {
fu f = new zi();
f.show();
System.out.println("-----------------");
System.out.println(f.num);
/*System.out.println(f.num2);error
* 因为父类中没有num2,编译看左边,运行看左边*/
System.out.println("------------------");
/*f.method();error 因为父类中没有method方法*/
f.function();
}
}
结果为:
show zi
-----------------
10
------------------
function fu
多态的好处
多态的好处
- 提高了程序的维护性(由继承保证)
- 提高了代码的扩展性(由多态保证)
多态的弊端
- 不能访问子类特有功能
- 那么我们如何才能访问子类的特有功能呢?
多态的转型
多态中向上转型和向下转型
多态的弊端:
不能使用子类的特有功能
那我就想使用子类的特有功能行不行
行
那怎么用呢?
A 创造子类对象调用方法即可
(可以,但是很多时候不合理,并且,太占内存了)
B 把父类的引用强制转换为子类的引用(向下转型)
对象间的转型问题
向上转型:
fu f = new zi()
向下转型:
zi z = (zi)f //要求该f是必须能转换成子的
package miangxiangduixaing04;
public class fu3 {
public void show(){
System.out.println("show fu");
}
}
package miangxiangduixaing04;
public class zi3 extends fu3 {
public void show(){
System.out.println("show zi");
}
public void method(){
System.out.println("method");
}
}
package miangxiangduixaing04;
public class fu3test {
public static void main(String[] args) {
fu3 f = new zi3();
f.show();
//f.method();error 编译看左边,运行看右边
//创建子类对象
zi3 z = new zi3();
z.show();
z.method();
//你能把子的对象赋值给父亲,那么我能不能把父的引用赋值给子的引用呢?
//如果可以,但是如下
zi3 z1 = (zi)f;//引用不能new,不然就是创造一个对象出来了,
f本身就是zi,所以才能引用
zi.show();
zi.method();
}
}
结果为:
show zi
show zi
method
(后面俩报错)
多态中的转型问题
- 向上转型
从子到父
父类引用指向子类对象 - 向下转型
从父到子
父类对象转为子类对象
抽象类的引入
抽象类概述
回想前面的猫狗案例,提取出了一个动物类,并且我们在前面也创建过了动物对象,其实这是不对的,为什么呢?因为我说动物,你能知道我说的是什么动物么?只有看到了具体的动物,你才知道这是什么动物,所以说,动物本身并不是一个具体的事物,而是一个抽象的事物,只有具体的猫狗才是具体的动物,同理,不同的动物吃的也不一样,我们不应该在动物类中给出一个具体体现,而是给出一个声明即可,在java中,一个没有方法体的方法应该定义为抽象方法,该类必须定义为抽象类
注:
什么是没有方法体的方法
没有方法体是直类似接口的写法,没有代码的方法也是有方法体的。可以举个例子: 没有方法体:public abstract void noMethodBody(); 空方法体:public void empertyBody(){} 是有本质的区别的。没有方法体的方法,是需要你去实现的。空方法体,是需要覆盖的
-
抽象类
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。抽象类往往用来表征对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象。
抽象类的特点
抽象类的特点
- 抽象类和抽象方法必须用abstract关键字修饰
格式
abstract class 类名{}
public abstract class eat (); - 抽象类不一定有抽象方法,有抽象方法的类一定是抽象类
- 抽象类不能实例化(因为它不是具体的)
抽象类有构造方法,但是不能实例化,那构造方法的作用是什么呢?
用于子类访问父类数据的初始化
那么,抽象类如何实例化呢?
按照多态的方式,有具体的子类实例化,其实这也是多态的一种,抽象类多态 - 抽象类的子类
1、如果不想重写抽象方法,该子类是一个抽象类
(必须要满足子类是抽象类)
2、重写所有的抽象方法,这个时候子类是一个具体的类
(必须要满足重写了所有抽象方法,并且此时的子类是一个具体的类,具体的类就是指不是抽象类,没有abstract关键字)
package miangxiangduixaing04;
public abstract class animal {
//抽象方法
//抽象方法不能有主体,就是加了abstract的方法不能有大括号
public abstract void eat();//没有方法体
/*public void eat() { }//空方法体*/
public animal(){
}
}
package miangxiangduixaing04;
/*public class dog extends animal {
}
error 因为父类是抽象类,所以子类必须是抽象类,否则就会报错*/
public class dog extends animal{//不报错,因为这子类是抽象类
public void eat(){
//不报错,因为这重写了父类的抽象方法
System.out.println("ment");
}
}
package miangxiangduixaing04;
public class animaltest {
public static void main(String[] args) {
/*animal a = new animal();error 因为抽象类是不能实例化的*/
animal a = new dog();
a.eat();
}
}
抽象类的成员特点
抽象类的成员特点
-成员变量:既可以是变量,也可以是常量
- 构造方法:有,用于子类访问父类数据的初始化
- 成员方法:既可以是抽象的,也可以是非抽象的
抽象类的成员方法特性 - A 抽象方法 强制要求子类做的事情
- B 非抽象方法 子类继承的事情,提高代码复用性
package mianxiangduixiang04;
abstract class animal {
public int num = 10;
public final int num2 = 100;
public animal() {}//无参构造
public animal(String name,int age) {}//带参构造
public abstract void show();
public void method() {
System.out.println("method");
}
}
package mianxiangduixiang04;
public class dog extends animal {
public void show() {
System.out.println("show dog");
}
}
package mianxiangduixiang04;
public class animaltest {
public static void main(String[] args) {
animal a = new dog();
a.num = 1;
System.out.println(a.num);
/*a.num2 = 20;error ,因为num2是常量,只能赋值一次*/
System.out.println(a.num2);
System.out.println("----------------");
a.show();
a.method();
}
}
结果为:
1
100
----------------
show dog
method
接口概述
回到我们讲过的猫狗案例,我们就想想,狗一般就是看门的,猫一般就是作为宠物,但是呢,现在有很多狗会钻火圈,猫会跳绳,而这些额外的动作并不是一开始就具备的,这应该属于经过特殊培训后的特殊动作,显然,这些定义到动物类中不合适,也不合适直接定义到猫类或者狗类中,因为只有部分的猫狗具备这些动作,所以,为了体现事物功能的扩展性,java就提供了接口来定义这些额外功能,并不给出具体实现,将来哪些猫狗需要被培训,只需要这部分猫狗把这部分额外的功能实现即可
接口特点
- 接口用关键字interface表示
格式 : interface 接口名 {} - 类实现接口用inplements表示
格式 :class 类名 inplements 接口名{} - 接口不能实例化
那么,接口如何实例化呢???
一种,接口多态 - 接口的子类
要么是抽象类(可以是抽象类,但是意义不大,因为它仍然不能实例化)
要么重写接口中的所有抽象方法(可以是具体类,要重写接口中的所有抽象方法(推荐方案))
由此可见:
A 具体类多态(几乎没有)
B 抽象类多态(常见)
C 接口多态(最常见)
package mianxiangduixiang04;
interface animaltrain{//接口 是抽象的,无法实例化
public void jump();
}
/*abstract class dogtrain implements animaltrain{//抽象类实现接口
}*/
public class dogtrain implements animaltrain{
public void jump() {
System.out.println("狗可以跳高了");
}
}
package mianxiangduixiang04;
public class animaltraintest {
public static void main(String[] args) {
/*animaltrain a = new animaltrain();
a.jump();*/
animaltrain a = new dogtrain();
a.jump();
}
}
结果为:
狗可以跳高了
*自我理解:为什么要有封装?为什么要有继承?为什么要有多态?为什么要有接口?
- 封装是把一个方法或者多个方法封装起来,别人要用的时候就得调用它,设置为私有类型,别人能访问的只有没有被设为私有的东西,提高了代码的安全性和复用性
- 继承是如果有多段代码,且每段代码中都有相同的方法,为了减少繁琐,所以用一个父类把这段代码封装起来,子类要用的时候直接调用即可,并且子类还可以有自己的方法,提高了代码的维护性和复用性
- 一个事物可能有多种形态,一段代码不可能把所有形态都写出来,所以就引出了抽象类,定义一个不具体的类,里面有一个不具体的方法,让这个类作为父类,然后在子类代码中重新定义这个不具体的方法,让他具体一点,具体知道这个方法是干什么的,所以多态必须满足继承,且子类是一个具体类,具体类中写具体方法
- 即使一个具体的类一个具体的方法也不一定能完全准确的表达出一个事物的具体属性,有时即使是同一类的东西也有不同的地方,这时就出现了接口,在接口中定义一个抽象类,在抽象类中写有抽象方法,把此类当作是父类,在写一个子类,具体的方法(事物具体的属性)在子类中给出,这样一来就提高了代码的扩展性*
接口的成员特点
成员变量
- 只能是常量
(接口中的变量默认是常量) - 默认修饰符 public static final
构造方法
- 没有 因为接口主要是扩展功能的,而没有具体存在
成员方法
- 只能是抽象方法
- 默认修饰符 public abstract
(所有的类都默认继承自一个类:Object
类 Object 是类层次结构的根类,每个类都是用 Object作为超类 )
package mianxiangduixiang04;
interface inter{
public int num = 10;
public int num2 = 20;
/*public inter () {}error 没有构造方法*/
public void show() ;//接口方法不能带主体,说明接口方法是抽象的
}
//接口名+Impl这种格式是实现接口类格式
public class interImpl implements inter{
public interImpl() {
super();
}
//所有类都继承自Object类,所以super访问的是Object类
public void show() {}
}
package mianxiangduixiang04;
public class interinpltest {
public static void main(String[] args) {
inter i = new interImpl();
System.out.println(i.num);
System.out.println(i.num2);
/*i.num=100;
i.num2=200;
System.out.println(i.num);
System.out.println(i.num2);error 无法为最终变量分配值*/
}
}
类与类,类与接口,接口与接口的关系
- 类与类
继承关系,只能是单继承,但是可以多层继承 - 类与接口
实现关系,可以单实现,也可以多实现。还可以在继承一类的同时实现多个接口 - 接口与接口
继承关系,可以单继承,也可以多继承
(多继承在接口上)
package mianxiangduixiang04;
interface father{
public abstract void show();
}
interface mother {
public abstract void show2();
}
interface sister extends father,mother{
public abstract void show3();
}
public class son extends Object implements father,mother{
//继承一个类的同时实现多个接口
public void show() {
System.out.println("show son");
}
public void show2() {
System.out.println("show2 son");
}
}
package mianxiangduixiang04;
public class fathertest {
public static void main(String[] args) {
father f = new son();
f.show();
/*f.show2();error*/
mother m = new son();
/*m.show();error*/
m.show2();
//对应的接口只能调对应的方法
}
}
结果为:
show son
show2 son
抽象类和接口的区别
成员区别
抽象类
成员变量:可以常量,也可以变量;
构造方法:有
成员方法:可以抽象,也可以非抽象接口
成员变量:只可以常量;
成员方法:只可以抽象方法
关系区别
- 类与类
继承 单继承 - 类与接口
实现 单实现 多实现 - 接口与接口
继承 单继承 多继承
设计理念区别
- 抽象类
被继承,的体现是 is a 的关系,共性功能 - 接口
被实现,体现的是 like a 的关系,扩展功能