Java基础

文章介绍了Java语言的基础知识,包括JDK的安装过程,JDK目录结构,如何使用javac编译源文件和java运行程序,以及Java的运行机制和IDEA开发工具的使用。重点强调了Java的跨平台特性和程序的编译与执行流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

“肆虐的风吹得很大,大到我不敢说话,我坐在风里,叼着笔,拿着信,看见了你,飘散的思绪散落一地,没有回响,没有你。”

Java入门(第一章)

JDK安装

官方网址点我点我

 而后就会自动跳转到Oracle账户注册,注册即可,注册完毕登录账号就能发现JDK已经在自动下载

JDK目录介绍

打开所安装目录的文件夹即可看到如下:

1.bin文件夹:存放一些可执行程序,如javac.exe(Java编译器)、java.exe(Java运行工具)、jar.exe(打包工具)、javadoc.exe(文档生成工具)等。其中最重要的是javac.exe和java.exe。

javac.exe是Java编译器,它可以将编写好的Java源文件编译成Java字节码文件(可执行的Java程序)。Java源文件的扩展名为.java,如HelloWorld.java。编译后生成对应的Java字节码文件,字节码文件的扩展名为.class,如HelloWorld.class。

java.exe是Java运行工具,它会启动一个Java虚拟机(JVM)进程,Java虚拟机相当于一个虚拟的操作系统,专门负责运行由Java编译器生成的字节码文件。

2.conf文件夹:存放JDK的相关的配置文件,可配置Java访问权限和密码。

3.include文件夹:由于JDK是使用C语言和C++开发的,因此在启动时需要引入一些C语言和C++的头文件,该文件夹中就存放了这些文件。

4.jmods文件夹:存放调试文件。

5.legal文件夹:存放Java及各类模块的软件许可。

6.lib文件夹:lib急library,意为Java类库或库文件,是开发工具使用的归档包文件。

第一个Java程序

编写Java源文件

在bin目录下新建文本文档,重命名为Helloworld.java。用记事本打开Helloworld.java编写一段java程序:

注意:第一行class后即为类名,要与文件名一致。

打开命令行窗口

1.windows+r

2.输入cmd

3.回车

编译Java源文件 

在命令框中输入cd+JDK的bin文件夹路径,我是安装到了E盘,所以路径要做出相应改变:

先将路径换盘,而后再选择路径,输入编译命令:javac HelloWorld.java 

可以看到bin文件夹下已经生成HelloWorld.class字节码文件

运行Java程序

输入java HelloWorld命令,运行编译好的字节码文件

注意:

1.在使用javac命令编译Java源程序时,需要输入完成的文件名,例如:HelloWorld.java程序在编译的时候就要输入javac HelloWorld.java。

2.在使用java命令运行程序时,需要的是字节码文件名,而非完整的文件名,例如运行HelloWorld.class程序时,应该输入java HelloWorld,后面不可加".class",否则报错。

系统环境变量

PATH环境变量

添加java命令路径set path=%PATH%;bin路径

再次输入set path查看PATH环境变量值

再次执行javac

查看Windows系统属性中的环境变量

配置PATH系统环境变量

查看和验证配置的PATH系统环境变量

CLASSPATH环境变量

添加java命令路径set path=%PATH%;bin路径

执行java HelloWorld

Java程序的运行机制

Java运行时,必须经过编译和运行两个步骤。首先将扩展名改为.java的源文件进行编译,生成扩展名为.class的字节码文件,然后Java虚拟机对字节码文件进行解释执行,将结果显示出来。

1.编写HelloWorld.java文件。

2.使用java HelloWorld.java命令开启Java编译器,编译HelloWorld.java文件。编译结束后,编译器会自动生成名为HelloWorld.class的字节码文件。

3.使用java HelloWorld命令启动Java虚拟机运行程序,Java虚拟机首先将编译好的字节码文件加载到内存,这个过程被称为类加载,由类加载器完成,然后Java虚拟机针对加载到内存中的Java类进行解释执行,输出执行结果。

通过上面的分析不难发现,Java程序是由Java虚拟机而非操作系统负责执行的,这样做的好处是可以实现Java程序的跨平台特性。也就是说,在不同的操作系统上,可以运行相同的Java程序,不同的操作系统只需安装不同版本的Java虚拟机即可。

Java程序的跨平台特性有效地解决了程序在不同操作系统中编译时产生的不同的机器代码的问题,大大降低了程序开发和维护的成本。

IntelliJ IDEA开发工具

安装IDEA

官网下载点我

软件为收费软件,下载一次可免费使用30天,到期了重新下载安装就行。

启动IDEA 

原神!!启动!!不不不,应该启动桌面的IDEA

使用IDEA进行开发 

创建Java项目

好的,我们一进来就看见一个main函数,里面有着简单的for循环语句,点击运行如下所示:

创建Java类

编写程序代码 

运行结束 

章节小结 

一、填空题

1.Java是一种面向对象的语言,它是由Sun公司(已被Oracle公司收购)开发的高级程序设计语言。

2.Java语言的特点有简单面向对象安全性跨平台性支持多线程分布性

3.将.java源文件编译为.class文件的是javac命令。

4.Java语言的跨平台特点是由JVM保证的。

5.Java程序的运行环境简称为JRE

二、判断题

1.JRE中包含了Java基本类库、JVM和开发工具。

错。JDK提供的是开发工具,JRE提供的是运行环境。与JDK相比,JRE中只包含Java的运行工具有,不包含Java编译工具,JRE一般被封装到JDK中。

2.编译Java程序需要使用java命令。

错。编译Java程序使用的是javac命令。

3.IDEA开发工具Debug模式下不进入函数内部的单步调试快捷键是F7。

错。IDEA开发工具Debug模式下不进入函数内部的单步调试快捷键是F8,F7是进入函数内部的单步调试的快捷键。

4.JDK8以后可以不用配置CLASSPATH环境变量。

错。从JDK5开始,如果CLASSPATH环境变量没有设置,Java虚拟机会自动将CLASSPATH设置为'.',也就是当前文件夹。

5.Java语言有三种技术平台,分别是Java SE,Java ME,Java EE。

对。

三、选择题

1.Java属于(C)语言。

A.机器语言

B.汇编语言

C.高级语言

D.以上都不对

2.Java语言的特点有(ABCD)。(多选)

A.简单

B.面向对象

C.跨平台性

D.支持多线程

3.在JDK的bin文件夹下有许多.exe可执行文件,其中java.exe命令的作用是(D)。

A.Java文档制作工具

B.Java文档生成工具

C.Java运行工具

D.Java打包工具

java.exe为Java在windows下运行环境的封装工具

4.在以下选项中,(ABCD)属于JDK工具。(多选)

A.Java编译器

B.Java文档生成工具

C.Java运行工具

D.Java打包工具

5.下面4种类型的文件中(D)可以在Java虚拟机中运行。

A..java

B..jre

C..exe

D..class

四、简答题

1.简述Java语言的特点

简单、面向对象、安全性、跨平台性、支持多线程、分布性。

2.简述Java的运行机制

Java程序运行时,必须经过编译和运行两个步骤。首先将后缀名为.java的源文件进行编译,生成后缀名为.class的字节码文件,然后Java虚拟机将字节码文件进行解释执行,并将结果显示出来。

五、编程题

使用记事本写一个HelloWorld程序,在命令行窗口编译运行,并输出结果

public class HelloWorld
{
    public static void main(String[] args)
    {
        System.out.println("hello world!!! 原神启动!");
    }
}

Java编程基础(第二章)

Java基本语法

每种语言都有其特定的语法规则,学过C++的同学都能够发现,Java与C++及其相似。在本章中,将对比C++与Java的相同点与不同点,便于记忆。

Java程序的基本格式

在C++中,去写代码都是创建一个源代码,而后直接编译运行即可,对于类是非必须的,无论类是否存在,都不影响代码的编写与运行。但对于Java来说,类是必须的,也就是说,写代码的话,要创建的并非是源程序,而是一个类,类的定义如下:

修饰符 class 类名
{
    程序代码
}

编写代码时,要注意:

1.Java程序代码分为结构定义语句和功能执行语句,其中结构定义语句用于声明一个类或方法,功能执行语句用于实现具体的功能,每条语句末尾都要以;结束,例如第一章中我们写的第一个Java程序:

System.out.println("hello world!!!原神启动!!");

2.Java严格区分大小写,定义类的时候只能用class,不可使用Class。

3.Java对于代码风格没有严格的限制,但从规范性来说,常用的编排方式是一行只写一条语句,符号'{'与语句同行,符号'}'独占一行,例如:

public class HelloWorld{
    public static void main(String[] args){
        System.out.println("hello world!!! 原神启动!");
    }
}

当然,也可以用强迫症的方式,如下:

public class HelloWorld
{
    public static void main(String[] args)
    {
        System.out.println("hello world!!! 原神启动!");
    }
}

二者均可,喜欢哪种用哪种。

4.Java程序种的一个连续的字符串不能分两行书写。

