面向对象高级
继承,抽象类,接口,多态,内部类
继承
(java只有单继承和多重继承(爷爸儿),没有多继承)
格式:
class 父类{
}
class 子类 extends 父类{
}
-
可以避免代码太多重复(比如人-姓名年龄,学生-姓名年龄)
-
Stdent2 继承Person2 .
第一步:Student s 创建对象,进入栈空间
第二步:new Student(); ,读取Student 继承Person类
第三步:先创建Person类的空间,地址:0x123
第四步:创建Student类的空间。
第五步:Student类中自动有个super,指地址0x123
第六步:写s.setName("张三"),方法,程序先进入Student类里面看,如果没有这个方法就通过super去 父类Person里面看,如果有就调用,没有就报错。
第七步:super调用父类方法。
super:
1、super可以访问父类构造方法
2、super可以访问父类的属性
3、super可以访问父类的方法
class Student extends Person{
public Student(){
super("无名称",1); //调用父类中的双参构造方法
super.sex = "男";// 调用属性,(属性不能是private)
super.setName(); // 调用方法
}
}
无法同时调用this,和super。因为都需要放在第一行。
重写(override)
1、参数列表=必须完全=与被重写方法相同
2、返回值类型=必须完全=与被重写方法的返回类型相同
3、访问权限=不能=比父类中被重新的方法的访问权限更低
4、父类的成员方法=只能=被其它的子类重写
5、声明为=static=和=private=的方法不能被重写,但是=可以=再次声明
java重载(overload)和重写(override)的区别
1、发生的位置:
重载:一个类中
重写:子父类中
2、参数列表现在
重载:必须不同的
重写:必须相同的
3、返回值类型
重载:与返回值类型无关
重写:返回值类型必须一直
4、访问权限:
重载:与访问权限无关
重写:子类的方法权限必须不能小于父类的方法权限
5、异常处理:
重载:与异常无关
重写:异常范围可以更小,但是不能抛出新的异常
final关键字
final用于修饰属性、变量
final修饰的局部变量,只能赋值一次(可以先声明后赋值)
final修饰的成员属性,必须在声明的时候赋值/
final用于修饰类
final修饰的类,不可以被继承
final用于修饰方法
final修饰的方法,不可以被重写
全局变量:(public static final)
final修饰变量会变成常量
final int a = 10;
a = 20; //报错,无法再次进行赋值
-----------------
final int a;
a = 10;
a = 20;// 报错,不能赋值为20
抽象类 abstract
类所描述的行为,方法是模糊的
比如:
人的类,人可以有子类,学生警察护士老师
人可以说话,但是每个子类说话方式不一样。所以父类说话方法没办法==统一被子类共同运用==
所以我们写不了父类中的say();方法,那我们便把父类写成抽象类(因为有不确定的东西)
抽象类原则
1、不能被实例化操作,及不能使用new
2、一个抽象类必须被子类所继承,被继承的子类(如果不是抽象类),则必须重写父类的抽象方法(将它变成确定方法),如果子类是抽象类,那么继续得找继承
public abstract class Person{
public void sayHolle(){
System.out.println("你好"); //如果有{},则不是抽象方法
}
public abstract void say();//如果没有{}修饰,则必须要添加abstract
}
实现类(抽象类,必须要有子类继承)
(子类也可以是抽象类,但是子类必须在找子类继承)
public class Student extends Person{
//子类不是抽象类,那么必须重写父类中得抽象方法
public void say(){
System.out.println("我是学生");
}
}
public class Nurse extends Person{
public void say(){
System.out.println("我是护士");
}
}
抽象类常见问题
1、抽象类不能被实例化(new)
2、抽象类不能被final声明(因为必须要有子类)
3、抽象类有构造方法(JVM自己创建,但是我们不能自己创建)
子类在对象实例化时候,流程是和普通类继承一样得,都是要先调用父类得构造方 法,之 后在调用子类自己得构造方法。
4、抽象类必须用public 或者procted修饰。
5、子类必须实现重写=所有=抽象方法,如果一个抽象方法没有被重写,则子类必须也为抽象类
接口(进阶要面向接口编程)
降低程序耦合性
利于维护扩展
接口和抽象类得差别:接口全是抽象方法(和全局常量),不能有具体实现得方法
interface 接口名称{
全局常量:
抽象方法():(不能有大括号{}),
且方法自动添加public abstract,所以可以不用添加
}
接口可以多继承
class 子类 implements 父接口1 ,父接口2{
}
接口和抽象类得区别
1、抽象类:被子类继承
接口: 被子类实现
2、抽象类:可以声明抽象方法和非抽象方法
接口:只能声明抽象方法
3、抽象类:可以定义普通变量
接口:定义得变量全是公共得静态常量
4、抽象类:被继承使用,无多继承
接口:使用实现来使用,可以多继承
5、抽象类:可以有构造方法
接口:不能有构造方法
多态
多态的体现:
子父类:子类就是父类的一种心态
重载:一个类中方法的多态性体现
重写:子父类中方法的多态性体现
多态的使用:对象的类型转化
向上转型:将子类实例变为父类实例:
格式:父类 父类对象名称 = 子类实例;
向下转型:将父类实例变为子类实例:
格式:子类 子类对象名称 = (子类)父类实例;
a是学生,b是护士
所以a也可以是人,b也可以是人。
人也可以是a,人也可以是b
但是a不可以是b
instanceof,判断传入的对象是此类型的那种形态(那个子类对象)
Public static void main(String[] args){
Nurse n = new Nurse();
say(n);
Student s = new Student();
say(s);
}
public static void say(Person p){
if(p instanceof Student){ // 判断是否为学生类
Student s =(Student)p;
s.say();
}else{
System.out.println("必须传入学生心态,才可以执行");
}
}
Object类
-
Object 是所有类的父类(基类),如果一个类没有明确的继承某一个具体的类,则将默认继承Object类。
-
Object可以接受任意的引用数据类型
toString 方法, equals方法
toString建议重写
public String toString(){
return "姓名:"+ this.name +" 年龄:"+this.age;
}
equals方法
Person p1 = new Person("张三",18);
Person p2 = new Person("张三",18);
System.out.println(p1 == p2); //false (可以看成==是来判断地址是否相等,因为两个对 象,所以地址不等)
如果比较两对象用“= =”是比较地址,如果要比较两个地址块的内容的话,就得重写equals方法
public boolean equals(Object o){
if(this == 0){ //如果传进来的o地址和this相等,那么就相等
return true;
}
if(o == null){
return false;//如果传进来的o地址为null,则不相等
}
if(o instanceof Person){ //对Person类的判断
Person p2 = (Person)o; // o的类型是Person类或者多态形式,进行强转
if(this.name.equals(p2.name) && this.age == p2.age){
//如果名字和年龄相等,则判断为相等
return true;
}
}
return false;
}
快捷键 shift+alt+s,系统可以自动生成
内部类(少用)
定义:
一个类里面在或者一个方法里面,在定义一个类
四种:
1、成员内部类
类似成员变量,成员方法
2、局部内部类
定义在方法里的类
3、匿名内部类
4、静态内部类
成员内部类
特点:
成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态 成员)。
不过要注意的是,当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员。如果要访问外部类的同名成员,需要以下面的形式进行访问:
外部类.this.成员变量
外部类.this.成员方法
局部内部类
定义在方法中
匿名内部类(和匿名类一样,只用一次)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IcVHWsp5-16
使用限制:
1、使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。
2、匿名内部类中是不能定义构造函数的。
3、匿名内部类中不能存在任何的静态成员变量和静态方法。
4、匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。
5、匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。
6、只能访问final型的局部变量
为什么只能访问final局部变量
(后续学习),文件生成问题.class文件
因为内部类会被单独的编写成一个字节码文件,为了保证单独的字节码文件使用的变量和外面的是一致的,所以一定要用final进行限制不能修改
静态内部类
静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。
==静态内部类是不需要依赖于外部类对象的==,这点和类的静态成员属性有点类似,==并且它不能使用外部类的非static成员变量或者方法.==(因为静态保存在方法区,加载的比非静态的快)
包装类
可变参数
public static void main(String[] args){
//以数组形式
System.out.Println(sum(1,2,3,4,5)); // 结果15
}
public static int sum(int... nums){
//可变参数,调用的时候可以传0-n个参数
//参数在方法内部 , 以数组的形式来接收
int a =0;
for(int i=0;i<nums.length;i++){
n += nums[i];//累加
}
return n;
}
注意:
方法区,加载的比非静态的快)
包装类
可变参数
public static void main(String[] args){
//以数组形式
System.out.Println(sum(1,2,3,4,5)); // 结果15
}
public static int sum(int... nums){
//可变参数,调用的时候可以传0-n个参数
//参数在方法内部 , 以数组的形式来接收
int a =0;
for(int i=0;i<nums.length;i++){
n += nums[i];//累加
}
return n;
}
注意:
可变参数只能出现在参数列表的最后。