正在玩命加载中 . . .

JavaSE基础


JDK、JRE、JVM

  • JDK:Java开发工具箱
  • JRE:java运行环境
  • JVM:java虚拟机

JDK包括JRE,JRE包括JVM。

JVM是不能独立安装的,JRE和JDK都是可以独立安装的。有单独的JDK安装包,也有单独的JRE安装包,没有单独的JVM安装包。

安装JDK的时候,JRE就自动安装了,同时JRE内部的JVM也就自动安装了。安装JRE的时候,JVM也就自动安装了。

问题1:假设你在软件公司开发了一个新的软件,现在要去客户那边给客户把项目部署一下,把项目跑起来,你需要安装JDK吗?

:只需要安装JRE就行了。JRE体积很小,安装非常便捷快速。

问题2:为什么安装JDK的时候会自带一个JRE?

:因为java程序员开发完程序之后,要测试这个程序,让这个程序运行起来,需要JRE。所以JDK安装的时候内部自带一个JRE。

Java的加载与执行(理论比较重要)

java程序非常重要的两个阶段:

  • 编译阶段
  • 运行阶段

注意:java程序员直接编写的java代码(普通文本)是无法执行被JVM识别的。java程序员编写的java代码这种普通文本必须经过一个编译,将这个“普通文本代码”变成“字节码”,JVM能够识别“字节码”。java代码这种普通文本变成字节码的过程,被称为:编译。

问题1:编译阶段和运行阶段可以在不同的操作系统上完成吗?

:在windows上编译,编译之后生成了“字节码”,把“字节码”放到linux上运行,完全可以。因为Java是跨平台的,可以做到一次编写到处运行。

数据类型

关于计算机存储单位

计算机只能识别二进制。(1001101100…)

1字节 = 8bit(8比特)即 1byte = 8bit,1bit就是一个1或0。

1KB = 1024byte
1MB = 1024KB
1GB = 1024MB
1TB = 1024GB

面向对象

概述

“面向对象”(Object Oriented)是一种以对象为中心的编程思想,简称 OO。

特征

面向对象具有三大特征:

  • ① 封装(Encapsulation)
  • ② 继承(Inheritance)
  • ③ 多态(Polymorphism)

类的定义

[修饰符] class 类名 {
    类体 = 属性 + 方法
}

比如:定义一个学生类

public class Student{
    int no;
    String name;
    int age;
    boolean sex;
}

以上程序当中 no、name、age、sex 都是属性,它们都是成员变量中的实例变量,所谓实例变量就是对象级别的变量,这些属性要想访问,必须先创建对象才能访问,不能直接通过类去访问,因为每一个学生的学号都是不一样的。没有学生对象,谈何学号!

对象的创建和使用

知识框架

对象的创建和使用知识框架

对象的创建和使用

对象的创建

一个类是可以创建多个对象的,语法格式:new 类名()。

比如:

public class StudentTest {
    public static void main(String[] args) {
        //创建一个学生对象
        Student s1 = new Student();
        //再创建一个学生对象
        Student s2 = new Student();
        //以上代码其实和这行代码差不多
        int i = 10;
    }
}

对于 Student s1 = new Student() 来说,其中 Student 是一种引用数据类型,s1 是变量名,new Student() 执行之后是一个 Student 类型的对象。 (s1与new Student() 的概念要分清),具体见下图:

引用与对象

对象的使用

public class StudentTest {
    public static void main(String[] args) {
        //创建一个学生对象
        Student s1 = new Student();
        //以上代码其实和这行代码差不多
        int i = 10;
        int no1 = s1.no;
        System.out.println("学号:" + no1);
        String name1 = s1.name;
        System.out.println("姓名:" + name1);
        int age1 = s1.age;
        System.out.println("年龄:" + age1);
        boolean sex1 = s1.sex;
        System.out.println("性别:"+ sex1);
    }
}

以上程序中并没有给学号赋值,在 java 语言当中,当实例变量没有手动赋值,在创建对象的时候,也就是说在 new 的时候,系统会对实例变量默认赋值,它们的默认值请参考下表:

数据类型 默认值
byte 0
short 0
int 0
long 0L
float 0.0f
double 0.0
boolean false
char \u0000
引用类型 null

深层理解

java虚拟机(JVM)内存管理

JVM内存管理图