System.out.println("hello world!!!
                            原神启动!!");

可以使用'+'来链接不在一行的字符串

System.out.println("hello world!!!" + 
                            "原神启动!!");

Java中的注释 

在Java的单行注释、多行注释和允许嵌套使用注释的情况,与C++基本一致,较为特殊的注释方式有文档注释:

文档注释是以/**开头,在注释末尾以*/结束。

文档注释是对一段代码概括性的解释说明,可以使用javadoc命令将文档注释提取出来,生成帮助文档。文档注释具体示例如下:

/**
    *@author 计科土狗
    *@version 4.16
    */

javadoc提供了一些标签用于文档注释,其中常用的标签如下表所示:

标签描述
@author标识作者
@deprecated标识过期的类或成员
@exception标识抛出的异常
@param标识方法的参数
@return 标识方法的返回值
@see标识指定参数的内容
@serial标识序列化属性
@version标识版本
@throws标识引入一个特定的变化

Java中的标识符

命名规则与C++基本一致,其中对于Java的标识符定义有一些约定俗成的规则:

1.包名中的所有字母一律小写,例如:zhuyiin.csdn.text。

2.类名和接口名中的每个单词的首字母都大写,例如:ArrayList、Iterator。

3.常量名的所有字母都大写,字母之间用下划线链接,例如:Mincraft_TNT_WHEAT。

4.变量名和方法名的第一个单词的字母小写,从第二个单词开始每个单词的首字母大写,例如:lineNumber、getLineNumber。

5.在程序中,应尽量使用有意义的英文单词定义标识符,便于阅读程序,例如:userName定义用户名,password定义密码。

Java中的关键字

abstract、assert、boolean、break、byte、case、catch、char、class、continue、default、do、double、else、enum、extends、final、finally、float、for、if、implements、import、int、interface、instanceof、long、native、new、package、private、protected、public、return、short、static、strictfp、super、switch、synchronized、this、throw、throws、transient、try、void、volatile、while。

关键字的使用需要注意:

1.所有的关键字都是小写。

2.不能使用关键字命名标识符。

3.const和goto是保留的关键字,虽然在Java中并未任何实际意义,但是在程序中不能够作为自定义的标识符。

4.true、false和null虽然都不属于关键字,但它们有特殊的含义,也不能作为标识符使用。

Java中的常量

整型常量

二进制:前面以0b/0B开头,后面由0~1组成,如0b01101100。

八进制:前面以0开头,后面由0~7组成,如0342。

十进制:由数字0~9组成的数字序列,如5201314。

十六进制:前面以0x/0X开头,后面由0~9和A~F组成,如0X7F。

浮点数常量

单精度浮点数以f/F结尾,如2e3f。

双精度浮点数以d/D结尾,如3.84d。

在使用浮点数时也可以不加后缀,系统默认为double类型。

字符常量

与C++类似。特殊在于使用'\u0000'表示空字符。

字符串常量

与C++类似。

布尔常量

与C++类似。

null常量

与C++类似。

Java中的变量

变量的定义

在程序的运行期间,随时可能产生一些临时数据,应用程序会将这些数据保存在内存单元中,每个内存单元都用一个标识符表示,这些用于表示内存单元的标识符就称为变量,内存单元中存储的数据就是变量的值。

数据处理是程序的基本功能,变量是程序中数据的载体,因此变量在程序中占据着重要的地位。

变量的数据类型

Java在定义变量是必须声明变量的类型,在为变量赋值时必须赋予和变量同一种类型的值,否则程序会报错。

在Java中,变量的数据类型分为两种:基本数据类型和引用数据类型。

基本数据类型是Java内嵌的,在任何操作系统中都是具有相同大小和属性,而引用数据类型是在Java程序中由编程人员自己定义的类型。

变量的类型转换

与C++类似。

变量的作用域

与C++类似。

Java中的运算符

与C++类似。

选择结构语句

与C++类似。

循环结构语句

与C++类似。

方法

什么是方法

等同于为C++中能够被反复调用的函数。方法就是一段可以重复调用的代码。

修饰符 返回值类型 方法名(参数类型 参数名1,参数类型 参数名 2,...)
{
    执行语句
    return 返回值;
}

对于方法的语法格式,具体说明如下:

  • 修饰符:方法的修饰符比较多,修饰符在后面的学习过程中将会逐步介绍。
  • 返回值类型:用于限定方法返回值的数据类型。
  • 参数类型:用于限定调用方法时传入参数的数据类型。
  • 参数名:是一个变量,用于接收调用方法是传入的数据。
  • return关键字:用于返回方法指定类型的值并结束方法。
  • 返回值:被return语句返回的值,该值会返回给调用者。

需要注意的是,方法中的“参数类型 参数名1,参数类型 参数名2,...”称作参数列表,参数列表用于描述方法在被调用时需要接收的参数,如果方法不需要接收任何参数,则参数列表为空,即()内不写任何内容。方法的返回值类型必须是方法声明的返回值类型,如果方法没有返回值,返回值类型要声明为void,此时,方法中的return语句可以省略。

方法的重载

类似于C++中的函数重载。所谓方法重载,就是在同一个作用域内,方法名相同但参数个数或者参数类型不同的方法。

public class AddFunction {
    public static void main(String[] args){
        int sum1 = add(1,2);
        int sum2 = add(1,2,3);
        double sum3 = add(1.2,2.3);
        System.out.print(sum1 + " " + sum2 + " " + sum3);
    }
    public static int add(int a,int b){
        return a + b;
    }
    public static int add(int a,int b,int c){
        return a + b + c;
    }
    public static double add(double a,double b){
        return a + b;
    }
}

数组

数组的基本要素

一个数组由四个基本元素构成:数组名称、数组元素、元素索引、数据类型。

在Java中,声明数组的方式如下:

数组类型[] 数组名;
数组名 = new 数组类型[长度];

声明一个数组,代码如下:

int[] x;
x = new int[100];

 上述代码就相当于在内存中定义了100个int类型的变量,第1个变量的名称为x[0],第2个变量的名称为x[1]...依次类推,第100个变量的名称为x[99],这些变量的初始值都是0。

第一行声明了变量x,该变量的类型为int[],即声明了一个int类型的数组。变量x会占用一块内存单元,它的初始值为0。

第二行代码创建了一个数组,将数组的地址赋给变量x,在程序运行期间可以使用变量x引用数组,这时变量x在内存中的状态会发生变化。

需要注意的是,数组中最小的索引是0,最大的索引是数组的长度减1,为了方便获得数组的长度,Java提供可一个length属性,在程序中可以通过“数组名length”的方式获得数组的长度,即元素的个数。

数组的简单使用

简单的数组定义和数组访问样例:

public class ShuZuUsings {
    public static void main(String[] args){
        int[] arr;
        arr = new int[3];
        System.out.println("arr[0] = " + arr[0]);
        System.out.println("arr[1] = " + arr[1]);
        System.out.println("arr[2] = " + arr[2]);
    }
}

如果不想使用初始默认值,也可显式地为这些元素赋值:

public class ShuZuUsings {
    public static void main(String[] args){
        int[] arr;
        arr = new int[3];
        arr[0] = 1;
        arr[1] = 2;
        arr[2] = 3;
        System.out.println("arr[0] = " + arr[0]);
        System.out.println("arr[1] = " + arr[1]);
        System.out.println("arr[2] = " + arr[2]);
    }
}

 在定义数组时只指定数组的长度,由系统自动为元素赋初值的方式称为动态初始化。在初始化数组时还有一种方式称为静态初始化,就是在定义数组的同时为数组的每个元素赋值。数组的静态初始化有以下两种方式:

类型 数组名 = new 类型[] {元素,元素,...}
类型 数组名 = {元素,元素,...}

以上两种方式都可以实现数组的静态初始化,为了简便,建议采取第二种方式,下面是一个静态初始化的案例:

public class ShuZuUsings {
    public static void main(String[] args){
        int[] arr = {0,1,2};
        System.out.println("arr[0] = " + arr[0]);
        System.out.println("arr[1] = " + arr[1]);
        System.out.println("arr[2] = " + arr[2]);
    }
}

数组的常见操作

数组的遍历

在操作数组时,经常需要依次访问数组中的每个元素,这种操作称作数组的遍历。由于数组中的元素较多,所以常用循环语句完成数组的遍历,在循环遍历数组时,使用数组索引作为循环条件,只要索引没有越界,就可以访问数组元素。

public class Main {
    public static void main(String[] args) {
        int[] arr = {0,1,2,3,4,5,6};
        for(int i = 0;i < arr.length;i ++ ){
            System.out.println("arr[" + i + "] = " + arr[i]);
        }
    }
}
数组中最值的获取

在操作数组时,经常需要获取数组中元素的最值,例如在一个数组中寻找最大值和最小值:

public class Main {
    public static void main(String[] args) {
        int maxNum = -0x3f3f3f3f, minNum = 0x3f3f3f3f;
        int[] arr = {5, 2, 0, 1, 3, 1, 4};
        for (int i = 0; i < arr.length; i++) {
            if (maxNum < arr[i])maxNum = arr[i];
            if (minNum > arr[i])minNum = arr[i];
        }
        System.out.println("数组的最小值为:" + minNum);
        System.out.println("数组的最大值为:" + maxNum);
    }
}
在数组的指定位置插入一个数据

例如在现有数组int[] arr = {10,11,13,14,15},要求将12插入到索引为2的位置:

public class Main {
    public static void main(String[] args) {
        int[] arr = {10,11,13,14,15};
        int[] temp = new int[arr.length + 1];
        int flag = 0;
        for(int i = 0;i < arr.length;i ++ )
        {
            if(i == 2 && flag == 0)
            {
                temp[i] = 12;
                temp[i + 1] = arr[i];
                flag = 1;
            }
            if(flag == 0)temp[i] = arr[i];
            if(flag == 1)temp[i + 1] = arr[i];
        }
        for(int i = 0;i < temp.length;i ++ )
        {
            System.out.print(temp[i] + " ");
        }
    }
}
数组排序
public class Main {
    public static void main(String[] args) {
        int[] a = {25,24,12,76,101,96,28};
        for(int i = 1;i < a.length - 1;i ++ ) {
            for (int j = 0; j < a.length - i; j++) {
                if (a[j] > a[j + 1]) {
                    int t = a[j];
                    a[j] = a[j + 1];
                    a[j + 1] = t;
                }
            }
        }
        for(int i = 1;i < a.length - 1;i ++ )System.out.print(a[i] + " ");
    }
}

二维数组

二维数组的定义有很多方式

数据类型 [][] 数组名 = new 数据类型[行数][列数];
数据类型 [][] 数组名 = new 数据类型[行数][];
数据类型 [][] 数组名 = {{第1行初始值},{第2行初始值},...,{第n行初始值}};

 下面给出打印九九乘法表的例子:

public class Main {
    public static void main(String[] args) {
        int[][] a = new int[10][10];
        for(int i = 1;i <= 9;i ++ )
        {
            for(int j = 1;j <= i;j ++ )
            {
                a[i][j] = i * j;
                System.out.print(i + " * " + j + " = " + a[i][j] + " ");
            }
            System.out.println();
        }
    }
}

章节小结

一、填空题

1.Java程序代码必须放在一个类中,类使用class关键词定义。

2.Java中的注释有三类,分别是单行注释、多行注释和文档注释。

3.在Java中,int类型所占存储空间为4字节。

4.用于比较两个整数是否相等的运算符是==。

5.数组是一个容器。存储到数组中的每个元素都有自己的自动编号,最小值为0。

二、判断题

1.二进制是由数字0和1组成的数字序列。

对。

2.continue语句只用于循环中,它的作用是跳出循环。

3.三元运算符的语法格式为“判断条件?表达式1:表达式2”。

对。

4.在switch语句中,每个case关键字后面必须有break。

错。

5.若x=5,则表达式(x + 5) / 3的值是3。

对。

三、选择题

1.下列类的定义格式中正确的是

A.

修饰符 class类名{
    程序代码
}

C. 

class类名{
    程序代码
}

2.下列选项中,不属于基本数据类型的是

A.string

3.下列选项中,比较运算符使用正确的选项是

B.4 == 3结果为false

D.4 >= 3结果为true

4.阅读代码,上述程序运行结束,输出结果为:

C.false true true false

5.假设int x = 2,三元表达式x > 0 ? x + 1 : 5的结果为

C.3

四、简答题

1.简述Java中的8种基本数据类型,并说明每种数据类型所占的存储空间的大小

数据类型所占存储空间大小
byte1
boolean1
char2
short2
int 4
float4
double 8
long 8

2.简述跳转语句continue和break的作用和区别。

在switch条件语句和循环语句中都可以使用break语句。当它出现在switch条件语句中时,其作用时终止某个case并跳出switch结构。当它出现在循环语句中,作用是跳出循环语句,执行循环后面的代码:continue语句用在循环语句中,它的作用是终止本次循环,执行下一次循环。

五、编程题

1.编写程序,计算1+2+3+...+99的值,要求如下:

1)使用循环语句实现对1~99的遍历

