基础6
构造方法
- 特点:与类同名而没有返回值类型的方法 可以写return(用于规避一些不合法的数据)
- 作用:创建对象, 初始化属性值
this关键字
- this关键字在类中调用本类里面的属性和方法,代表当前在活动的对象。
- this语句 -> this(参数列表)
- 表示在本类的构造方法中调用本类对应形式的其他的构造方法
- this语句必须放在构造方法的第一行
代码块
- 构造代码块:定义在类中用{}包起来的代码 — 在创建对象的时候先于构造方法执行
- 局部代码块:定义在方法中用{}包起来的代码 — 提高了栈内存的利用率
面向对象的特征
封装、继承、多态(抽象)封装
- 定义:将代码进行提取和概括,抽取成不同的形式
- 体现形式:方法、类(属性的私有化 -> 将属性设置为private,对外提供对应的get/set的方法,在方法中可以进行限定使数据更加符合场景要求)
- 优势:提高了代码的复用性,保证了数据的合法性
- 规定用Private修饰的变量,不能在类外引用(赋值和提取),只能通过对象调用方法进行访问!
一些属性需要限制条件,但判断语句只能写在方法中,所以在一个方法中赋值和限制,提供了类外的访问方式,只能用方法的传值进行赋值,这就是封装!
继承
- 如果一些类中含有相同的代码,那么可以将这些相同的代码提取到一个新的类中,然后通过extends关键字让原来的类和新的类产生关系。
- 原来的类就称之为了子类(派生类),新的类就成了父类(超类/基类)。
- 子类通过继承父类可以使用父类中的一部分方法和属性
- 注意:子类继承了父类的全部的数据域(方法+属性),但是继承之后只有一部分数据域对子类可见
单继承 vs 多继承 - 在Java中,支持的是类和类之间的单继承 -> 一个子类只能继承一个父类,但是一个父类可以有多个子类
- 单继承一定优于多继承吗? - 不对
class A {
public int m(){return 9;}
}
class B {
public boolean m(){return false;}
}
class C extends A, B{}
C c = new C();
c.m(); // 返回值类型能确定吗?
- 多继承比单继承能够更好的提高代码的复用性,但多继承导致在调用方法的时候可能产生歧义
- 单继承提高代码的复用性,避免方法的调用产生歧义
权限修饰符
指在Java中用于限定使用范围的关键字
- 继承子父类关系,能不能访问一定要看修饰符!
- Public, 是公共的,都可以用!
- Protected,判断被protected修饰的父类中的属性和方法,判断方法本身所在的位置和目前所在的位置(当前使用类)是不是一个包,同包的话,能用!不同包的话,对象所在的类和方法本身所在类是不是子父类关系,是,判断创建的是父类对象还是子类对象,是子类的话,子类对象使用的时候必须在对应子类中使用;是父类的话,是当前类(目前所在的位置)子类就可以用!
- protected在子类中使用的时候指的是在对应的子类中使用,不能跨子类使用
- 默认 ,以同包为核心来判断!只要同包就可以,不同包就不可以!这个不是子父类的那这种,是new一个同包类,可以访问他自己的属性和方法!
Private ,只有自己可以!
super关键字
-
在子类中用于表示父类对象的引用,可以在子类中调用父类中的方法和属性。
-
super语句 — 子类在继承父类之后,子类的构造方法中会含有一个super语句。
-
如果没有手动指定super语句,那么默认使用super()调用父类无参的构造;
-
如果父类只提供了含参构造,那么子类就必须手动提供对应形式的super语句
-
super语句必须在子类构造方法的首行
Super 代替父类对象的引用!
-
1.在子类构造方法中,如果没写的话,默认添加super()!
-
2.子类必须调用父类的构造方法!
-
3.子类的构造方法的写法(手动添加super语句)依赖父类的构造方法,如果父类中只提供了含参构造,那么子类中就必须手动提供对应形式的super语句!
-
4.Super语句必须在子类构造方法的第一行!
-
5.Super语句和this语句不能同时使用!因为只有一个第一行!
方法的重写/覆盖
- 重写:在父子类中存在了方法签名相同的非静态方法。
- 方法签名:方法名和参数类型相同
遵循 “两等两小一大”原则:
- 方法签名相同
- 如果父类中的方法的返回值类型是基本类型/void,那么子类重写的方法的返回值类型与父类一致
- 如果父类中的方法的返回值类型是引用类型,那么子类在重写方法的时候,返回值类型要么与父类一致,要么是父类方法返回值类型的子类
- 子类重写的方法的权限修饰符的范围要大于等于父类中对应方法的权限修饰符的范围
- 子类重写的方法抛出的编译时异常不能超过父类方法的编译时异常的范围。
注意:如果父类中的方法用private修饰,从继承角度看,父类的这个方法对子类不可见,所以此时与子类中的方法构不成重写
解释4
注意:Java中所有的基本类型之间没有继承关系,之所以能够自动提升,是因为所表示的范围是否能够包含
解释3
多态
- 根据何时确定执行多态方法中的哪一个,多态分为两种情况:编译时多态和运行时多态。如果在编译时能够确定执行多态方法
编译时多态:方法的重载
- 方法重载都是编译时多态。根据实际参数的数据类型、个数和次序,Java在编译时能够确定执行重载方法中的哪一个,称为编译时多态,否则称为运行时多态。
- 方法覆盖表现出两种多态性,当对象引用本类实例时,为编译时多态,否则为运行时多态。例如,以下声明p、m引用本类实例,调用toString()方法是编译时多态。
public class Test {
public static void main(String[] args) {
Person p = new Person(); //对象引用本类实例
Man m = new Man(); //编译时多态,执行Person类的toString()
System.out.println(p.toString());
System.out.println(m.toString()); //编译时多态,执行Man类的toString()
}
}
class Person{
public String toString() {
String name = "Person";
return name;
}
}
class Man extends Person{
public String toString(){
String name = "Man";
return name;
}
}
运行时多态:向上造型和方法的重写 — 基于继承的
- 注意:如果使用向上造型来创建对象,那么这个对象所能调用的方法看的是父类中的声明,方法如何执行看的是子类中的实现过程
运行时多态: 向上造型和方法的重写 -> 基于继承的
- 继承 -> 重写
继承提供子父类关系,加一个条件(方法签名一致,非静态方法)构成重写,在子类中创建对象,调用重写方法,优先执行子类中重写的方法,构成多态!子类没重写的话,就用父类的! - 继承 -> 向上造型
继承提供子父类关系,父类引用指向子类对象(父类声明,子类创建),引用调用父类中的方法(能干什么),具体执行的时候用的是子类中重写的方法(并且在这个重写的方法中默认有一行super.父类中的方法),子类没重写的话,用父类的,构成多态!
package exer1;
public class Demo extends Demo1{
@Override
public void m() {
super.m();
}
public static void main(String[] args) {
Demo1 d = new Demo();
d.m();
System.out.println("------");
}
}
class Demo1{
public void m(){
System.out.println("haha");
}
}
向上造型 -> Pet p = new Cat();引用调用父类中的方法,执行子类中重写的方法
-
1.对象在编译过程中并不会检查到底使用的是哪个子类
-
2.在编译期间只会检查声明类和实现类之间是否有继承关系
-
3.直到运行的时候才会检查具体的子类然后根据子类来分配空间
小知识
- 变量遵循就近原则!
- 构造代码块/初始化代码块,无论利用哪个构造方法创建对象,构造代码块都会先于构造方法执行
- 如果每一个构造方法中都有一些要初始化的操作,可以将它们提取到构造代码块中执行
- 没写构造方法,程序在编译时就自动添加一个无参的构造方法,写了的话,不再自动添加无参的构造方法了,但自己可以写。
- 所有自己写的都要见名知意
- 当类中没有手动指定构造方法的时候在编译的时候自动添加一个无参构造
package cn.tedu.object;
public class LocalCodeDemo {
public static void main(String[] args) {
int i = 8;
// 定义在方法中代码块 --- 局部代码块
{
// j的作用范围变小,生命周期变短,释放内存 -> 提高栈内存的利用率
int j = 10;
System.out.println(i + j);
}
System.out.println(i);
}
}
package cn.tedu.fengzhuang;
public class FengzhuangExer {
public static void main(String[] args) {
Rectangle r = new Rectangle(5.45, 3.87);
System.out.println(r.getGirth());
System.out.println(r.getArea());
}
}
// 定义一个代表矩形的类
class Rectangle {
private double width; // 宽
private double height; // 高
// 当矩形定义好之后,宽和高就固定下了
public Rectangle(double width, double height) {
// 判断宽和高是否合法
if (width <= 0 || height <= 0)
return;
this.width = width;
this.height = height;
}
// 矩形画好之后宽和高不能产生变动了,所以不需要再提供set方法
public double getWidth() {
return width;
}
public double getHeight() {
return height;
}
// 计算周长
public double getGirth() {
return 2 * (width + height);
}
// 计算面积
public double getArea() {
return width * height;
}
}