① 程序计数器:

  • 概念:可以看做当前线程所执行的字节码的行号指示器
  • 特点:线程私有的内存

② java 虚拟机栈(重点):

  • 概念:描述的是 java 方法执行的内存模型。(每个方法在执行的时候会创建一个栈帧,用于存储局部变量表,操作数栈,动态链接,方法出口等信息。每个方法从调用直至完成的过程,就对应一个栈帧从入栈到出栈的过程。)
  • 特点 :线程私有,生命周期和线程相同 。这个区域会出现两种异常:StackOverflowError 异常: 若线程请求的深度大于虚拟机所允许的深度。OutOfMemoryError 异常:若虚拟机可以动态扩展,如果扩展是无法申请到足够的内存。

③ 本地方法栈:

  • 概念:它与虚拟机栈所发挥的作用是相似的,区别是 java 虚拟机栈为执行 java 方法服务,而本地方法栈是为本地方法服务。
  • 特点:线程私有,也会抛出两类异常:StackOverflowError 和OutOfMemoryError。

④ java 堆(重点):

  • 概念:是被所有线程共享的一块区域,在虚拟机启动时创建。
  • 特点:线程共享,存放的是对象实例(所有的对象实例和数组),GC 管理的主要区域。可以处于物理上不连续的内存空间。

⑤ 方法区(重点):

  • 概念:存储已被虚拟机加载的类信息、常量、静态变量,即时编译器编译后的代码等数据。
  • 特点:线程共享的区域,抛出异常 OutOfMemory 异常:当方法区无法满足内存分配需求的时候。

目前,java虚拟机有三块主要的内存空间,分别是“虚拟机栈(后面简栈)”、“方法区”、“堆区”。

  • 方法区:存储类的信息
  • 栈:存储方法执行时的栈帧以及局部变量
  • 堆:主要存储 new 出来的对象,以及对象内部的实例变量

JVM内存管理简图

对象和引用的区别

  1. 对象是通过 new 出来的,在内存中存储
  2. 引用是:但凡是变量,并且该变量中保存了内存地址指向内存当中的对象的。

构造方法Constructor

构造方法是类中特殊的方法,通过调用构造方法来完成对象的创建,以及对象属性的初始化操作。

[修饰符列表] 构造方法名(形式参数列表){
    构造方法体;
}

注意

  • 构造方法名和类名一致
  • ② 构造方法用来创建对象,以及完成属性初始化操作
  • ③ 构造方法返回值类型不需要写,写上就报错,包括 void 也不能写
  • ④ 构造方法的返回值类型实际上是当前类的类型
  • ⑤ 一个类中可以定义多个构造方法,这些构造方法构成方法重载

当一个类没有显示的定义任何构造方法的时候,系统默认提供无参数构造方法,当显示的定义构造方法之后,系统则不再提供无参数构造方法。建议程序员手动的将无参数构造方法写上,因为不写无参数构造方法的时候,这个默认的构造方法很有可能就不存在了。

构造方法虽然在返回值类型方面不写任何类型,但它执行结束之后实际上会返回该对象在堆内存当中的内存地址,这个时候可以定义变量接收对象的内存地址,这个变量就是之前所学的“引用”。

构造方法的作用是专门用来创建对象同时给属性赋值的

空指针异常

java.lang.NullPointerException 被称为空指针异常。

Balloon ball = new Balloon;

ball = null;

总之,当一个“空的引用”去访问“对象相关/实例相关”数据的时候,此时一定会发生空指针异常。

封装

知识框架

封装的知识框架

封装的理解

封装之后就形成了独立实体,独立实体可以在不同的环境中重复使用,显然封装可以降低程序的耦合度,提高程序的扩展性,以及重用性或复用性。另外封装可以隐藏内部实现细节,站在对象外部是看不到内部复杂结构的,对外只提供了简单的安全的操作入口,所以封装之后,实体更安全了。

如何封装

  1. 使用java中的修饰符:

    • private,私有,私有数据只能在本类中访问

    public class MobilePhone {
        //电压:手机正常电压在 3~5V
        private double voltage;
    }    
  2. 对外提供公开的访问入口,让外部程序统一通过这个入口去访问数据。这两个方法通常被称为 set 方法get 方法

  3. public class MobilePhone {
        //电压:手机正常电压在 3~5V
        private double voltage;
    
        public MobilePhone(){}
        public void setVoltage(double _voltage){
            if(_voltage < 3 || _voltage > 5){
            //当电压低于 3V或者高于 5V时抛出异常,程序则终止
            throw new RuntimeException("电压非法,请爱护手机!");
            }
            //程序如果能执行到此处说明以上并没有发生异常,电压值合法
            voltage = _voltage;
        }
        public double getVoltage(){
            return voltage;
        }
    }