2)在遍历过程中,通过条件判断当前的数是否为奇数,如果就累加,否则不加。

public class Main {
    public static void main(String[] args) {
        int ans = 0;
        for(int i = 1;i <= 99;i ++ )
        {
            if(i % 2 != 0)ans += i;
        }
        System.out.print(ans);
    }
}

2.使用do...while循环语句计算整数5的阶乘。

public class Main {
    public static void main(String[] args) {
        int ans = 1,cnt = 5;
        do
        {
            ans *= cnt;
            cnt -- ;
        }while(cnt != 0);
        System.out.print(ans);
    }
}

“董卿写过一段话:无论我们最后生疏成什么样子,曾经对你的好都是真的,就算终有一散,也别辜负相遇,希望你不后悔认识我,也是真的快乐过,如果能回到以前,我会选择不认识你。不是我后悔,是我不能面对现在的结局。”

面向对象(上)(第三章)

面向对象的思想

面向对象的特征分别有:封装性、继承性和多态性。

封装性

封装是面向对象的核心思想,它有两层含义:一是把对象的属性和行为看成一个密不可分的整体,将这二者封装在对象当中。二是指信息隐藏,将不想让外界知道的信息隐藏起来。

继承性

继承性主要描述的是类与类之间的关系,通过继承,可以在原有类的功能上进行扩展。继承不仅提高了代码的复用性,提高了开发效率,还降低了程序发生错误的可能性,为程序的维护以及扩展提供了便利。

多态性

多态性指一个类中定义的属性和方法被其他类继承后,它们可以具有不同的数据类型或者表现出不同的行为,使得同一个属性和方法在不同的类中有着不同的语义。

类与对象

在Java程序中,类和对象是最基本、最重要的单元。类标识某类群体一些基本特征抽象,对象表示一个个具体的事物。

类的定义

在面向对象的思想中最核心的就是对象,创建对象的前提是定义一个类。类是Java中一个重要的引用类型,也是组成Java程序的基本要素,所有的Java程序都是基于类的。

类是对对象的抽象,用于描述一组对象的共同特征和行为。类中可以定义成员变量和成员方法。成员变量用于描述对象的特征,成员变量也被称作对象的属性;成员方法用于描述对象的行为,可简称为方法。

类的定义格式如下:

class 类名
{
    成员变量;
    成员方法;
}

局部变量与成员变量的不同 

在Java中,定义在类中的变量称为成员变量,定义在方法中的变量称为局部变量。如在某个方法中定义的局部变量与成员变量同名,这种情况是允许的。此时,在方法中通过变量名访问的是局部变量,而非成员变量。

对象的创建与使用

要想使用一个类,则必须创建该类的对象。在Java中使用new关键字创建对象,使用new关键字创建的对象具体格式如下:

类名 对象名 = null;
对象名 = new 类名();

在上述格式中,创建对象分为声明对象和实例化对象。当然也可直接通过以下方式实现:

类名 对象名 = new  类名();

 例如,创建一个Student类如下所示:

Student stu = new Student();

new Student()用于创建Student类的一个示例对象,Student stu声明了一个Student类的变量stu。运算符=将新创建的实例对象地址赋值给变量stu,变量stu引用的对象简称为stu对象。 

public class StudentTest {
    public static void main(String[] args) {
        Student stu = new Student();
    }
}
class Student {
    String name;

    void read() {
        System.out.println(name + "最喜欢玩原神了!");
    }
}

上述代码在main函数中实例化了Student对象,对象名为stu。使用new关键字创建的对象在堆内存中分配空间。对象名stu保存在栈内存中,对象的属性信息则保存在对应的堆内存中。创建对象后,可以使用对象访问类中的某个属性或者方法,对象属性和方法的访问通过运算符.实现,格式如下所示:

对象名.属性名
对象名.方法名
public class StudentTest {
    public static void main(String[] args) {
        Student stu1 = new Student();
        stu1.name = "计科土狗";
        stu1.read();
        Student stu2 = new Student();
        stu2.name = "睡觉大王";
        stu2.read();
    }
}
class Student {
    String name;
    void read() {
        System.out.println(name + "最喜欢玩原神了!");
    }
}

 运行结果如下所示:

可以看出,stu1与stu2对象在调用read()方法时,打印的name值并不相同。这是因为stu1对象和stu2对象在系统内存中是两个完全独立的个体,它们分别拥有各自name属性,对stu1对象的name属性进行赋值并不会影响stu2对象的name属性值。

由此可知,程序分别实例化了两个Student对象stu1和stu2,它们分别指向各自的堆内存空间。

对象的引用传递

类属于引用数据类型,引用数据类型的内存空间可以和同时被多个栈内存引用。

public class StudentTest {
    public static void main(String[] args) {
        Student stu1 = new Student();
        Student stu2 = null;
        stu2 = stu1;
        stu1.name = "睡觉大王";
        stu1.age = 20;
        stu2.age = 50;
        stu1.read();
        stu2.read();
    }
}
class Student {
    String name;
    int age;
    void read() {
        System.out.println("我叫" + name + "最喜欢库洛米和玉桂狗!");
        System.out.println("我今年已经" + age + "岁啦!!!");
    }
}

在上述代码中,利用Student类创建了两个对象stu1和stu2,代码仅对于stu1进行实例化,并未对stu2进行实例化,代码运行结果如下:

由运行结果可知,stu1与stu2的输出内容是一致的,这是因为stu2对象获得了stu1对象的堆内存空间的使用权,实现过程让我们回溯一下:

  • 首先stu1与stu2被声明,而后使用new创建对象stu1,系统为其开辟堆内存空间。
  • 对象stu1指向开辟的堆内存地址0x001。
  • 通过对象stu1为对象stu2分配内存空间使用权,对象stu2指向堆内存地址0x001。
  • 0x001中的age先是被修改为20,而后又被修改为50。

一个栈内存空间只能指向一个堆内存空间,如果想要再指向其他堆内存空间,就必须断开已有的指向,才能分配新的指向。

访问控制权限

在Java中,针对类、成员方法和属性,Java提供了四种访问控制属性,分别是private、default、protected、public(按访问权限级别的高低依次给出)。

  • private:私有访问权限。用于修饰类的属性和方法,也可以用来修饰内部类。被private修饰的类的成员只能在本类中访问。
  • default:默认访问权限。如果一个类型中的属性和方法没有任何访问权限声明,则该属性或方法就是默认访问权限,可以被本包中的其他类访问,但是不能被其他包类访问。
  • protected:受保护的访问权限。如果一个类中的成员被protected关键字修饰,则只能被本包级不同包的子类访问。
  • public:公共访问类型,如果一个类的成员被public修饰,则该成员可以在所有类中被访问,不管是否在同一个包中。
访问范围privatedefaultprotectedpublic

同一个类

同一个包中的类
不同包的子类
全局范围

