java基础
待补
文章目录
前言
前面介绍了面向对象中类和对象的概念以及如何将数据放到类和对象中(封装),本章将继续介绍三大特性中继承和多态的概念。
以及如何使用抽象类和接口来约束类。
一、类的继承
1.特点
- 使用extends指定继承的类,只允许单继承(只能有一个父类),
- 在创建子类对象时,会先执行父类构造器,再执行子类构造器
继承链:执行子类构造器前会先执行父类构造器,执行父类构造器前又会执行它的父类的构造器,由此形成一个调用构造器的顺序链。
2.能继承什么
继承能让子类拥有父类的属性和方法,但并不是所有的属性和方法都可以被子类继承。
不能继承的:
- 父类构造器
- 父类私有成员方法
能继承的:
成员变量
非私有的成员方法
3.继承体系下成员的访问特点
在不使用this和super关键字的情况下,在子类的成员方法中访问一个变量会按照如下顺序查找:
子类局部范围-》子类成员变量范围-》父类成员变量范围
4.this和super
this:用于指代子类对象,当创建一个类的对象时,会把该对象的地址(引用)存储一份到this变量中,于是可以在子类中使用this关键字访问自己的成员。
- this():调用自己的无参构造器
- this(参数):调用自己的有参构造器
- this.成员变量:调用成员变量
- this.成员方法:调用成员方法
super:super关键子存储当前类的直接父类的地址,于是可以用来调用父类成员。常见的用法是,如果子类中重写的方法只是对父类中的逻辑进行补强,就先使用super调用父类逻辑,再书写补充的代码。
5.内存情况
存在继承的情况下,该类会在堆内存中开辟一块super区域来存储父类数据。
其中从父类继承的方法存放的地方叫"虚方法表"。
二、Object类
是所有类共同的最大的父类,
下面是几个定义在Object类中的常用方法:
- getClass():返回对象执行时的Class实例
- toString():将一个对象返回为字符串形式
- equals():比较两个对象,(与==的差别是:==比较两个对象的地址,equals()比较两个对象的内容)
补充:Object中的equals()方法也是比较对象的地址,一般该方法都会由子类重写后才会是用于比较内容
三、对象类型的转换
前面说过基本类型可以进行转换,引用类型其实也可以进行转换。但是,只有有继承关系的类才可以进行转换。
1.向上转型
子类对象转换为父类对象,
如teacher是people的子类,则:
People tom=new Teacher()
可以执行
向上转型是安全的、自动进行的,但转型后的对象会失去子类特有的属性和方法
2.向下转型
父类对象转换为子类对象
如teacher是people的子类,则:
Teacher tom=(Teacher) people
可以强制将people这个父类对象转换为子类对象。
向下转型不安全,要使用强制转换实现,并且需要该父类对象先引用子类对象
//新建一个子类对象,用一个父类对象(变量)指向该对象
People people=new Teacher();
//把这个people对象从People转换为子类对象
Teacher T=(Teacher)people
如果父类对象不先引用子类对象,即:
People people=new People()
//则:
Teacher T=(Teacher)people
//转型不会成功
因为本身底层使用new创建的就是一个Teacher对象,所以可以转型过去。
如果底层是创建一个父类对象,或者其他子类的对象,则无法转换。
3.instanceof关键字
正因为进行对象的强制转换有条件,所以我们在进行转换前先要确定当前对象在底层是否真的是某个子类的实例。
如果该对象是由我们自己使用new创建出来则可以很轻易的判断。
但很多时候,我们获取到对象是无法确定快速确定其创建语法的,所以java中可以使用instanceof关键字来进行判断。
格式:
a instanceof bclass
a:某个类的引用对象
bclass:某个类
作用:判断变量a的类型是否是bclass的实例或者bclass子类的实例,返回boolean值
四、方法重写
1.概念
当子类需要父类对应的功能,但对功能的具体实现子类有自己特有的内容时,可以重写父类方法,以此在沿袭了父类功能的情况下还实现了子类的特有功能。
2.应用场景
当子类需要父类对应的功能,但对功能的具体实现子类有自己特有的内容时,可以重写父类方法,以此在沿袭了父类功能的情况下还实现了子类的特有功能。
3.@override注解
在重写时使用的注解,非必要,作用是在编写时检查重写是否符合规范(方法名是否相同、参数是否相同)。
该注解可以在编译阶段就检查重写格式是否正确。
4.注意事项
以下是一些重写时的注意事项:
- 父类私有方法无法重写(因为也没继承到)
- 重写的方法其访问权限要大于等于原方法(子类要更开放)
- 重写的方法其返回值类型要小于等于原方法
- 父类中的静态方法无法被重写,子类中若有同样的方法,则该方法是子类自己的方法(非重写)
五、final关键字
1.修饰变量
被修饰变量只能被赋值一次,相当于变成常量。
2.修饰方法
被修饰的方法无法被子类重写
3.修饰类
被修饰的类无法被继承
六、代码块
代码块即一块被{}包裹的区域
1.局部代码块
位置:方法中定义,
作用:限定对象生命周期,变量作用域
如:
public class Test {
/*
局部代码块
位置:方法中定义
作用:限定变量的生命周期,及早释放,提高内存利用率
*/
public static void main(String[] args) {
{
int a = 10;
System.out.println(a);
}
// System.out.println(a);
}
}
2.构造代码块
位置:类中方法外定义
特点:每次构造方法执行前会执行该代码块内内容
作用:可以抽取多个构造器内的相同内容到构造代码块中,提高代码复用性
public class Test {
/*
构造代码块:
位置:类中方法外定义
特点:每次构造方法执行的时,都会执行该代码块中的代码,并且在构造方法执行前执行
作用:将多个构造方法中相同的代码,抽取到构造代码块中,提高代码的复用性
*/
public static void main(String[] args) {
Student stu1 = new Student();
Student stu2 = new Student(10);
}
}
class Student {
{
System.out.println("好好学习");
}
public Student(){
System.out.println("空参数构造方法");
}
public Student(int a){
System.out.println("带参数构造方法...........");
}
}
3.静态代码块
位置:类中方法外定义(相比构造代码块需要额外用static修饰)
特点:每次随着类的加载而加载,只执行一次
作用:在类加载时做一些初始化操操作。
public class Test {
/*
静态代码块:
位置:类中方法外定义
特点:需要通过static关键字修饰,随着类的加载而加载,并且只执行一次
作用:在类加载的时候做一些数据初始化的操作
*/
public static void main(String[] args) {
Person p1 = new Person();
Person p2 = new Person(10);
}
}
class Person {
static {
System.out.println("我是静态代码块, 我执行了");
}
public Person(){
System.out.println("我是Person类的空参数构造方法");
}
public Person(int a){
System.out.println("我是Person类的带...........参数构造方法");
}
}
所以,当一个对象被创建时,执行顺序如下:
静态代码块->初始化代码块->构造器
七、多态
一个类可以有多个子类,对于父类中的同一方法,不同子类有不同的实现方式。
多态前提:
- 类之间有继承或实现关系
- 子类重写父类方法
- 父类引用指向子类对象
多态特点:
多态弊端
八、抽象类与接口
1.抽象类
1.1概述
有时我们的某些方法(功能)在父类中并不会定义其具体的实现功能,而是只想表示有这个功能,具体实现交给子类完成,这样的功能(方法)没有方法体,叫抽象方法,拥有抽象方法的类叫抽象类。
用abstract关键字来修饰抽象类和抽象方法。
1.2特点
- 如果有类继承了抽象类,则必须把抽象类中的抽象方法重写(除非子类也是抽象类)
- 有抽象方法一定是抽象类,抽象类可以没有抽象方法
- 抽象类不能实例化
- 抽象类可以有构造方法,即使无法创建抽象类实例,但可以提供给子类调用
1.3例子
抽象类的作用是为其子类定义一种“规范”,
比如:
有动物类为抽象类,其中有两个方法,其中eat为抽象方法,fly为普通方法.(因为作为动物一定会吃东西,但不同动物的饮食不同所以只是表示动物有“吃饭”的行为)
public abstract class animal{
abstract void eat();
public fly(){
print("飞翔");
}
}
有一个dog类继承了animal类:于是作为Animal类的子类,狗自然也会吃饭,必须重写eat()方法,而飞行并不是强制要求子类会的行为就不必重写。
public class dog(){
//必须重写来自animal类中的eat抽象方法,普通方法fly则非必须
void eat(){
print("狗吃骨头")
}
}
2.接口
2.1概述
接口是为了解决抽象类的一个弊端出现的。
抽象类的弊端:抽象类中的某些抽象方法,对于某子类而言是不需要的,但该子类却必须继承并重写。
(这里可能有个歧义:并不是说使用接口,其中的抽象方法就不需要全部重写,接口中的抽象方法作为其实现类也需要全部重写。也就是说接口并不是通过让你不必重写抽象方法来解决这个弊端的)。
但区别在于:抽象类是一个类,其子类通过继承来使用抽象类,而java中只能单继承,所以子类无法选择排除抽象类中不需要的抽象方法。
而一个类可以实现多个接口,只要把抽象方法再次细分到不同的接口中,不同的实现类根据需要选取对应的接口实现即可。这样就可以让这些实现类只继承到自己想要的功能。
总结:抽象类和接口都使用抽象方法来为其子类(或实现类)规定一个“标准”。
抽象方法都要被实现,但接口有多实现的特性,所以可以把抽象方法再次进行划分,让其实现类自行决定使用哪些抽象方法,而抽象类因为单继承的特性让其子类只能无奈一次继承所有抽象方法。
2.2接口使用
2.2.1定义接口
使用interface关键字定义接口,如:
interface Dao {
void addStudent();
void addStudent2();
}
2.2.2实现接口
在类的后面使用implements指定实现接口
2.3接口中的变量
存在于接口中的变量默认且一定被public static final修饰,我们定义时可以省略这些修饰符,因为其默认自动生效。
2.4接口中的方法
方法:接口中的方法分为抽象方法、默认方法、静态方法、私有方法
- 抽象方法:没有方法体,必须被实现类继承并重写(除非实现类是抽象类)
- 默认方法:使用default关键字定义,有方法体,会被实现类继承(实现类的非静态方法可以调用)可以被重写(不强制要求重写)
- 静态方法:用static修饰,有方法体,只能用接口名.静态方法名调用(或者被接口中其他方法调用),不能被实现类继承。
- 私有方法:用private修饰,只能被接口中其他方法调用
补充说明:默认方法、静态方法、私有方法都是jdk8之后加入的新特性,而且实际上静态方法和私有方法并不是一种新的方法种类。
明确说是jdk8之后允许使用private和static来修饰方法,也就是说我们甚至可以同时使用这两个关键字来修饰接口中的同一个方法
方法调用问题:
接口中有方法体的三种方法(默认方法、静态方法、私有方法),就像正常的类中一样,静态方法中能只能调用静态方法。默认方法和私有方法可以调用该接口中的所有方法。
2.5接口的特点
- 接口可以继承其他接口,并且接口间的继承关系可以是多继承。
- 继承其他接口不强制要求实现父类接口的方法
总结
本篇简单讲了下继承、多态、抽象类、接口的使用和特点