this和static

知识框架

this和static知识框架

static

对于static,看看这篇文章:深入理解static关键字

static 是 java 语言中的关键字,可以用来修饰变量、方法、代码块等

  1. 翻译为 “ 静态的 ” 。
  2. 所有static关键字修饰的都是类相关的,类级别的。
  3. 所有static修饰的,都是采用“ 类名. ”的方式访问,不需要创建对象。
  4. static修饰的变量:静态变量。
  5. static修饰的方法:静态方法。

静态变量

  1. 变量根据声明的位置进行划分:

    • 局部变量:方法体中声明,方法体中访问,方法结束之后局部变量内存就释放了,内存方面局部变量存储在当中。
    • 成员变量:类体中定义。
  2. 成员变量又可以分为:实例变量,静态变量

    • 静态变量:使用了 static 关键字,存储在方法区当中,在类加载时初始化
    • 实例变量:未使用 static 关键字,属于对象级别,存储在堆内存当中,在构造方法执行过程中初始化

    Static1

    注意:实例相关的,必须先创建对象,通过引用,才能访问,否则可能会出现空指针异常;静态的,不需要对象的参与,直接通过“ 类名 ”即可访问,没有空指针异常的发生。

  3. 何时使用?

    观看以下Chinese类案例:

    Chinese类

    PS:只要是方法,不管是静态方法、实例方法,还是构造方法,它们在运行的时候都需要压栈。

    以下是未使用静态变量时的内存图:

    Static2

    分析:“中国人类”创建的所有“中国人对象”,“国籍”都是“中国”,不随对象的改变而改变,如果定义为实例变量的话,就会浪费大量堆内存空间。

    静态变量在类加载时初始化,不需要new对象,静态变量的空间就开出来了。静态变量存储在方法区:

    static String country = “中国”;

    以下则是使用了静态变量时的内存图:

    Static3

    分析:建议将“国籍”属性定义为类级别的属性,声明为静态变量,上升为“整个族”的数据,这样的变量不需要创建对象直接使用“类名”即可访问。

注意:静态的(static)也可以使用引用访问,但是建议使用类名访问。引用如果出现空指针,仍然可以访问静态的,不会出现空指针异常空指针异常只有出现在空引用访问实例相关的,才会出现。

静态代码块

对于静态代码块,详情点击:Static静态代码块以及各代码块之间的执行顺序

静态方法

语法格式:

类{
    //静态代码块
    static{
        java 语句;
    }
}

静态代码块在类加载时执行,并且只执行一次。静态代码块实际上是 java 语言为程序员准备的一个特殊的时刻,这个时刻就是类加载时刻

比如:

public class StaticTest01 {
    //静态代码块
    static{
        System.out.println(2);
    }
    //静态代码块
    static{
        System.out.println(1);
    }
    //main 方法
    public static void main(String[] args) {
        System.out.println("main execute!");
    }
    //静态代码块
    static{
        System.out.println(0);
    }
}

运行结果:

2
1
0
main execute!

静态代码块遵循自上而下的顺序依次执行。另外静态代码块当中的代码在 main 方法执行之执行。

实例:

public class StaticTest02 {
    int i = 100;
    static{
        System.out.println(i);
    }
}        

运行结果:

错误:无法从静态上下文中引用非静态变量 i
​ System.out.println(i);

运行结果报错,原因:i 变量是实例变量,实例变量必须先创建对象才能访问,静态代码块在类加载时执行,这个时候对象还没有创建呢,所以 i 变量在这里是不能这样访问的。 可以考虑将实例变量 i 变为静态变量。

this

概述

public class Customer {
    private String name;
    public Customer(){
    }
    public Customer(String _name){
        name = _name;
    }
    public void setName(String _name){
        name = _name;
    }
    public String getName(){
        return name;
    }
}
public class CustomerTest {
    public static void main(String[] args) {
        Customer c1 = new Customer("张三");
        Customer c2 = new Customer("李四");
    }
}