需要注意的是,外部类的访问权限只能是public或default,所以Test类只能使用public修饰或者不写修饰符。局部成员是没有访问控制权限的,因为局部成员只在其所在的作用域内起作用,不可能被其他类访问,如果在程序中对局部成员使用访问控制权限修饰符,编译器会报错。

如果一个源文件定义的所有类都没有使用public修饰,那么这个源文件的文件名可以是一切合法的文件名;如果一个源文件中定义了一个使用public修饰的类,那么这个文件的文件名必须与public修饰的类名相同。

封装性

为什么要封装

在Java面向对象的思想,封装是指将类的实现细节包装、隐藏起来的方法。封装性可以被认为是一道保护屏障,防止本类的代码和数据被外部类定义的代码随机访问。

public class StudentTest {
    public static void main(String[] args) {
        Student stu = new Student();
        stu.name = "睡觉大王";
        stu.age = -18;
        stu.read();
    }
}
class Student {
    String name;
    int age;
    void read() {
        System.out.println("我叫" + name + "最喜欢库洛米和玉桂狗!");
        System.out.println("我今年已经" + age + "岁啦!!!");
    }
}

在上述代码中,发现age被设置为-18,显然年龄为负数不是一个正常的值,这就需要在设计Student类时,对成员变量的访问进行一些限定,不允许外界随意访问,这就需要实现类的封装。 

如何实现封装

类的封装是指将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象的内部信息,而是通过该类提供的方法实现对内部信息的访问。

封装实现的具体过程是:在定义一个类时,将类中的属性私有化,即使用private关键字修饰类的属性。私有属性只能在它所在的类中被访问。如果外界想要访问私有属性,需要提供一些使用public修饰的公有方法,其中包括用于获取属性值的getXxx方法和设置属性值的setXxx方法。

public class StudentTest {
    public static void main(String[] args) {
        Student stu = new Student();
        stu.setName("睡觉大王");
        stu.setAge(-18);
        stu.setAge(18);
        stu.read();
    }
}
class Student {
    private String name;
    private int age;
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
    public int getAge(){
        return age;
    }
    public void setAge(int age){
        if(age < 0)System.out.println("输入年龄有误,请重新输入!");
        else this.age = age;
    }
    public void read() {
        System.out.println("我叫" + name + "最喜欢库洛米和玉桂狗!");
        System.out.println("我今年已经" + age + "岁啦!!!");
    }
}

运行结果如下:

 

构造方法

从前面所学的知识可以发现,实例化一个对象后,如果要为这个对象中的属性赋值,则必须直接访问对象的属性或者调用setXxx方法。如果需要在实例化对象时为这个对象的属性赋值,可以通过构造方法实现。构造方法是类的一个特殊成员方法,在类实例化对象时可以自动调用。

定义构造方法

构造方法的特殊要求:

  • 构造方法的名称必须与类名一致
  • 构造方法名称前不能由任何返回值类型的声明
  • 不能在构造方法中使用return返回一个值,但可以单独写return语句作为方法的结束
public class StudentTest {
    public static void main(String[] args) {
        System.out.println("声明对象");
        Student stu = null;
        System.out.println("实例化对象");
        stu = new Student();
    }
}
class Student {
    private String name;
    private int age;
    public Student(){
        System.out.println("调用了无参的构造方法");
    }
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
    public int getAge(){
        return age;
    }
    public void setAge(int age){
        if(age < 0)System.out.println("输入年龄有误,请重新输入!");
        else this.age = age;
    }
    public void read() {
        System.out.println("我叫" + name + "最喜欢库洛米和玉桂狗!");
        System.out.println("我今年已经" + age + "岁啦!!!");
    }
}

结果如下所示:

在一个类中,除了无参构造方法外,还可以定义有参的构造方法,通过有参构造方法可以实现对属性的赋值,如下所示:

public class StudentTest {
    public static void main(String[] args) {
        Student stu = null;
        stu = new Student("睡觉大王",18);
        stu.read();
    }
}
class Student {
    private String name;
    private int age;
    public Student(String name,int age){
        this.name = name;
        this.age = age;
    }
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
    public int getAge(){
        return age;
    }
    public void setAge(int age){
        if(age < 0)System.out.println("输入年龄有误,请重新输入!");
        else this.age = age;
    }
    public void read() {
        System.out.println("我叫" + name + "最喜欢库洛米和玉桂狗!");
        System.out.println("我今年已经" + age + "岁啦!!!");
    }
}

 结果如下所示:

构造方法的重载

和普通方法一样,构造方法也可以重载,在一个类中可以定义多个构造方法,但是每个构造方法的参数类型或参数个数不同。在创建对象时,可以通过调用不同的构造方法,为不同的属性赋值。

public class StudentTest {
    public static void main(String[] args) {
        Student stu1 = new Student("睡觉大王");
        Student stu2 = new Student("睡觉大王",18);
    }
}
class Student {
    private String name;
    private int age;
    //一个参数的构造方法
    public Student(String name){
        this.name = name;
        System.out.println("调用了一个参数的构造方法");
    }
    //两个参数的构造方法
    public Student(String name,int age){
        this.name = name;
        this.age = age;
        System.out.println("调用了两个参数的构造方法");
    }
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
    public int getAge(){
        return age;
    }
    public void setAge(int age){
        if(age < 0)System.out.println("输入年龄有误,请重新输入!");
        else this.age = age;
    }
    public void read() {
        System.out.println("我叫" + name + "最喜欢库洛米和玉桂狗!");
        System.out.println("我今年已经" + age + "岁啦!!!");
    }
}

运行结果如下:

在Java中的每个类都至少有一个构造方法。如果在一个类中没有定义构造方法,系统会自动为这个类创建一个默认的构造方法,这个默认的构造方法没有参数,方法中没有任何代码,所以Java中默认的构造方法在程序运行时什么也不做。

由于系统提供的默认构造方法往往不能满足要求,因此,通常需要程序员在类中自己定义构造方法,一旦为类定义了构造方法,系统就不再提供默认的构造方法了。为避免错误,如果在类中定义了一个有参的构造方法,最好再定义一个无参的构造方法。

需要注意的是,构造方法通常用public进行修饰。

this关键字

在Java开发中,当成员变量与局部变量发生重名问题时,需要使用this关键字分辨成员变量与局部变量。Java中的this关键字语法比较灵活,其主要作用有以下3个:

  • 使用this关键字调用本类中的属性
  • 使用this关键字调用成员方法
  • 使用this关键字调用构造方法

使用this关键字调用本类中的属性

成员变量与局部变量age和name利用this避免歧义的示范: 

public class StudentTest {
    public static void main(String[] args) {
        Student stu = new Student("睡觉大王",18);
        stu.read();
    }
}
class Student {
    private String name;
    private int age;
    public Student(String name,int age){
        this.name = name;
        this.age = age;
    }
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
    public int getAge(){
        return age;
    }
    public void setAge(int age){
        if(age < 0)System.out.println("输入年龄有误,请重新输入!");
        else this.age = age;
    }
    public void read() {
        System.out.println("我叫" + name + "最喜欢库洛米和玉桂狗!");
        System.out.println("我今年已经" + age + "岁啦!!!");
    }
}

使用this关键字调用成员方法

 利用this关键字调用成员方法:

class Student {
    private String name;
    private int age;
    public Student(String name,int age){
        this.name = name;
        this.age = age;
    }
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
    public int getAge(){
        return age;
    }
    public void setAge(int age){
        if(age < 0)System.out.println("输入年龄有误,请重新输入!");
        else this.age = age;
    }
    public void speak(){
        this.read();
    }
    public void read() {
        System.out.println("我叫" + name + "最喜欢库洛米和玉桂狗!");
        System.out.println("我今年已经" + age + "岁啦!!!");
    }
}

在speak函数中,使用this关键字调用了read方法,此处的this可以省略。

使用this关键字调用构造方法

构造方法在实例化对象时被Java虚拟机自动调用,在程序中不能像调用其他成员方法一样调用构造方法,但可以在一个构造方法中使用this(参数1,参数2,...)的形式调用其他的构造方法。

public class StudentTest {
    public static void main(String[] args) {
        Student stu = new Student("睡觉大王",18);
        stu.read();
    }
}
class Student {
    private String name;
    private int age;
    public Student(){
        System.out.println("调用了无参的构造方法");
    }
    public Student(String name,int age){
        this();
        this.name = name;
        this.age = age;
    }
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
    public int getAge(){
        return age;
    }
    public void setAge(int age){
        if(age < 0)System.out.println("输入年龄有误,请重新输入!");
        else this.age = age;
    }
    public void read() {
        System.out.println("我叫" + name + "最喜欢库洛米和玉桂狗!");
        System.out.println("我今年已经" + age + "岁啦!!!");
    }
}

运行结果如下:

使用this调用类的构造方法时,应该注意以下三点:

  • 只能在构造方法中使用this调用其他的构造方法,不能在成员方法中通过this调用构造方法。
  • 在构造方法中,使用this调用其他构造方法的语句必须位于第一行,且只能出现一次。
  • 不能在一个类的两个构造方法中使用this互相调用。

代码块

代码块,简单来讲就是用{}括起来的一段代码。根据位置及声明关键字的不同,代码块可以分为四种:普通代码块、构造块、静态代码块、同步代码块。本节主要对于普通代码块和构造代码块进行说明,其余概念会在后续章节中提及。

普通代码块

普通代码块就是直接在方法或语句中定义的代码块:

public class StudentTest {
    public static void main(String[] args) {
        {
            int age = 18;
            System.out.print("这是普通代码块");
        }
        Student stu = new Student("睡觉大王",18);
        stu.read();
    }
}

