Java面向对象(javaBean、继承、方法重写、多态、动态绑定)
- 1 javaBean 和关键字(package/import)
- 2 继承(特征二)
- 3 方法重写(override/overwrite)
- 4 多态(特征三)
- 4.1 super 关键字
- 4.2 super 与 this
- 4.3 属性的多态问题
- 4.4 子类在内存的分配方式
- 5 jvm 绑定机制
- 5.1 静态绑定
- 5.2 动态绑定
1 javaBean 和关键字(package/import)
1、javaBean 是 java 开发中常有的实体类,在 java 开发中符合 javaBean 类的有以下几点:
- (1)类的访问权限修饰符 public 的;
- (2)类中的属性要私有化 private;
- (3)类中要提供 getter/setter 方法。
2、package 关键字作用:定义包
- 语法:package 包名;
包名之间用“.”分隔,包在文件中显示是文件夹,例如:package com.test; - 特点:对类以目录的方式进行管理。
- 规则:
(1)包名一般是小写的单词;
(2)定义包名规范:项目的类型.公司名称.项目的名称.模块名称;
(3)package 写在类的首行。
3、import 作用:导入类、第三方工具类
- 语法:import [static] 包名.类名; //也可以import 包名.*; 不建议
- 静态导入:import static java.lang.System.out;
此时使用 out.println(“内容”) 就可以在控制台打印内容。 - 注意:不同包下拥有同名的类,使用时可以写:包名.类名 对象 = new 包名.类名();。
2 继承(特征二)
1、为什么要用继承?
为了避免出现重复的代码。
2、继承的作用:实现代码的复用。
3、语法:
class subClass extends superclass{
}
- subClass: 子类,派生类;
- superClass: 父类,(基类,超类);
- extends:关键字,表示扩展,子类复用了父类中的代码,而且子类中还可以封装自己的成员。
4、特点:
- (1)子类继承父类,拥有父类中的所有成员,(private访问权限修饰的成员除外);
- (2)构造器不能被子类继承,只能子类通过 super() 关键字来调用;
- (3)一个子类只能有一个直接父类,父类可以有多个子类(单继承);
- (4)继承具有传递性。
5、代码测试:FatherTest.java
package com.zc.test;
/**
* @author zc
* @date 2021/11/4 14:43
* 继承测试:FatherTest
*
* main(String[] args):主程序入口
*/
public class FatherTest {
/**
* 主程序入口
* @param args 系统参数
*/
public static void main(String[] args) {
// 初始化子类 Son 实例化对象 son
Son son = new Son();
// 给子类 son 继承父类的属性 name 赋值
son.name="张三";
// 给子类 son 继承父类的属性 age 赋值
son.age=18;
// 给子类 son 自己携带的属性 smoking 赋值
son.smoking=true;
// 子类 son 调用继承父类的方法 eat()
son.eat();
// 子类 son 调用自己携带的方法 walk()
son.walk();
System.out.println();
// 输出 son 内存的地址
System.out.println(son.toString());
}
}
/**
* 自定义一个父类:Father
*/
class Father{
/**
* 属性 name:姓名
*/
String name;
/**
* 属性 age:年龄
*/
int age;
/**
* 无参构造器
*/
public Father() {
System.out.println("父类无参");
}
/**
* 有参构造器
* @param name 参数姓名
* @param age 参数年龄
*/
public Father(String name, int age) {
super(); //显式调用父类(Object)的构造器
this.name = name;
this.age = age;
}
/**
* 打印一下
*/
public void eat() {
System.out.println("父类中的吃的行为");
}
}
/**
* 子类 Son 继承父类 Father
*/
class Son extends Father{
/**
* 属性 smoking:吸烟
*/
boolean smoking;
public Son() {
super();
System.out.println("子类无参构造 ");
}
/**
* 方法,打印一下
*/
public void walk() {
System.out.println("走路");
}
}
/**
* 子类 Daughter 继承父类 Father
*/
class Daughter extends Father{
/**
* 属性 beauty:美女
*/
String beauty;
/**
* 打印一下
*/
public void shopping() {
System.out.println("购物");
}
}
运行结果:
父类无参
子类无参构造
父类中的吃的行为
走路
com.zc.test.Son@74a14482
3 方法重写(override/overwrite)
1、方法的重写:方法的覆盖。
2、前提:必须要有继承关系。
3、说到方法重写,看看方法重载的特点:
- (1)在同一类中,方法名必须相同;
- (2)方法的参数列表必须不同(类型,个数,顺序);
- (3)与返回值无关;
4、方法重写的特点:
- (1)必须有继承关系或子父类关系,方法名,参数列表,返回值类型必须一致;
- (2)访问权限修饰符子类不能小于父类的访问权限修饰符;
- (3)*子类异常不能大于父类抛出的异常;
- (4)*父类方法和子类的方法同为 static 或同为非 static,加上 static 不能实现重写。
4 多态(特征三)
1、多态概念:
一个事物的多种形态(两个或两个以上的类(对象)对同一方法实现了不同的结果)。
2、实现多态方式:
- (1)使用重载或重写;
- (2)子类对象的多态性。
3、代码体现:
父类 对象 = new 子类();
- 父类引用指向子类对象。
将父类类型作为方法的参数或类的属性是不可以的:子类 对象 = new 父类();。这是错误的。
3、多态的状态:
- (1)编译时(左边);
- (2)运行时(右边)。
4、关于类型转换:
- 向上转型-自动转换:父类 对象 = new 子类();
- 向下转型:使用 instanceof 关键字进行判断类对象。
5、代码测试,在上面多态测试代码 FatherTest.java 中添加方法 play(Father father):
/**
* 判断父类对象向下转型属于哪个子类
* @param father 父类对象
*/
public static void play(Father father) {
// 调用父类自己的方法
father.eat();
// 如果父类对象 father 是子类对象 Son
if(father instanceof Son) {
// 将父类对象 father 强制转型为 Son 子类,并调用子类自己的方法
((Son)father).walk();
}
// 如果父类对象 father 是子类对象 Daughter
if(father instanceof Daughter) {
// 将父类对象 father 强制转型为 Daughter 子类,并调用子类自己的方法
((Daughter)father).shopping();
}
}
然后在 main 主函数中进行测试:
public static void main(String[] args) {
// 向上转型
Father father = new Daughter();
play(father);
}
运行结果:
父类无参
父类中的吃的行为
购物
4.1 super 关键字
1、使用:调用父类的成员。
- (1) 属性,如:super.属性;
- (2) 方法,如:super.方法();
- (3) 构造器,如:super() 或 super(参数)。
2、super 调用父类的构造器比较特殊:
- (1) 只要创建一个构造器,系统会默认在构造器的首行出现 super():表示默认调用父类无参构造器;
- (2) 如果某一个类没有创建构造器,系统会默认会分配一个无参的构造器;
- (3) super() 必须出现的构造器的首行,不能与 this() 同时出现;
- (4) super() 如果类中有 N 个构造器,可以出现 N 个 super([参数])。
4.2 super 与 this
this | super | |
属性 | this 可以调用本类的属性;this 可以调用父类的属性(私有权限不能调用) | super 只能调用父类的属性 |
方法 | this 可以调用本类的方法;this 可以调用父类的方法(私有权限不能调用) | super 只能调用父类的方法 |
构造器(构造方法) | this() 调用的是当前类中的构造器;this() 如果有 N 个构造器最多出现 N-1 个 this([参数]) | super() 显式调用父类的构造器;如果构造器中没有 super():表示隐式调用父类无参构造器 |
特殊 | 当前对象 | 无 |
this 关键字的测试:TestThis.java
/**
* @author zc
* @date 2021/11/30 14:38
* this
*/
public class TestThis {
public static void main(String[] args) {
Boy boy = new Boy();
boy.name="小明";
Girl girl = new Girl();
girl.name="小丽";
boy.marry(girl);
System.out.println("===============================");
girl.marry(boy);
}
}
class Boy{
String name;
public void marry(Girl girl) {
System.out.println("我要娶:"+girl.name);
// this代表的是当前对象 this总结谁调用此方法,那么this就表示那个对象
girl.marry(this);
}
}
class Girl{
String name;
public void marry(Boy boy) {
System.out.println("我要嫁:"+boy.name);
}
}
运行结果:
我要娶:小丽
我要嫁:小明
===============================
我要嫁:小明
4.3 属性的多态问题
属性无多态,方法有多态。
TestAttribute.java
/**
* @author zc
* @date 2021/11/30 14:48
* 属性的多态问题
*/
public class TestAttribute {
public static void main(String[] args) {
A a = new A();
// 10
System.out.println(a.id);
A a1 = new B();
// 10 属性无多态
System.out.println(a1.id);
// B 方法有多态
a1.info();
B b = new B();
// 11
System.out.println(b.id);
// B
b.info();
}
}
class A{
int id = 10;
public void info() {
System.out.println("A");
}
}
class B extends A{
int id = 11;
@Override
public void info() {
System.out.println("B");
}
}
运行结果:
10
10
B
11
B
4.4 子类在内存的分配方式
以上一个 TestAttribute.java 为例,子类 B 继承父类 A,实例化子类对象,分析子类 B 的内存分配方式:
// 实例化子类 B 对象
B b = new B();
// 调用子类B重写父类A的方法
b.info();
- 我们在 new 一个对象时,这个对象如果是继承一个父类,那么会在堆中会产生一个 this 和一个 super 引用,this 引用指向这个子类对象本身,super 引用则指向子类包含的父类对象,如何没有继承父类,则仅仅只有 this 引用。
- 当执行 b.info(); 时,只有当前 b 对象 this,没有父类 super,进入 B 类的 info() 方法,这是内存如下:
为了更清楚多态----父类引用指向子类对象,看看 JVM 的绑定机制。
5 jvm 绑定机制
在 jvm 加载的同时,会在方法区中为这个类存放很多信息,其中有一个数据结构叫方法表,它以数组的形式记录了当前类和所有父类的可见方法字节码在内存中的直接地址。此处 java 基础先不研究 jvm,先看表面的静态绑定与动态绑定。
5.1 静态绑定
在编译阶段能够确定方法在内存什么位置的机制就叫静态绑定机制。
所有私有方法、静态方法、构造器及 final 修饰的方法都是采用静态绑定机制。
在编译器阶段就已经指明了调用方法在常量池中的符号引用,JVM运行的时候只需要进行一次常量池解析即可。
比如 Arrays 工具类的调用:
// 静态初始化一个数组
int[] numArr = {12,4,78,56,32};
// 调用 Arrays 工具类对数组排序,此处 sort 方法就是静态绑定机制
Arrays.sort(numArr);
5.2 动态绑定
1、什么是动态绑定?
动态绑定是指在 “执行期间”(而非编译期间)判断所引用的实际对象类型,根据其实际的类型调用其相应的方法。
动态绑定是用效率换扩展性,判断的实际对象类型越多越耗时,但拥有很好的扩展性能。
2、实验:DynamicBinding.java
定义三个类:
- 父类 GeometricObject 代表几何形状:
类成员 | 描述 |
color | 几何颜色,属性 String 类型 |
weight | 几何重量,属性 double 类型 |
GeometricObject (String color, double weight) | 有参构造器 |
getter/setter | 属性 getter/setter 方法 |
findArea() | 函数,计算几何的面积,返回 double 类型 |
- 子类 Circle 代表圆形:
类成员 | 描述 |
radius | 圆半径,属性 double 类型 |
Circle (double radius, String color, double weight) | 有参构造器 |
getter/setter | 属性 getter/setter 方法 |
findArea() | 函数,计算圆的面积,返回 double 类型 |
- 子类 MyRectangle 代表矩形:
类成员 | 描述 |
width | 矩形的宽,属性 double 类型 |
height | 矩形的高,属性 double 类型 |
MyRectangle (double width, double height, String color, double weight) | 有参构造器 |
getter/setter | 属性 getter/setter 方法 |
findArea() | 函数,计算矩形的面积,返回 double 类型 |
定义一个测试类 DynamicBinding,编写 equalsArea 方法测试两个对象的面积是否相等(注意方法的参数类型,利用动态绑定技术),编写displayGeometricObject 方法显示对象的面积(注意方法的参数类型,利用动态绑定技术)。
import java.math.BigDecimal;
/**
* @author zc
* @date 2021/11/25 14:48
* 多态实验:动态绑定
*
* main(String[] args):主系统入口。
* equalsArea(GeometricObject object1, GeometricObject object2):函数,判断两个几何形状面积是否相等。
* displayGeometricObject(GeometricObject object):函数,显示几何形状的面积。
*/
public class DynamicBinding {
/**
* 主系统入口
* @param args 系统参数
*/
public static void main(String[] args) {
GeometricObject object1 = new Circle(10, "黑色", 2);
displayGeometricObject(object1);
GeometricObject object2 = new MyRectangle(10, 31.4, "黄色", 3);
displayGeometricObject(object2);
equalsArea(object1, object2);
}
/**
* 判断两个几何形状面积是否相等
* @param object1 几何形状 1
* @param object2 几何形状 2
*/
private static void equalsArea(GeometricObject object1, GeometricObject object2){
double area1 = 0.0;
double area2 = 0.0;
if (object1 instanceof Circle){
area1 = ((Circle) object1).findArea();
} else if (object1 instanceof MyRectangle){
area1 = ((MyRectangle) object1).findArea();
}
if (object2 instanceof Circle){
area2 = ((Circle) object2).findArea();
} else if (object2 instanceof MyRectangle){
area2 = ((MyRectangle) object2).findArea();
}
if (area1 != 0.0 && area2 != 0.0){
BigDecimal decimal1 = new BigDecimal(area1);
BigDecimal decimal2 = new BigDecimal(area2);
if (decimal1.equals(decimal2)){
System.out.println("两个几何形状面积相等");
} else {
System.out.println("两个几何形状面积不相等");
}
} else {
System.out.println("几何形状面积不能为 0");
}
}
/**
* 显示几何形状的面积
* @param object 几何形状实例对象
*/
private static void displayGeometricObject(GeometricObject object){
if (object instanceof Circle){
double area = ((Circle)object).findArea();
System.out.println("几何形状为圆,面积为:" + area);
} else if (object instanceof MyRectangle) {
double area = ((MyRectangle)object).findArea();
System.out.println("几何形状为矩形,面积为:" + area);
}
}
}
/**
* 父类--几何形状: GeometricObject
*/
class GeometricObject {
/**
* 属性,几何颜色 color
*/
private String color;
/**
* 属性,几何重量 weight
*/
private double weight;
/**
* 有参构造器
* @param color 参数,几何颜色
* @param weight 参数,几何重量
*/
GeometricObject (String color, double weight) {
this.color = color;
if (weight > 0){
this.weight = weight;
} else {
System.out.println("几何重量要大于 0,不然默认为 10.0");
this.weight = 10.0;
}
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
if (weight > 0){
this.weight = weight;
} else {
System.out.println("几何重量要大于 0,不然默认为 10.0");
this.weight = 10.0;
}
}
public double findArea(){
return 0.0;
}
}
/**
* 子类--圆形:Circle
*/
class Circle extends GeometricObject {
/**
* 属性,半径
*/
private double radius;
/**
* 有参构造器
* @param radius 参数,圆的半径
* @param color 参数,圆的颜色
* @param weight 参数,圆的重量
*/
Circle (double radius, String color, double weight) {
super(color, weight);
if (radius > 0){
this.radius = radius;
} else {
System.out.println("圆的半径要大于 0,不然默认为 10.0");
this.radius = 10.0;
}
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
if (radius > 0){
this.radius = radius;
} else {
System.out.println("圆的半径要大于 0,不然默认为 10.0");
this.radius = 10.0;
}
}
/**
* 重写父类方法,计算圆的面积
* @return
*/
@Override
public double findArea(){
return 3.14 * Math.pow(radius, 2);
}
}
/**
* 子类--矩形:MyRectangle
*/
class MyRectangle extends GeometricObject {
/**
* 属性,矩形宽
*/
private double width;
/**
* 属性,矩形高
*/
private double height;
/**
* 有参构造器
* @param width 参数,矩形宽
* @param height 参数,矩形高
* @param color 参数,矩形颜色
* @param weight 参数,矩形重量
*/
MyRectangle (double width, double height, String color, double weight) {
super(color, weight);
if (width > 0){
this.width = width;
} else {
System.out.println("矩形的宽要大于 0,不然默认为 10.0");
this.width = 10.0;
}
if (height > 0){
this.height = height;
} else {
System.out.println("矩形的高要大于 0,不然默认为 10.0");
this.height = 10.0;
}
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
if (width > 0){
this.width = width;
} else {
System.out.println("矩形的宽要大于 0,不然默认为 10.0");
this.width = 10.0;
}
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
if (height > 0){
this.height = height;
} else {
System.out.println("矩形的高要大于 0,不然默认为 10.0");
this.height = 10.0;
}
}
/**
* 重写父类方法,计算矩形的面积
* @return
*/
@Override
public double findArea(){
return width * height;
}
}
运行结果:
几何形状为圆,面积为:314.0
几何形状为矩形,面积为:314.0
两个几何形状面积相等