this关键字

this 可以看做一个变量,它是一个引用,存储在 Java 虚拟机堆内存的对象内部,this 这引用保存了当前对象的内存地址指向自身。

this使用在实例方法

this 无法在静态 static 的方法中,static 方法在调用的时候不需要创建对象,直接采用 “ 类名. ” 的方式调用,也就是说,是不需要 “ 当前对象 ” 参与。而 this代表的就是 “ 当前对象 ” 。

this 使用在实例方法当中,代表 “ 当前对象 ” 。

this使用在构造方法

this 使用在构造方法第一行,通过当前构造方法调用本类当中其他的构造方法,其目的是代码复用。

语法:

this(实际参数列表);

实例:

public class Date {
    private int year;
    private int month;
    private int day;
    //业务要求,默认创建的日期为 1970 年 1 月 1 日
    public Date(){
        this(1970 , 1, 1);
    }
    public Date(int year,int month,int day){
        this.year = year;
        this.month = month;
        this.day = day;
    }
    //set和get方法略...
}

继承(Inheritance)

继承看一看这篇文章:java继承从“我爸是李刚”讲起

知识框架

概述

继承时子类继承父类的特征和行为,使得子类对象(实例)具有父类的属性,或子类从父类继承方法,使得子类具有与父类相同的行为。所以继承符合的关系is-a

如何继承

语法格式:

class 类名 extends 父类名{
    类体;
}

实例:银行账户和信用账户(信用账户具有银行账户的性质)

public class Account { //银行账户类
    //账号
    private String actno;
    //余额
    private double balance;
    //账号和余额的 set 和 get 方法
    public String getActno() {
        return actno;    
    }
    public void setActno(String actno) {
        this.actno = actno;
    }
    public double getBalance() {
        return balance;
    }
    public void setBalance(double balance) {
        this.balance = balance;
    }
}    
public class CreditAccount extends Account{ //信用账户类
    //信誉度(特有属性)
    private double credit;
    //信誉度的 set 和 get 方法
    public double getCredit() {
        return credit;
    }
    public void setCredit(double credit) {
        this.credit = credit;
    }
}
public class AccountTest {
    public static void main(String[] args) {
        CreditAccount act = new CreditAccount();
        act.setActno("111111111");
        act.setBalance(9000.0);
        System.out.println(act.getActno() + "信用账户,余额" + act.getBalance() + "元");
    }
}

有了继承,才衍生出方法的覆盖多态

继承的相关特性

  • ① B类继承 A类,则称 A类为超类(superclass)、父类、基类,B类则称为子类(subclass)、派生类、扩展类。
  • ② java 中的继承只支持单继承,不支持多继承。
  • ③ 虽然 java 中不支持多继承,但有的时候会产生间接继承的效果。
  • ④ java 中规定,子类继承父类,除构造方法和被 private 修饰的数据不能继承外,剩下都可以继承。 注意:父类的 private 修饰的属性不能再子类中直接访问,可以间接访问。

测试继承自Object类的方法

以下是Object类的部分源代码:

Object类中toString方法源代码

注意

native——C++底层

尝试调用该方法:

public class ExtendsTest{
    public static void main(String[] args) {
        ExtendsTest et = new ExtendsTest();
        String s = et.toString();
        System.out.println(s);
    }
}

运行结果:

ExtendsTest@15db9742

等同对象在堆内存的内存地址(经过哈希算法)

执行顺序

难点:类和类继承之后的代码执行顺序

public class Test {
    public static void main(String[] args) {
        new H2();
    }
}
class H1{
       //父类代码块
    {    
        System.out.println("父类代码块");//--3
    }
    public H1(){
        System.out.println("父类构造");//--4
    }    
    static{
        System.out.println("父类静态代码块");//--1
    }
}
class H2 extends H1{
    static{
        System.out.println("子类静态代码块");//--2
    }
    //子类代码块
    {
        System.out.println("子类代码块");//--5
    }
    public H2(){
        System.out.println("子类构造");//--6
    }
}

分析:
子类 H2 继承 H1,new H2()执行的时候,会先进行类加载,先加载 H2 的父类 H1,所以 H1 当中的静态代码块先执行,然后再执行 H2 中的静态代码块,静态代码块执行结束之后,不会马上执行构造方法,代码块会先执行。Java 中有一条规则:子类构造方法执行前先执行父类的构造方法。所以父类 H1 的代码块先执行,再执行 H1 的构造方法,然后再执行 H2 的代码块,最后执行 H2 的构造方法。