在上述代码中,每一对"{}"括起来的代码都称为一个代码块,StudentTest又是一个大的代码块,在StudentTest中包含了main()方法代码块,在main()方法中又定义了一个局部代码块,局部代码对main()方法进行了"分隔",起到了限定作用域的作用。

上述代码的局部代码块中定义了变量age,main()方法代码块中也定义了变量age,但由于两个变量处在不同的代码块中,作用域不同,因此并不相互影响。

构造块

构造块是直接在类中定义的代码块。

public class StudentTest {
    public static void main(String[] args) {
        Student stu = new Student("睡觉大王",18);
        stu.read();
    }
}
class Student {
    private String name;
    private int age;
    {
        System.out.println("我是构造块");
    }
    public Student(String name,int age){
        this.name = name;
        this.age = age;
        System.out.println("我是构造方法");
    }
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
    public int getAge(){
        return age;
    }
    public void setAge(int age){
        if(age < 0)System.out.println("输入年龄有误,请重新输入!");
        else this.age = age;
    }
    public void read() {
        System.out.println("我叫" + name + "最喜欢库洛米和玉桂狗!");
        System.out.println("我今年已经" + age + "岁啦!!!");
    }
}

运行结果如下所示:

由运行结果,得出以下结论:

  • 在实例化Student类对象stu时,构造块先于构造方法执行。
  • 每当实例化一个Student类对象时,都会在执行构造方法之前执行构造块。

static关键字

在Java中,定义了一个static关键字,用于修饰类的成员,被static修饰的成员具备一些特殊性。

静态属性

如果在Java程序中使用static修饰属性,则该属性称为静态属性(也称全局属性)。静态属性可以使用类名直接访问,格式如下:

类名.属性名

学习静态属性之前先看一个样例:

public class StudentTest {
    public static void main(String[] args) {
        Student stu1 = new Student("睡觉大王",18);
        Student stu2 = new Student("库洛米",19);
        Student stu3 = new Student("玉桂狗",20);
        stu1.display();
        stu2.display();
        stu3.display();
        stu1.school = "杭州电子科技大学";
        System.out.println("修改stu1的大学为杭州电子科技大学后");
        stu1.display();
        stu2.display();
        stu3.display();
    }
}
class Student {
    String name;
    int age;
    String school = "沙窝里皇家学院";
    public Student(String name,int age){
        this.name = name;
        this.age = age;
    }
    public void display() {
        System.out.println("我叫" + name);
        System.out.println("今年已经" + age + "岁");
        System.out.println("我来自" + school);
    }
}

运行结果如下所示:

睡觉大王的学校信息由沙窝里皇家学院修改为了杭州电子科技大学之后,玉桂狗和库洛米的则没有修改,表明非静态属性是对象所有的,改变当前属性的值,不会影响其他对象的属性值。假设沙窝里皇家学院改名为了河南工业软件大学,此时Student类有10w个学生对象,如果要修改这些学生的学校信息,则需要改10w次,非常麻烦。

为解决上述问题,可以使用static关键字修饰school的值,将其变为公共属性。这样,school属性只被分配了一块内存空间,被Student类的所有对象共享,只要某个对象进行了一次修改,全部学生的school属性都会发生变化。

public class StudentTest {
    public static void main(String[] args) {
        Student stu1 = new Student("睡觉大王",18);
        Student stu2 = new Student("库洛米",19);
        Student stu3 = new Student("玉桂狗",20);
        stu1.display();
        stu2.display();
        stu3.display();
        stu1.school = "杭州电子科技大学";
        System.out.println("修改stu1的大学为杭州电子科技大学后");
        stu1.display();
        stu2.display();
        stu3.display();
    }
}
class Student {
    String name;
    int age;
    static String school = "沙窝里皇家学院";
    public Student(String name,int age){
        this.name = name;
        this.age = age;
    }
    public void display() {
        System.out.println("我叫" + name);
        System.out.println("今年已经" + age + "岁");
        System.out.println("我来自" + school);
    }
}

运行结果如下所示:

 

可以发现,虽然只修改了睡觉大王的学生信息,库洛米和玉桂狗的信息也随之改变,说明使用static声明的属性是所有对象共享的。

static不能修饰局部变量

static关键字只能修饰成员变量,不能修饰局部变量,否则编译器会报错。

静态方法

如果想要使用类中的成员方法,就需要先将这个类实例化。而在进行开发时,开发人员有时候希望在不创建对象的情况下,通过类名就可以直接调用某个方法,这时就需要使用静态方法,要实现静态方法,只需要在成员方法前加上static关键字。

同静态变量一样,静态方法也可以通过类名和对象访问,访问格式如下:

类名.方法

或者

实例对象名.方法

下面通过一个示例来说明:

public class StudentTest {
    public static void main(String[] args) {
        Student stu1 = new Student("睡觉大王",18);
        Student stu2 = new Student("库洛米",19);
        Student stu3 = new Student("玉桂狗",20);
        stu1.display();
        stu2.display();
        stu3.display();
        Student.setSchool("杭州电子科技大学");;
        System.out.println("修改stu1的大学为杭州电子科技大学后");
        stu1.display();
        stu2.display();
        stu3.display();
    }
}
class Student {
    private String name;
    private int age;
    private static String school = "沙窝里皇家学院";
    public Student(String name,int age){
        this.name = name;
        this.age = age;
    }
    public static String getSchool()
    {
        return school;
    }
    public static void setSchool(String s)
    {
        school = s;
    }
    public void display() {
        System.out.println("我叫" + name);
        System.out.println("今年已经" + age + "岁");
        System.out.println("我来自" + school);
    }
}

运行结果如下:

Student类中的属性都使用private关键字进行了封装。想要更改属性值就要使用setSchool方法,由于school属性使用关键字static继续宁修饰的,所以可以直接使用类名调用school的setSchool方法。

静态方法只能访问静态成员,非静态成员需要先创建对象才能访问,即随着对象的创建,非静态成员才会分配内存,而静态方法在被调用时可以不创建任何对象。

静态代码块

在Java类中,用static关键字修饰的代码块称为静态代码块。当类被加载时,静态代码块就会执行,由于类只加载一次,所以静态代码块只执行一次。在程序中,通常使用静态代码块对类的成员变量进行初始化。

下面通过案例学习:

public class StudentTest {
    public static void main(String[] args) {
        Student stu1 = new Student("睡觉大王",18);
        Student stu2 = new Student("库洛米",19);
        Student stu3 = new Student("玉桂狗",20);

    }
}
class Student {
    private String name;
    {
        System.out.println("我是构造块代码");
    }
    static{
        System.out.println("我是静态块代码");
    }
    public Student(String name,int age){
        System.out.println("我是Student类的构造方法");
    }
}

运行结果如下所示:

代码块执行的顺序为静态代码块->构造代码块->构造方法。static修饰的代码块会随着class文件一同加载,属于优先级最高的代码块。

章节小结

一、填空题

1.面向对象的三大特征是封装、多态、继承。

2.针对类、成员方法和属性,Java提供了4中访问控制权限,分别是private、default、protected、public。

3.静态方法必须使用static关键字来修饰。

4.类的封装是指在定义一个类时将类中的属性私有化,即使用private关键字来修饰。

5.在Java中解决成员变量与局部变量名称冲突时,可以使用this关键字。

二、判断题

1.在成员方法中出现的this关键字代表的时调用这个方法的对象。(×)

2.封装就是隐藏对象的属性和实现细节,仅对外提供共有的方法。(√)

3.面向对象的特点主要可以概括为封装性、继承性和重载性。(×)

4.定义在类中的变量叫做成员变量,定义在方法中的变量叫做局部变量。(√)

5.构造方法的名称必须与类名保持一致。(√)

三、选择题

1.下列关于this关键字的说法中,错误的是()

C.this关键字可以出现在任何方法中

2.阅读程序,运行结果为:

B.WorldHello

3.对于声明为private、protected及public的类成员在类外部()

D.都不能访问

4.阅读程序,描述正确的是()

B.输出2个hello后会抛出异常

5.下列类定义不正确的是()

C.static class X implements Y1,Y2{...}

四、简答题

1.简述你对面向对象的三大特征的理解

面向对象的特点可以概括为封装性、继承性和多态性。

封装是面向对象思想核心过程,将对象的属性和行为封存起来,不需要让外界知道具体实现细节,这就是封装思想。继承性主要描述的是类与类之间的关系,通过继承可以在无需编写原有类的情况下,对原有类进行扩展。多态性是指程序中允许出现重名现象,它指在一个类中定义的属性和方法被其他类继承后,它们可以具有不同的数据类型或表现出不同的行为,这使得同一个属性和方法可以在不同的类中具有不同的语义。

2.简述构造方法的特点

  • 构造方法与类名相同。
  • 在构造方法名的前面没有返回值类型的声明。
  • 在构造方法中不能使用return返回一个值。

五、编程题

Resume类

public class Main {
    public static void main(String[] args) {
        Resume human = new Resume("计科土狗","男",20);
        human.introduce();
    }
}
class Resume{
    private String name;
    private String sex;
    private int age;
    Resume(){}
    Resume(String name,String sex,int age){
        this.name = name;
        this.sex = sex;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
    public String getSex(){
        return sex;
    }
    public void setSex(String sex){
        this.sex = sex;
    }
    public int getAge(){
        return age;
    }
    public void setAge(int age){
        this.age = age;
    }
    public void introduce(){
        System.out.println("姓名:" + name);
        System.out.println("性别:" + sex);
        System.out.println("年龄:" + age);
    }
}

面向对象(下)(第四章)

继承

继承的概念

在程序中,继承描述的是事物之间的从属关系,通过继承可以使多种事物之间形成一种关系体系。在Java中,类的继承是指一个现有类的基础上构建一个新的类,构建的新类被称作子类,现有类被称为父类。子类会自动继承父类的属性和方法,使得子类具有父类的特征和行为。

在Java中,如果想声明一个类继承另一个类,需要使用extends关键字,其语法格式如下:

class 父类{
    ...
}
class 子类 extends 父类{
    ...
}

下面使用示例讲解:

class Animal{
    private String name;
    private int age;
    public final String COLOR = "黑色";
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
    public int getAge(){
        return age;
    }
    public void setAge(int age){
        this.age = age;
    }
}
class Dog extends Animal{
    //此处为空
}
public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.setName("哈士奇");
        dog.setAge(3);
        System.out.println("名称:" + dog.getName());
        System.out.println("年龄:" + dog.getAge());
        System.out.println("颜色:" + dog.COLOR);
    }
}

在上述代码中,Dog为Animal的子类,Animal为Dog的父类。Dog类中并没有定义任何属性和方法,但是能调用父类Animal的方法,证明子类在继承父类的时候会自动继承父类的属性和方法。

子类除了可以继承父类的属性和方法,也可以定义自己的属性和方法。

class Animal{
    private String name;
    private int age;
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
    public int getAge(){
        return age;
    }
    public void setAge(int age){
        this.age = age;
    }
}
class Dog extends Animal{
    private String COLOR;
    public String getCOLOR(){
        return COLOR;
    }
    public void setCOLOR(String COLOR){
        this.COLOR = COLOR;
    }
}
public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.setName("哈士奇");
        dog.setAge(3);
        dog.setCOLOR("黑色");
        System.out.println("名称:" + dog.getName());
        System.out.println("年龄:" + dog.getAge());
        System.out.println("颜色:" + dog.getCOLOR());
    }
}

需要注意的是,子类虽然可以通过继承访问父类的成员和方法,但不是所有的父类属性和方法都可以被子类访问。子类只能访问父类中用public和protected修饰的属性和方法,父类中被默认修饰符default和private修饰的属性和方法不能被子类访问。

在类的继承中,需要注意以下问题:

  • 在Java中,类只支持单继承,不支持多继承。
  • 多个类可以继承一个父类
  • 在Java中,一个类的父亲可以再继承另外的父类。
  • 在Java中,子类和父类是相对的,一个类可以是某个类的父类,也可以是某个类的子类。  

方法的重写

在继承关系中,子类会自动继承父类中定义的方法,但有时在子类中对继承的方法进行一些修改,即对父类的方法进行重写。在子类中重写的方法需要和父类中被重写的方法具有相同的方法名、参数列表、以及返回值类型。

class Shout{
    public void shout(){
        System.out.println("动物发出叫声");
    }
}
class Dog extends Shout{
    public void shout(){
        System.out.println("汪汪汪~~~");
    }
}
public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.shout();
    }
}

子类重写父类方法时的访问权限 

子类重写父类的方法时,不能使用比父类的方法更为严格的访问权限。例如父类的方法是public权限,子类的方法就不能是private权限。如果子类在重写父类的方法时定义的权限更加严格,则在编译时将出现错误。

super关键字

当子类重写父类后,子类对象将无法访问被子类重写过的方法。为解决这个问题,Java提供了super关键字,使用super关键字可以在子类中访问父类的非私有方法、非私有属性以及构造方法。

  • 使用super关键字访问父类的非私有属性或调用父类的非私有方法,具体格式如下:
super.属性
super.方法(参数1,参数2,...)
class Animal{
    String name = "牧羊犬";
    public void shout(){
        System.out.println("动物发出叫声");
    }
}
class Dog extends Animal{
    public void shout(){
        super.shout();
        System.out.println("汪汪汪~~~");
    }
    public void printName(){
        System.out.println("名字:" + super.name);
    }
}
public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.shout();
        dog.printName();
    }
}

使用super关键字调用父类中执行的构造方法,具体格式如下:

super(参数1,参数2,...)
class Animal{
    private String name;
    private int age;
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
    public int getAge(){
        return age;
    }
    public void setAge(int age){
        this.age = age;
    }
    public Animal(){}
    public Animal(String name,int age){
        this.name = name;
        this.age = age;
    }
    public String info(){
        return "名称:" + this.getName() + ",年龄:" + this.getAge();
    }
}
class Dog extends Animal{
    private String COLOR;
    public String getCOLOR(){
        return COLOR;
    }
    public void setCOLOR(String COLOR){
        this.COLOR = COLOR;
    }
    public Dog(String name,int age,String COLOR){
        super(name,age);
        this.COLOR = COLOR;
    }
    public String info(){
        return super.info() + ",颜色:" + this.COLOR;
    }
}
public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog("哈士奇",3,"黑色");
        System.out.println(dog.info());
    }
}

通过super调用父类构造方法的代码必须位于子类构造方法的第一行,并且只能出现一次。

​​​super与this关键字的作用非常相似,都可以访问属性以及调用方法和构造方法,但是二者之间还是有区别的。

区别点superthis
访问属性直接访问父类中的非私有属性访问本类中的属性。如果本类中没有该属性,则从父类中继续查找
调用方法直接调用父类中的非私有方法调用本类中的方法。如果本类中没有该方法,则从父类中继续查找
调用构造方法调用父类构造方法,必须放在子类构造方法的首行调用本类构造方法,必须放在构造方法的首行

需要注意的是, this和super不可以同时出现,因为使用this和super调用构造方法的代码都要求必须放在构造方法的首行。

final关键字

在默认情况下,所有成员变量和成员方法都可以被子类重写。如果父类的成员不希望被子类重写,可以在声明父类的成员时使用final关键字修饰。final有“最终”“不可更改”的含义。在Java中,可以使用final关键字修饰类、属性、方法。在使用final关键字时需要注意以下几点:

  • 使用final关键字修饰的类不能有子类
  • 使用final关键字修饰的方法不能被子类重写
  • 使用final关键字修饰的变量是常量,常量不可修改

final关键字修饰类

Java中使用final关键字修饰的类不可以被继承,也就是这样的类不能派生子类。

final class Animal{}
class Dog extends Animal{}
public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
    }
}

结果如下所示:

编译器显示“无法从最终Animal进行继承”错误,说明Dog类不能继承使用final修饰的Animal类。由此可见,被final关键字修饰的类不能被其他类继承。

final关键字修饰方法

当一个类的方法被final关键字修饰后,该类的子类不能重写该方法。

class Animal{
    public final void shout(){}
}
class Dog extends Animal{
    public void shout(){}
}
public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
    }
}

结果如下所示:

使用final关键字修饰父类Animal中的shout()方法,在子类Dog类中重写shout()方法时,编译器报告“Dog中的shout()无法覆盖Animal中的shout()”错误,这是因为Animal类的shout()方法被final关键字修饰,而子类不能对final关键字修饰的方法进行重写。 

final关键字修饰变量

Java中被final修饰的变量为常量,常量只能在声明时被赋值一次,在后面的程序中常量的值不能被改变。如果再次对final修饰的常量赋值,则程序会在编译时报错。

public class Main {
    public static void main(String[] args) {
        final int AGE = 18;
        AGE = 20;
    }
}

运行结果如下:

程序编译报错为“无法为最终变量AGE分配值”,这是因为使用final修饰的常量本身不可被修改。

需要注意的是,在使用final声明变量时,变量的名称要求全部为大写字母。如果一个程序中变量使用public static final声明,则此变量将成为全局变量。 

抽象类和接口

抽象类

定义一个类时,常常需要定义一些成员方法用于描述类的行为特征,但有时候这些方法的实现方式是无法确定的。例如前面定义的Animal类中的shout()方法用于描述动物的叫声,但是不同的动物叫声也不相同,因此在shout()方法中无法准确描述动物的叫声。

针对上面描述的情况,Java提供了抽象方法来满足这种需求,抽象方法是使用abstract关键字修饰的成员方法,抽象方法在定义时不需要实现方法体。抽象方法的语法格式如下:

abstract 返回值类型 方法名称 参数列表

当一个类包含了抽象方法,该类就是抽象类。抽象类和抽象方法一样,必须使用abstract关键字进行修饰。抽象类的语法格式如下:

abstract class 抽象类名称{
    属性;
    访问权限 返回值类型 方法名称(参数){            //普通方法
        return[返回值];
    }
    访问权限 abstract 返回值类型 抽象方法名称(参数);//抽象方法,无方法体
}

从上面抽象类的语法格式中可以发现,抽象类的定义比普通类多了一个或多个抽象方法,其他地方与普通类的组成基本相同。抽象类的定义规则如下:

  • 包含抽象方法的类必须是抽象类
  • 声明抽象类和抽象方法时都要使用abstract关键字修饰
  • 抽象方法只需要声明而不需要实现
  • 如果一个非抽象类继承了抽象类之后,那么该类必须重写抽象类中的全部抽象方法。
abstract class Animal{
    public abstract void shout();
}
class Dog extends Animal{
    public void shout(){
        System.out.println("汪汪汪~~~");
    }
}
public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.shout();
    }
}

使用abstract关键字修饰的抽象方法不能使用private关键字修饰,因为抽象方法必须被子类实现,如果使用了private关键字修饰抽象方法,则子类无法实现该方法。

接口

接口是一种用来定义程序的协议,它用于描述类或结构的一组相关行为。接口是由抽象类衍生的一个概念,并由此产生了一种编程方式,可以称这种编程方式为面向接口编程。面向接口编程就是将程序不同的业务逻辑分离,以接口的形式对接不同的业务模块。接口中不实现任何业务逻辑,业务逻辑由接口的实现类完成,当业务需求变更时,只需要修改实现类中的业务逻辑,而不需要修改接口中的内容,以减少需求变更对系统产生的影响。