println()方法

System.out.println(“Hello”);

System.out中,out后面没有小括号,说明out是变量名。另外System是一个类名,直接使用System.out,说明out是一个静态变量。System.out返回一个对象,然后采用“对象.”的方式访问println方法。

类比例子:

public class Test{
    //静态变量
    static Student stu = new Student();
    //程序入口
    public static void main(String[] args){
        Test.stu.exam();
    }
}
class Student{
    public void exam(){
        System.out.println("考试了!");
    }
}

方法覆盖和多态

知识框架

方法覆盖(Override)

以下是一个简单的程序:

public class Test{
    public static void main(String[] args){
        Person p = new Person();
        Chinese c = new Chinese();
        American a = new American();
        p.eat();
        c.eat();
        a.eat();
    }
}
class Person{
    public void eat(){
        System.out.println("我要吃饭!");
    }
}
class Chinese extends Person{
    public void eat(){
        System.out.println("我要吃中餐!");
    }
}
class American extends Person{
    public void eat(){
        System.out.println("我要吃西餐!");
    }
}

运行结果:

我要吃饭!
我要吃中餐!
我要吃西餐!

以上的程序中,Chinese和American类继承了Person类中的eat方法,并进行了覆盖。之后,子类对象调用了覆盖之后的方法。

构成方法覆盖的条件

  • ① 方法覆盖发生在具有继承关系的父子类之间,这是首要条件;
  • ② 覆盖之后的方法与原方法具有相同的返回值类型、相同的方法名、相同的形式参数列表,即相同

注意事项

  • ① 由于覆盖之后的方法与原方法一模一样,建议在开发的时候采用复制粘贴的方式,不建议手写;
  • 私有的方法不能被继承,所以不能被覆盖;
  • 构造方法不能被继承,所以也不能被覆盖;
  • ④ 覆盖之后的方法不能比原方法拥有更低的访问权限,可以更高
  • ⑤ 覆盖之后的方法不能比原方法抛出更多的异常,可以相同或更少
  • ⑥ 方法覆盖只是和方法有关,和属性无关;
  • ⑦ 静态方法不存在覆盖(不是静态方法不能覆盖,是静态方法覆盖意义不大);

简单示例

public class Test{
    public static void main(String[] args){
        Cat cat = new Cat();
        cat.move();
        cat.catchM();
        Bird bird = new Bird();
        bird.move();
    }
}
class Animal{
    public void move(){
        System.out.println("动物在移动!");
    }
}
class Cat{
    public void move(){
        System.out.println("猫在走猫步!");
    }
    public void catchM(){
        System.out.println("猫在抓老鼠!");
    }
}
class Bird{
    public void move(){
        System.out.println("鸟儿在飞翔!");
    }
}

总结

当父类中继承过来的方法无法满足当前子类业务需求的时候,子类有必要将父类中继承过来的方法进行覆盖/重写。

多态

对于多态,向上、向下转型,可以看看这篇文章:深入理解java多态没有烤山药的存在,java就不香了吗?

概述

多态就是“同一个行为”发生在“不同的对象上”会产生不同的效果。

java中允许出现的2中语法:

  • 向上转型(Upcasting),子类型转换为父类型,自动类型转换
  • 向下转型(Downcasting),父类型转换为子类型,强制类型转换

2中转型的前提是:必须有继承关系,否则报错。

向上和向下转型

示例及分析

public class Test{
    public static void main(String[] args){
        Animal a1 = new Cat();
        a1.move();
        a1.catchM();
        Animal a2 = new Bird();
        a2.move();
    }
}
class Animal{
    public void move(){
        System.out.println("动物在移动!");
    }
}
class Cat{
    public void move(){
        System.out.println("猫在走猫步!");
    }
    public void catchM(){
        System.out.println("猫在抓老鼠!");
    }
}
class Bird{
    public void move(){
        System.out.println("鸟儿在飞翔!");
    }
}

以上程序演示的就是多态。

多态就是“同一个行为(move)”作用在“不同的对象上”会有不同的表现结果。