在Java中,使用接口的目的是克服单继承的限制,因为一个类只能有一个父类,而一个类可以同时实现多个父接口。在JDK8之前,接口是由全局常量和抽象方法组成的。JDK8对接口进行了重新定义,接口中除了抽象方法外,还可以定义默认方法和静态方法,默认方法使用default关键字进行修饰,静态方法使用static关键字修饰,而且这两种方法都允许有方法体。

接口使用interface关键字声明:

[public] interface 接口名 [extends 接口1,接口2,...]{
    [public] [static] [final] 数据类型 常量名 = 常量;
    [public] [abstract] 返回值的数据类型 方法名(参数列表);
    [public] static 返回值的数据类型 方法名(参数列表);
    [public] default 返回值的数据类型 方法名(参数列表);
}

上述语法格式中,“extends 接口1,接口2,...”表示一个接口可以有多个父接口,父接口之间采用逗号分隔,接口中的变量默认使用public static final进行修饰,及全局常量。接口中定义的抽象方法默认使用public abstract进行修饰。

不管写不写访问权限,接口中方法的访问权限永远是public。 

接口本身不能直接实例化,接口中的抽象方法和默认方法只能通过接口实现类的示例对象进行调用。实现类通过implements关键字实现接口,并且实现类必须重写接口中所有的抽象方法。需要注意的是,一个类可以同时实现多个接口,实现多个接口时,多个接口名需要使用英文逗号分隔。

定义接口实现类的语法格式如下:

修饰符 class 类名 implements 接口1,接口2,...{
    ...
}
interface Animal{
    public static final int ID = 1;
    public static final String NAME = "哈士奇";
    public abstract void shout();
    public abstract void info();
    public static int getId(){
        return Animal.ID;
    }
}
interface Action{
    public abstract void eat();
}
class Dog implements Animal,Action{
    public void eat(){
       System.out.println("我喜欢吃骨头");
    }
    public void shout(){
        System.out.println("汪汪汪");
    }
    public void info(){
        System.out.println("名称" + NAME);
    }
}
public class Main {
    public static void main(String[] args) {
        System.out.println("编号" + Animal.getId());
        Dog dog = new Dog();
        dog.info();
        dog.shout();
        dog.eat();
    }
}

需要注意的是,接口的实现类必须实现接口中所有的抽象方法,否则程序编译报错,以上代码演示的是类与接口之间的实现关系,如果在开发中一个子类既要实现接口又要继承类,则可以按照以下语法格式定义子类。

修饰符 class extends 父类名 implements 接口1,接口2,...{
    ...
}

对上例代码进行修改,演示一个类既可以实现接口又可以继承类的情况:

interface Animal{
    public static final String NAME = "哈士奇";
    public abstract void shout();
    public abstract void info();
}
abstract class Action{
    public abstract void eat();
}
class Dog extends Action implements Animal{
    public void eat(){
       System.out.println("我喜欢吃骨头");
    }
    public void shout(){
        System.out.println("汪汪汪");
    }
    public void info(){
        System.out.println("名称" + NAME);
    }
}
public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.info();
        dog.shout();
        dog.eat();
    }
}

在Java中,接口不允许继承抽象类,但是允许接口继承接口,并且一个接口可以同时继承多个接口。

interface Animal{
    public static final String NAME = "哈士奇";
    public abstract void info();
}
interface Color{
    public abstract void black();
}
interface Action extends Animal,Color{
    public abstract void shout();
}
class Dog implements Action{
    public void black(){
       System.out.println("黑色");
    }
    public void shout(){
        System.out.println("汪汪汪");
    }
    public void info(){
        System.out.println("名称" + NAME);
    }
}
public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.info();
        dog.shout();
        dog.black();
    }
}

多态

多态概述

在Java中,多态是指不同类的对象在调用同一个方法是表现出的多种不同行为。Java中的多态主要有以下两种形式:

  • 方法的重载
  • 对象的多态(方法的重写)
abstract class Animal{
    public abstract void shout();
}
class Cat extends Animal{
    public void shout(){
        System.out.println("喵喵喵~~~");
    }
}
class Dog extends Animal{
    public void shout(){
        System.out.println("汪汪汪~~~");
    }
}
public class Main {
    public static void main(String[] args) {
        Animal ans1 = new Cat();
        ans1.shout();
        Animal ans2 = new Dog();
        ans2.shout();
    }
}

对象类型的转换

对象类型转换主要分为以下两种情况:

  • 向上转型:子类对象->父类对象
  • 向下转型:父类对象->子类对象
对象向上转型

对象向上转型,父类对象可以调用子类对象重写父类的方法,这样当需要新添功能时,只需要新增一个子类,在子类中对父类的红能进行扩展,而不用更改父类的代码,保证了程序的安全性,对于向上转型,程序会自动完成,对象向上转型的格式如下:

父类类型 父类对象 = 子类实例;
class Animal{
    public void shout(){
        System.out.println("喵喵喵~~~");
    }
}
class Dog extends Animal{
    public void shout(){
        System.out.println("汪汪汪~~~");
    }
    public void eat(){
        System.out.println("吃骨头~~~");
    }
}
public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog();
        dog.shout();
    }
}

需要注意的时,父类Animal的对象dog是无法调用Dog类中的eat()方法的,因为eat()方法只在子类中定义,并未在父类中定义。

对象向下转型

除了向上转型,对象还可以向下转型。向下转型一般是为了重新获得因为向上转型而丢失的子类特性。对象在进行向下转型前,必须先进行向上转型,否则将出现对象转换异常。

向下转型是,必须要指明要转为的子类类型,对象向下转型格式如下:

父类类型 父类对象 = 子类示例;
子类类型 子类对象 = (子类)父类对象;
class Animal{
    public void shout(){
        System.out.println("喵喵喵~~~");
    }
}
class Dog extends Animal{
    public void shout(){
        System.out.println("汪汪汪~~~");
    }
    public void eat(){
        System.out.println("吃骨头~~~");
    }
}
public class Main {
    public static void main(String[] args) {
        Animal an = new Dog();
        Dog dog = (Dog)an;
        dog.shout();;
        dog.eat();
    }
}

需要注意的是,在向下转型时,不能直接将父类示例强制转换为子类示例,否则程序将会报错。

instanceof关键字

Java中可以使用instanceof关键字判断一个对象是否是某个类或接口的实例,语法格式如下:

对象 instanceof 类(或接口)
class Animal{
    public void shout(){
        System.out.println("动物叫~~~");
    }
}
class Dog extends Animal{
    public void shout(){
        System.out.println("汪汪汪~~~");
    }
    public void eat(){
        System.out.println("吃骨头~~~");
    }
}
public class Main {
    public static void main(String[] args) {
        Animal ans1 = new Dog();
        System.out.println("Animal ans1 = new Dog():" + (ans1 instanceof Animal));
        System.out.println("Animal ans1 = new Dog():" + (ans1 instanceof Dog));
        Animal ans2 = new Animal();
        System.out.println("Animal ans2 = new Animal():" + (ans2 instanceof Animal));
        System.out.println("Animal ans2 = new Animal():" + (ans2 instanceof Dog));
    }
}

Object类

Java提供了Object类,它是所有类的父亲,每个类都直接或间接的继承了Object类,因此Object类通常被称为超类。当定义一个类时,如果没有使用extends关键字为这个类显式地指定父亲,那么该类会默认继承Object类,Object类的常用方法如下表所示:

方法名称方法说明
boolean equals()判断两个对象是否“相等”
int hashCode()返回对象的哈希值
String toString返回对象的字符串表示形式

现以toString为例: 

class Animal{
    public void shout(){
        System.out.println("动物叫~~~");
    }
}
public class Main {
    public static void main(String[] args) {
        Animal ans = new Animal();
        System.out.println(ans.toString());
    }
}

运行结果如下:

编译器并未报错,这是因为Animal默认继承Object类,同时继承了Object类的toString方法。

在实际开发中,通常情况下不会直接调用Object类中的方法,因为Object类中的方法并不适用于所有的子类,这就需要对Object类中的方法进行重写,以满足实际开发需求。下面通过重写Object类的toString()方法进行演示。

class Animal{
    public String toString(){
        return "这是一个动物";
    }
}
public class Main {
    public static void main(String[] args) {
        Animal ans = new Animal();
        System.out.println(ans.toString());
    }
}

运行结果如下:

内部类

在Java中,允许一个类的内部定义类,这样的类称为内部类,内部类所在的类称作为外部类。在实际开发中,根据内部类的位置、修饰符和定义方式的不同,内部类可分为4种,分别是成员内部类、局部内部类、静态内部类、匿名内部类。

成员内部类

在一个类种除了可以定义成员变量、成员方法,还可以定义类,这样的类称作成员内部类。成员内部类可以访问外部类的所有成员,无论外部类的成员是何种访问权限。如果想通过外部类访问内部类,则需要通过外部类创建内部类对象。创建内部类对象的具体语法格式如下:

外部类名 外部类对象 = new 外部类名();
外部类名.内部类名 内部类对象 = 外部类对象.new 内部类名();
class Outer{
    int m = 0;
    void test1(){
        System.out.println("外部类成员方法test1()");
    }
    class Inner{
        int n = 1;
        void show1(){
            System.out.println("外部类成员变量m = " + m);
            test1();
        }
        void show2(){
            System.out.println("内部类成员方法show2()");
        }
    }
    void show2(){
        Inner inner = new Inner();
        System.out.println("内部类成员变量n = " + inner.n);
        inner.show2();
    }
}
public class Main {
    public static void main(String[] args) {
        Outer outer = new Outer();
        Outer.Inner inner = outer.new Inner();
        inner.show1();
        outer.show2();
    }
}