java 中之所以有多态机制,是因为 java 允许一个父类型的引用指向一个子类型的对象。也就是说允许这种写法:Animal a2 = new Bird()**,属于向上转型(Upcasting),或者叫做自动类型转换**。

对于以下代码的分析:

Animal a1 = new Cat();
a1.move();

java 程序包括编译运行两个阶段,分析 java 程序一定要先分析编译阶段,然后再分析运行阶段:

  • 在编译阶段,编译器只知道 a1 变量的数据类型是 Animal,那么此时编译器会去 Animal.class 字节码中查找 move() 方法,发现 Animal.class 字节码中存在 move()方法,然后将该 move()方法绑定到 a1 引用上,编译通过了,这个过程我们可以理解为“静态绑定”阶段完成了。
  • 紧接着程序开始运行。进入运行阶段,在运行的时候实际上在堆内存中 new 的对象是 Cat 类型,也就是说真正在 move 移动的时候,是 Cat 猫对象在移动,所以运行的时候就会自动执行 Cat 类当中的 move()方法,这 个过程可以称为“动态绑定”。但无论是什么时候,必须先“静态绑定”成功之后才能进入“动态绑定”阶段
Animal a1 = new Cat();
a1.catchM();

上述代码,运行报错。原因:是因为“Animal a = new

Cat();”在编译的时候,编译器只知道 a 变量的数据类型是 Animal,也就是说它只会去Animal.class 字节码中查找 catchMouse()方法,结果没找到,自然“静态绑定”就失败了,编译没有通过。就像以上描述的错误信息一样:在类型为 Animal 的变量 a 中找不到方法catchMouse()。

修改:

//向上转型
Animal a = new Cat();
//向下转型:为了调用子类对象特有的方法
Cat c = (Cat)a;
c.catchMouse();

因此,得出结论:只有在访问子类型中特有数据的时候,需要先进行向下转型

instance of

示例代码:

public class Test05 {
    public static void main(String[] args) {
        Animal a = new Bird();
        Cat c = (Cat)a;
    }
}

以上的代码可以编译,语法上是没有错误的,但是运行时出现错误。这会产生ClassCastException,翻译为类型转换异常,此时需要instance of操作符。

示例代码:

public class Test05 {
    public static void main(String[] args) {
        Animal a = new Bird();
        if(a instanceof Cat){
            Cat c = (Cat)a;
            c.catchMouse();
        } else if(a instanceof Bird){
            Bird b = (Bird)a;
            b.sing();
        }
}

多态的三个必要条件

  • ① 继承
  • ② 方法覆盖
  • ③ 父类型引用指向子类型对象

super关键字

概述

严格来说,super 其实并不是一个引用,它只是一个关键字,super 代表了当前对象中从父类继承过来的那部分特征

以下有一个例子,便于大家理解 super 和 this。(this 指向一个独立的对象,super 并不是指向某个 “ 独立 ” 的对象)

假设张大明是父亲,张小明是儿子。张小明的眼睛、鼻子和父亲的很像。那么也就是说儿子继承了父亲的眼睛和鼻子特征,那么眼睛和鼻子肯定最终还是长在儿子的身上。假设 this 指向张小明,那么 super 就代表张小明身上的眼睛和鼻子。换句话说 super 其实是 this 的一部分。

如下图所示:张大明和张小明其实是两个独立的对象,两个对象内存方面没有联系,super 只是代表张小明对象身上的眼睛和鼻子,因为这个是从父类中继承过来的,在内存方面使用了 super 关键字进行了标记,对于下图来说 “ this.眼睛 ” 和 “ super.眼睛 ” 都是访问的同一块内存空间。

super例子

  • super 和 this 都可以使用在实例方法当中
  • super 不能使用在静态方法当中,因为 super 代表了当前对象上的父类型特征,静态方法中没有 this,肯定也是不能使用 super 的
  • super 也有这种用法:“ super(实际参数列表) ; ”,这种用法是通过当前的构造方法调用父类的构造方法

super使用在构造方法

语法:

super(实际参数列表);

含义:子类构造方法执行过程中调用父类的构造方法。

实例:

public People(String idCard,String name,boolean sex){
    this.idCard = idCard;
    this.name = name;
    this.sex = sex;
}
public Student(String idCard,String name,boolean sex,int sno){
    super(idCard,name,sex);
    this.sno = sno;
}

文章作者: LogicVan
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 LogicVan !
评论
  目录