局部内部类

局部内部类,也称方法内部类,是指定义在某个局部范围中的类,它和局部变量都是在方法中定义的,有效返回只限于方法内部。

局部内部类可以方位外部类的所有成员变量和成员方法,而在外部类中无法直接访问局部内部类中的变量和方法。如果要在外部类中访问局部内部类的成员,只能在局部内部类的所属方法中创建局部内部类的对象,通过对象访问局部内部类的变量和方法。

class Outer{
    int m = 0;
    void test1(){
        System.out.println("外部类成员方法test1()");
    }
    void test2(){
        class Inner{
            int n = 1;
            void show() {
                System.out.println("外部类成员变量m = " + m);
                test1();
            }
        }
        Inner inner = new Inner();
        System.out.println("局部变量n = " + inner.n);
        inner.show();
    }
}
public class Main {
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.test2();
    }
}

静态内部类

静态内部类,就是使用static关键字修饰的成员内部类。与成员内部类相比,在形式上,静态内部类只是在内部类前增加了static关键字,但在功能上,静态内部类只能访问外部类的静态成员,通过外部类访问静态内部类的成员时,因为程序已经提前在静态常量区为静态内部类分配好了内存,所以及时静态内部类没有加载,依然可以通过外部类直接创建一个静态内部类对象。

创建静态内部类对象的基本语法格式如下:

外部类名.静态内部类名 变量名 = new 外部类名.静态内部类名();
class Outer{
    static int m = 0;
    void test1(){
        System.out.println("外部类成员方法test1()");
    }
    static class Inner{
        int n = 1;
        void show() {
            System.out.println("外部类静态成员变量m = " + m);
        }
    }
}
public class Main {
    public static void main(String[] args) {
        Outer.Inner inner = new Outer.Inner();
        inner.show();
    }
}

匿名内部类

在Java中调用某个方法时,如果该方法的参数是接口类型,那么在传参时,除了可以传入一个接口实现类,还可以传入实现接口的匿名内部类作为参数,在匿名内部类中实现接口方法。匿名内部类就是没有名称的内部类,定义匿名内部类时,其类体作为new语句的一部分。定义匿名内部类的基本语法格式如下:

new 继承的父类或实现的接口名(){
    匿名内部类的实体
}

上述语法格式创建了一个匿名内部类的对象,该匿名内部类继承了指定父类或实现了指定接口。

下面通过一个匿名内部类的定义详细讲解匿名内部类的基本语法格式。例如定义了animalShout()方法,该方法的参数为Animal接口类型的对象,那么在调用该方法时,可以在方法参数的位置定义一个实现Animal接口的匿名内部类。animalShout()方法具体调用代码如下:

animalShout(new Animal(){});

在上述代码中,在animalShout()方法的参数位置写上new Animal(){},相当于创建了一个Ainmal接口的实现类的对象,并将对象作为参数传给animalShout()方法。在new Animal()后面有一对大括号,表示创建的对象为Animal的实现类的示例,该实现类是匿名的。

interface Animal{
    public abstract void shout();
}
public class Main {
    public static void main(String[] args) {
        String name = "小花";
        animalShout(new Animal(){
            @Override
            public void shout(){
                System.out.println(name + "喵喵喵~~~");
            }
        });
    }
    public static void animalShout(Animal an){
        an.shout();
    }
}

需要注意的是,在JDK8之前,局部变量前必须加final关键字,否则程序编译时报错,但在JDK8以后,允许在局部内部类、匿名内部类中访问未用final修饰的局部变量。 

本章小结

一、填空题

1.在面向对象中,类之间共享属性和操作的机制称为继承。

2.在继承关系中,子类会自动继承父类中的方法,但有时在子类中需要对继承的方法进行一些修改,即对父类的方法进行重写。

3.final关键字可用于修饰类、变量和方法,他有“无法改变的”或者“最终”的含义。

4.一个类如果要实现一个接口,可以使用关键字implements。

5.一个类如果要实现一个接口,那么它就需要重写接口中定义的全部方法,否则该类就必须定义成抽象类。

6.如果子类像引用父类的成员,可以使用关键字super。

二、判断题

1.定义一个抽象类的关键字是interface。(×)

2.父类的引用指向自己子类的对象是多态的一种体现形式。(√)

3.Java中一个类最多可以有一个直接父类。(√)

4.接口中定义的变量默认都是public static final型,且必须赋初值。(√)

5.final修饰的局部变量只能被赋值一次。(√)

6.在定义方法时不写方法体,这种不包含方法体的方法称为静态方法。(×)

7.Java中的instanceof关键字可以判断一个对象是否为某个类(或接口)的实例。(√)

三、选择题

1.下面程序的运行结果是()

B.可以编译运行,并输出结果AB

2.下列关于继承的描述中错误的是()

D.Java是支持多继承的

3.下列关于对象的类型转换的描述中错误的是()

C.由new语句创建的父类对象可以强制转换为子类对象

4.下列关于接口的说法中错误的是()

D.接口中定义的变量可以被修改

5.阅读代码,返回值为true的是()

C.s3.equals(s4)

四、简答题

1.简述Java中继承的概念以及使用继承的好处。

概念:类的继承是指在一个现有类的基础上去构建一个新的类,构建出来的新类被称为子类,子类会自动拥有父类所有可继承的属性和方法。

好处:继承性主要描述的是类与类之间的关系,通过继承,可以在无需重写原有类的情况下,对原有类的功能进行使用和扩展。

2.简述多态的作用

  • 应用程序不必为每一个子类编写功能调用,只需要对抽象父类进行处理计科,大大提高了程序的可复用性。
  • 子类的功能可以被父类的方法或者引用变量所调用,这叫做向后兼容,可以提高可扩充性和可维护性。
  • 使用多态可以解决项目中紧耦合问题,提高程序的可扩展性,是OCP原则的一个具体实现。

3.简述接口和抽象类的区别

  • 接口只能定义抽象方法,不能实现方法,抽象类既可以定义抽象方法,也可以实现方法。
  • 接口可以实现多个,抽象类只能继承一个。
  • 接口强调的是功能,具体有什么能力,抽象类强调的是所属关系。
  • 接口中的所有成员变量为静态,必须初始化,不可修改,所有方法为公开抽象,不能有构造方法。抽象类则无此要求。

五、编程题

Employee类

abstract class Employee{
    private String name;
    private int birth_month;
    public String getName(){
        return this.name;
    }
    public void setName(String name){
        this.name = name;
    }
    public int getBirth_month(){
        return this.birth_month;
    }
    public void setBirth_month(int birth_month) {
        this.birth_month = birth_month;
    }
    public Employee(){}
    public Employee(String name,int birth_month){
        this.name = name;
        this.birth_month = birth_month;
    }
    public double getSalary(int month){
        double salary = 0;
        if(month == this.birth_month)salary += 100;
        return salary;
    }
}

SalariedEmployee类

class SalariedEmployee extends Employee{
    private double monthSalary;
    public double getMonthSalary(){
        return this.monthSalary;
    }
    public void setMonthSalary(double monthSalary){
        this.monthSalary = monthSalary;
    }
    public SalariedEmployee(){}
    public SalariedEmployee(String name,int birth_month,double monthSalary){
        super(name,birth_month);
        this.monthSalary = monthSalary;
    }
    public double getSalary(int month){
        double salary = monthSalary + super.getSalary(month);
        return salary;
    }
}

HourlyEmployee类

class HourlyEmployee extends Employee{
    private double hourlySalary;
    private int hours;
    public double getHourlySalary(){
        return this.hourlySalary;
    }
    public void setHourlySalary(double hourlySalary){
        this.hourlySalary = hourlySalary;
    }
    public int getHours(){
        return this.hours;
    }
    public void setHours(int hours){
        this.hours = hours;
    }
    public HourlyEmployee(){}
    public HourlyEmployee(String name,int birth_month,double hourlySalary,int hours){
        super(name,birth_month);
        this.hourlySalary = hourlySalary;
        this.hours = hours;
    }
    public double getSalary(int month){
        if(hours < 0){
            System.out.println("数据错误");
            return 0;
        }
        else if(hours <= 160){
            return hourlySalary * hours + super.getSalary(month);
        }
        else return hourlySalary * 160 + (hours - 160) * 1.5 * hourlySalary + super.getSalary(month);
    }
}

SalesEmployee类

class SalesEmployee extends Employee{
    private double sales;
    private double rate;
    public double getSalesSalary(){
        return this.sales;
    }
    public void setSalesSalary(double sales){
        this.sales = sales;
    }
    public double getRate(){
        return this.rate;
    }
    public void setRate(double rate){
        this.rate = rate;
    }
    public SalesEmployee(){}
    public SalesEmployee(String name,int birth_month,double sales,double rate){
        super(name,birth_month);
        this.sales = sales;
        this.rate = rate;
    }
    public double getSalary(int month){
        return sales * rate + super.getSalary(month);
    }
}

BasePlusSalesEmployee类

class BasePlusSalesEmployee extends SalesEmployee{
    private double base;
    public double getBase(){
        return this.base;
    }
    public BasePlusSalesEmployee(){}
    public BasePlusSalesEmployee(String name,int birth_month,double sales,double rate,double base){
        super(name,birth_month,sales,rate);
        this.base = base;
    }
    public double getSalary(int month){
        return super.getSalary(month) + base;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

计科土狗

谢谢家人们

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值