陈国君Java程序设计基础(第六版)习题-标准答案

一、Java语言概述

1.Java语言有哪些特点?

  • 简单易学。Java去掉了C/C++语言支持的单个不易理解和掌握的数据类型(指针pointer、联合体unions、结构体structs),转而用类和类的属性等面向对象的方法来实现,更加合理规范,且降低学习难度。
  • 面向对象。Java是一种以对象为中心、以消息为驱动的面向对象的编程语言,支持封装、继承、多态。
  • 平台无关性。Java语言是靠Java虚拟机(JVM)在目标代码级实现平台无关性的。
  • 分布式。分布式包括数据分布和操作分布,Java语言支持这两种分布性。
  • 可靠性。Java解释器运行时实施检查,可以发现数组和字符串的访问越界,另外,Java语言提供了异常处理机制,可以把一组错误的代码放在一个地方,这样可以简化错误处理任务,便于恢复。
  • 安全性。当Java字节码进入解释器时,首先必须经过字节码校验器的检查;其次,Java解释器将决定程序中类的内存布局;再次,类装载器负责把来自网络的类装载到单独的内存区域,避免应用程序之间相互干扰破坏;最后,客户端用户还可以限制从网络上装载的类只能访问某些文件系统。
  • 支持多线程。一方面,Java环境本身就是多线程的,若干系统线程运行,负责必要的无用单元回收、系统维护等系统级操作。另一方面,Java语言内置多线程机制,可以大大简化多线程应用程序开发。
  • 支持网络编程。Java语言通过它所提供的类库可以处理TCP/IP,用户可以用URL地址在网络上很方便地方位其他对象。
  • 编译与解释并存。.java文件首先会被编译成字节码文件(.class文件),字节码文件可以被Java解释器执行,有解释器将字节码文件再翻译成二进制码,使程序得以运行。

2.什么是Java虚拟机?

任何一种可以运行Java字节码的软件均可被看成Java的“虚拟机”(JVM),如浏览器与Java的开发工具等皆可以被视为一部JVM。可以把Java的字节码看成JVM上所运行的机器码(machine code),即JVM中解释器负责将字节码解释成本地的机器码。所以从底层上看,JVM就是以Java字节码为指令组的“软CPU”。可以说JVM是可运行Java字节码的假想计算机。它的作用类似于Windows操作系统,只不过在Windows上运行的是.exe文件,而在JVM上运行的是Java字节码文件,也就是扩展名为.class的文件。JVM其实就是一个字节码解释器。

3.什么是字节码?采用字节码的最大好处是什么?

(1)字节码是Java虚拟机(Java Virtual Machine,JVM)的指令组,和CPU上的微指令码很相像。Java程序编译成字节码后文件尺寸较小,便于网络传输。

(2)字节码最大的好处是可跨平台运行,即Java的字节码可以编写一次,到处运行。用户使用任何一种Java编译器将Java源程序(.java)编译成字节码文件(.class)后,无论使用哪种操作系统,都可以在含有JVM的平台上运行,这种跨越平台的特性也是让Java语言急速普及的原因之一。

4.什么是平台无关性?Java语言是怎样实现平台无关性的?

(1)平台无关性:某种语言编写的程序不用修改就可在不同的软硬件平台上运行。

  • 平台无关有两种:源代码级和目标代码级。C和C++语言编写的应用程序不用修改,只需要重新编译就可以在不同平台上运行。

(2)Java语言是靠Java虚拟机(JVM)在目标代码级实现平台无关性的。

5.Java语言程序有几种?每种程序的结构包含那几个方面?

(1)使用Java语言可以编写两种类型的程序:Application(应用程序)和Applet(小程序)。这两种程序的开发原理是相同的,但是在运行环境和计算结构上却有着显著的不同。

(2)Java应用程序是可以在Java平台上独立运行的程序,应用程序中包括至多一个package语句、0个或多个import语句和至少一个类三部分。Java小程序是嵌入在HTML文件中的Java程序,是一种需要搭配浏览器才能运行的Java程序,所以小程序处理应用程序包括的三部分之外,还应有一个HTML文档将小程序嵌入其中。

6.什么是Java程序的主类?应用程序于小程序的主类有何不同?

(1)主类是Java程序执行的的入口点。一个程序中,可以有多个类,但只能有一个类是主类。在Java应用程序中,这个主类是指包含main()方法的类。在Java小程序里,这个主类是一个继承自系统类JApplet的子类。

(2)应用程序的主类不一定要求是public类,但小程序的主类一定要求是public类。


二、Java语言开发环境

1.什么是JDK?什么是JRE?JDK与JRE的关系是什么?

Java SE可以分为四部分:JVM、JRE、JDK和Java语言。

(1)JDK(Java Development Kits,Java开发工具包),是一个编写Java Application应用程序和Applet小程序的程序开发环境,其中包括一些Java开发工具和Java的核心类库(Java API)等,是所有Java开发工具的基础。

(2)JRE(Java Runtime Environment,Java运行环境)是Java执行程序所必须的,JRE主要是为开发好的Java程序提供程序平台。

(3)JDK与JRE的关系是:JRE是一个运行环境,JDK是一个开发环境。JRE不包含开发工具,如编译器、调试器和其他工具等,而JDK包含了JRE以及开发过程中需要的一些工具程序,引起安装JDK后除了可以编辑Java程序外,也可以运行Java程序。所以编写Java程序时需要JDK,而运行Java程序时需要JRE。

2.Java开发工具JDK 10安装后,在安装文件夹下生成几个子文件夹?这些子文件夹中包含的内容有哪些?

在JDK安装文件夹下包含的子文件夹及相应子文件夹下所包含的内容如下:

  • bin:该文件夹存放javac.exe、java.exe、jmod.exe、jar.exe等命令程序。
  • conf:该文件夹存放一些可供开发者编辑的Java系统配置文件。
  • include:该文件夹存放支持本地代码编程与C程序相关的头文件。
  • jmods:该文件夹存放预编译的Java模块,相当于JDK9之前的.jar文件。
  • legal:该文件夹存放有关Java每个模块的版权声明和许可协议等。
  • lib:该文件夹存放Java类库。

3.环境变量Path和ClassPath的作用是什么?如何设置Path环境变量?

(1)Path环境变量的作用是设置供操作系统去寻找和执行应用程序(.exe、.com、.bat等)路径的顺序,对Java而言即Java的安装路径;ClassPath是JVM执行Java程序时搜索类的路径(类所在的文件夹)的顺序,以最先找到的为准,JVM除了在ClassPath的环境变量指定的文件夹中查找要运行的类之外,默认是不会在当前文件夹下查找相应类的,除非设置在当前文件夹下查找。

(2)设置Path环境变量的方法有两种:一种是在控制面板中“系统和安全”下的“系统”页面内(或右击“我的电脑”图标,在弹出的快捷菜单中选择“属性”命令)设置;另一种是在命令行窗口中利用set命令进行设置。系统环境变量ClassPath在Java 10中不用再设置,Java程序完全可以编译与运行。

4.编写Java程序有哪些注意事项?

首先要安装JDK,然后必须设置系统环境变量Path。还需注意必须要按程序的命名规则给程序命名。程序中的每个类都有类名和类体,类体重通常包含两种成分:一种是成员变量;另一种是成员方法,方法体中的每个美剧以分号“;”结尾,方法体内不能再定义其他方法。另外,Java是严格区分大小写的语言。

5.Java应用程序源文件的命名有什么规定?

Java应用程序源文件的命令规则为,首先源文件的扩展名必须是.java;如果源文件中有多个类,则最多只能有一个public类,如果有,那么源文件的名字必须与这个public类的名字相同(文件名的大小写可以与public类名的大小写不同),如果源文件没有public类,那么源文件的名字由用户任意命名。

6.Java应用程序的主类是什么样的类?

主类是Java程序执行的入口点,一个Java程序可以有多个类,但只能有一个类是主类。对应用程序而言其主类必须是包含有main()方法的类。

7.如何在命令行方式下编译与运行Java应用程序?

首先在命令行窗口将应用程序源文件使用“javac 文件名.java”命令编译成扩展名为.class的字节码文件,然后运行字节码文件即可,即在命令行提示符下输入“java 主类名”。需要注意的是,原文件名和主类名可能不同,所以编译和运行程序时要注意区别。


三、Java语言基础

1.Java语言定义了哪几种基本数据类型?

Java语言定义了4类8种基本类型,其中4种整型、2种浮点型、1种布尔型、1种字符型。

  • 整型:byte、short、int、long
  • 浮点型:float、double
  • 布尔型:boolean
  • 字符型:char

2.表示整数类型数据的关键字有哪几个?他们各占用几个字节?

(1)表示整数类型数据的关键字有4个,分别是:byte、short、int、long

(2)占用字节数如下:

  • byte:1个字节
  • short:2个字节
  • int:4个字节
  • long:8个字节

3.单精度浮点型(float)和双精度浮点型(double)的区别是什么?

单精度浮点型和双精度浮点型表示的都是实数,双精度浮点型数占8个字节,而单精度浮点型数占4个字节,双精度浮点型数的精度是单精度浮点型数精度的2倍。

4.字符型常量与字符串常量的主要区别是什么?

字符型常量是用单引号括起来的单个字符,而字符串常量是用双引号括起来的多个字符,且字符数可以为0个。

5.简述Java语言对定义标识符的规定。

(1)标识符可以有字母、数字、下划线(_)和美元符号($)等组合而成;

(2)标识符必须以字母、下划线或美元符号开头,不能以数字开头。

同时,应注意Java语言是大小写敏感的语言。例如,class和Class、System和system分别代表不同的标识符,在定义和使用时要特别注意这一点。

用Java语言编程是,经常遵循一下命名习惯(不是强制的):

  • 类名首字母大写;变量名、方法名及对象名的首字母小写;
  • 对于所有标识符,其中包含的所有单词都应紧靠在一起,而且中间单词的首字母大写。例如:ThisIsAClassName、thisIsMethodOrFieldName;
  • 若定义常量时,则所有字母大写,这样便可标志出他们属于编译期的常数;
  • Java包(package)属于一种特殊情况,他们全都是小写字母,即便中间的单词亦是如此。

6.Java语言采用何种编码方案?有何特点?

(1)Java语言中的字符采用的是Unicode字符集编码方案,在内存中占两个字节,是16位如符号的整数,一共有65536个,字符的取值范围为0~65535,表示其在Unicode字符集中的排序位置

(2)Unicode字符表的前128个字符刚好是ASCII表。每个国家的字母表的字母都是UNicode表中的一个字符。由于Java语言的字符类型采用了Unicode这种新的国际标准编码方案,因而便于中文字符和西文字符的处理。

7.什么是强制类型转换?在什么情况下需要用强制类型转换?

(1)数值型数据的类型转换分为自动类型转换(或称隐含类型转换)和强制类型转换两种。把占用比特数较少的数据(简称较短的数据)转换成占用比特数较多的数据(简称较长的数据),都使用自动类型转换,即类型转换由编译系统自动完成,不需要程序做特别的说明。但如果把较长的数据转换成较短的数据时,就要进行强制类型转换,否则就会产生编译错误。

(2)强制类型转换必须在程序中用“(欲转换的数据类型)变量名”形式的语句显性说明将变量名的类型强制转换成什么类型。需要说明的是,指定的变量名及其数据本身将不会因此而改变。

8.自动类型转换的前提是什么?转换时从“短”到“长”的优先级顺序是怎样的?

(1)Java语言会在下列条件同时成立时,自动进行数据类型的转换。

  • 转换前的数据类型与转换后的类型兼容;
  • 转换后数据类型的表示范围比及转换前数据类型的表示范围大。
    • 条件二说明不同类型的数据进行运算时,需先转换为同一类型,然后进行运算。

(2)转换从“短”到“长”的优先关系为:

byte ----> short ----> char ----> int ----> long ----> float ----> double

9.数字字符串转换为数值型数据时,所使用的方法有哪些?

转换的方法功能说明
Byte.parseByte(String s)将数字字符串转换为字节型数据
Short.parseShort(String s)将数字字符串转换为短整型数据
Integer.parseInt(String s)将数字字符串转换为整形数据
Long.parseLong(String s)将数字字符串转换为长整型数据
Float.parseFloat(String s)将数字字符串转换为浮点型数据
Double.parseDouble(String s)将数字字符串转换为双精度型数据
Boolean.parseBoolean(String s)将字符串转换为布尔型数据

10.写出由键盘输入数据的两种基本格式。

(1)数据输入方式1:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class test10 {
    public static void main(String[] args) throws IOException {
        String str;
        BufferedReader buf;
        buf = new BufferedReader(new InputStreamReader(System.in));
        str = buf.readLine();
        System.out.println(str);
    }
}

(2)数据输入方式2:

import java.util.Scanner;

public class test10 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        double num;
        num = scanner.nextDouble();
        System.out.println(num);
    }
}

11.编写程序,从键盘上输入一个浮点数,然后将该浮点数的整数部分输出。

import java.util.Scanner;

public class test11 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        float f = scanner.nextFloat();
        System.out.println((int)f);
    }
}

12.编写程序,从键盘上输入两个整数,然后计算他们相除后得到的结果并输出。

import java.util.Scanner;

public class test12 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int a = scanner.nextInt();
        double b = scanner.nextDouble();
        System.out.println(a/b);
    }
}

13.编写程序,从键盘上输入圆柱体的底半径r和高h,然后计算其体积并输出。

import java.util.Scanner;

public class test13 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        double r = scanner.nextDouble();
        double h = scanner.nextDouble();
        double V = Math.PI * r * r * h;
        System.out.println(V);
    }
}

14.Java语言有哪些算术运算符、关系运算符、逻辑运算符、位运算符和赋值运算符?

(1)算术运算符:

  • 二元算术运算符:+ - * / %
  • 一元算术运算符:+ - ++ –

(2)关系运算符:> >= <

(3)逻辑运算符:& | ! ^ && ||

(4)运算符:~ & | ^ >> << >>>

(5)赋值运算符:= += -= *= /= %= &= |= ^= >>= <<= >>>=

15.逻辑运算符中的逻辑与、逻辑或和简洁与、简洁活的区别是什么?

简洁运算(&&、||)与非简洁运算(&、|)的区别在于:非简洁运算在必须计算完成运算符左右两个表达式之后,才取结果;简洁运算可能只需计算运算符左边的表达式而不用计算右边的表达式,即对于&&,只要左边的表达式为false,就不用计算右边表达式,则整个表达式为false;对于||,只要左边表达式为true,就不用计算右边表达式,则整个表达式为true。

16.逻辑运算符与位运算符的区别是什么?

位运算符的操作数只能为整型或字符型数据,但逻辑运算符的操作数据为boolean型的量。

17.什么是位运算符的优先级和结合性?

位运算符的优先级决定了表达式中不同运算符执行的先后顺序;结合性决定了并列的多个同级运算符的先后执行顺序。

18.写出下列表达式的值,设x=3,y=17,yn=true。

(1)x+y*x-- 54

(2)-x*y+y -34

(3)x<y&&yn true

(4)x>y||!yn false

(5)y!=++x?x:y 4

(6)y++/–x 8


四、流程控制

1.将学生的学习成绩按不同的分数段分为优、良、中、及和不及格五个等级,从键盘上输入一个0~100的成绩,输出相应的等级。要求用switch语句实现。

import java.util.Scanner;

public class test01 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int score = scanner.nextInt();
        int tag = score / 10;
        System.out.print("该学生的成绩为: ");
        switch (tag) {
            case 10:
            case 9:
                System.out.println("优");
                break;
            case 8:
                System.out.println("良");
                break;
            case 7:
                System.out.println("中");
                break;
            case 6:
                System.out.println("及");
                break;
            default:
                System.out.println("不及格");
        }
    }
}

2.将学生的学习成绩按如下的分数段评定为四个等级:85100为A;7084位B;6069为C;059为D。从键盘上输入一个0~100的成绩,要求用switch语句根据成绩,评定并输出相应的等级。

import java.util.Scanner;

public class test02 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入学生的成绩:");
        int score = scanner.nextInt();
        if (score < 60) {
            System.out.println("D");
            return;
        }
        int tag = score / 5;
        switch (tag) {
            case 20:
            case 19:
            case 18:
            case 17:
                System.out.println("A");
                break;
            case 16:
            case 15:
            case 14:
                System.out.println("B");
                break;
            default:
                System.out.println("C");
        }
    }
}

3.编写一个Java应用程序,从键盘输一个1~100之间的整数,然后判断该数是否既可以被3整除又可以被7整除的数。

import java.util.Scanner;

public class test03 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入一个1~100之间的整数:");
        int num = scanner.nextInt();
        if (num % 3 == 0 && num % 7 == 0) System.out.println("yes");
        else System.out.println("NO");
    }
}

4.编写一个Java应用程序,在键盘上输入数n,计算并输出1!+2!+…+n!的结果。

import java.util.Scanner;

public class test04 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入一个整数n:");
        int n = scanner.nextInt();
        if (n == 0) {
            System.out.println(1);
            return;
        }
        int sumTemp = 1;
        int sum = 0;
        for (int i = 1; i <= n; i++) {
            sumTemp = sumTemp * i;
            sum = sum + sumTemp;
        }
        System.out.println(sum);
    }
}

5.在键盘上输入数n,编程计算sum=1-1/2!+1/3!-…(-1)^(n-1)*1/n!。

import java.util.Scanner;

public class test05_2 {
    public static void main(String[] args) {
        int n, i = 1;
        double s = 1, sum = 0;
        Scanner buf = new Scanner(System.in);
        do {
            System.out.println("请输入n的值:");
            n = buf.nextInt();
        } while (n <= 0);
        do {
            sum = sum + s;
            i++;
            s = -s / i;
        } while (i <= n);
        System.out.println("sum=" + sum);
    }
}

6.水仙花数是指其个位、十位和百位三个数字的立方和等于这个三位数本身,求出所有的水仙花数。

import java.util.Scanner;

public class test06 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        for (int i = 100; i < 999; i++) {
            int ge = i % 10;
            int shi = i / 10 % 10;
            int bai = i / 100 % 10;
            if (i == ge * ge * ge + shi * shi * shi + bai * bai * bai) System.out.println(i);
        }
    }
}

7.从键盘输入一个整数,判断该数是否是完全数。完全数是指其所有的因数(包括1但不包括其本身)的和等于该数自身的数。例如,28=1+2+4+7+14就是一个完全数。

import java.util.Scanner;

public class test07 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int num = scanner.nextInt();
        int sum = 0;
        for (int i = 1; i < num; i++) {
            if (num % i == 0) {
                sum = sum + i;
            }
        }
        if (sum == num) System.out.println("是完全数");
        else System.out.println("不是完全数");
    }
}

8.计算并输出一个整数各位数字之和。例如:5423的各位数字之和位5+4+2+3。

import java.util.Scanner;

public class test08 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int num = scanner.nextInt();
        int s;
        int sum = 0;
        while (num > 0) {
            s = num % 10;
            sum = sum + s;
            num = num / 10;
        }
        System.out.println(sum);
    }
}

9.从键盘上输入一个浮点数,然后将该浮点数的整数部分和小数部分分别输出。

import java.util.Scanner;

public class test09 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        float f = scanner.nextFloat();
        String s = f + "";
        int index = s.indexOf('.');
        System.out.println("整数部分:" + s.substring(0, index));
        System.out.println("小数部分:" + s.substring(index + 1));
    }
}

10.编程输出如下数字图案。

1 3 6 10 15

2 5 9 14

4 8 13

7 12

11

public class test10 {
    public static void main(String[] args) {
        int i, count, a, c;
        int b = 1;
        for (count = 1; count <= 5; count++) {
            c = count;
            c--;
            a = b + c;
            b = a;
            System.out.print(b + " ");
            for (i = 1 + count; i <= 5; i++) {
                a += i;
                System.out.print(a + " ");
            }
            System.out.print("\n");
        }
    }
}

第五章、数组与字符串

1.从键盘输入n个数,输出这些数中大于其平均值的数。

import java.util.Scanner;

public class test01 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入数组的长度:");
        int n = scanner.nextInt();
        int[] ints = new int[n];
        System.out.println("请依次输入数组的每个元素:");
        for (int i = 0; i < n; i++) {
            ints[i] = scanner.nextInt();
        }
        int sum = 0;
        for (int i = 0; i < n; i++) {
            sum = sum + ints[i];
        }
        float ave = sum / n;
        StringBuilder sb = new StringBuilder("");
        for (int i = 0; i < n; i++) {
            if (ints[i] > ave) {
                sb = sb.append(ints[i]).append(",");
            }
        }
        System.out.println(sb.substring(0, sb.length() - 1));
    }
}

2.从键盘输入n个数,求这n个数中的最大数与最小数并输出。

import java.util.Scanner;

public class test02_2 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入一组数,中间用空格隔开:");
        String[] strings = scanner.nextLine().split(" ");
        int n = strings.length;
        int[] ints = new int[n];
        for (int i = 0; i < n; i++) {
            ints[i] = Integer.parseInt(strings[i]);
        }
        int MIN = ints[0];
        int MAX = ints[0];
        for (int i = 0; i < n; i++) {
            if (MIN > ints[i]) {
                MIN = ints[i];
            }
            if (MAX < ints[i]) {
                MAX = ints[i];
            }
        }
        System.out.println("最小数为:" + MIN);
        System.out.println("最大数为:" + MAX);
    }
}

3.求一个3阶方阵的对角线上各元素之和。

import java.util.Scanner;

public class test03 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int[][] ints = new int[3][3];
        System.out.println("请输入一个三阶方阵:");
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                ints[i][j] = scanner.nextInt();
            }
        }
        int sum = 0;
        for (int i = 0; i < 3; i++) {
            sum = sum + ints[i][i];
        }
        System.out.println("对角线各元素之和为:" + sum);
    }
}

4.找出4×5矩阵中值最小和最大的元素,并分别输出其值及所在的行号和列号。

import java.util.Scanner;

public class test04 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int[][] ints = new int[3][3];
        System.out.println("请输入一个4×5矩阵:");
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 5; j++) {
                ints[i][j] = scanner.nextInt();
            }
        }
        int MIN = ints[0][0];
        int MAX = ints[0][0];
        int row1 = 0, col1 = 0, row2 = 0, col2 = 0;
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 5; j++) {
                if (MIN > ints[i][j]) {
                    MIN = ints[i][j];
                    row1 = i;
                    col1 = j;
                }
                if (MAX < ints[i][j]) {
                    MAX = ints[i][j];
                    row2 = i;
                    col2 = j;
                }
            }
        }
        System.out.println("矩阵元素最小值:" + MIN + ",行号为:" + row1 + ",列号为:" + col1);
        System.out.println("矩阵元素最大值:" + MAX + ",行号为:" + row2 + ",列号为:" + col2);
    }

5.产生0~100的8个随机整数,并利用冒泡排序法将其升序后输出(冒泡排序算法:每次进行相邻两数的比较,若次序不对,则交换两数的次序)。

import java.util.Arrays;

public class test05 {
    public static void main(String[] args) {
        int[] ints = new int[8];
        for (int i = 0; i < 8; i++) {
            ints[i] = (int)(100 * Math.random());
        }
        System.out.println("产生的随机数为:");
        System.out.println(Arrays.toString(ints));
        int temp;
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 8; j++) {
                if (ints[i] < ints[j]) {
                    temp = ints[i];
                    ints[i] = ints[j];
                    ints[j] = temp;
                }
            }
        }
        System.out.println("冒泡升序之后的随机数为:");
        System.out.println(Arrays.toString(ints));
    }
}

6.有15个红球和15个绿球排成一圈,从第1个球开始数,当数到第13个球时就拿出此球,然后再从下一个球开始数,当再数到第13个球时又取出此球,如此循环进行,直到谨慎15个球为止,问怎样排才能使每次去除的球都是红球?

public class test06 {
    public static void main(String[] args) {
        int[] a = new int[31];
        int k = 1;
        for (int i = 1; i <= 15; i++) {
            for (int j = 1; j <= 13; j++, k++) {
                if (k > 30) k = 1;
                while (a[k] != 0) if (++k > 30) k = 1;
            }
            a[k - 1] = 1;
        }
        for (int i = 0; i <= 30; i++) System.out.print(" " + a[i]);
        System.out.println("\n 1:表示红球  0:表示绿球");
    }
}

7.编写Java应用程序,比较命令行中给出的两个字符串是否相等,并输出比较的结果。

public class test07 {
    public static void main(String[] args) {
        boolean result = false;
        if (args.length <= 1) System.out.println("命令行中数据的个数不对");
        else if (args[0].equals(args[1])) {
            result = true;
            System.out.println("命令行给出的两个字符串比较结果是:" + result);
        } else {
            System.out.println("命令行给出的两个字符串比较结果是:" + result);
        }
    }
}

8.从键盘上输入一个字符串和子串开始的位置与长度,截取该字符串的子串并输出。

import java.util.Scanner;

public class test08 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入一个字符串:");
        String str = scanner.nextLine();
        System.out.println("请输入子串开始的位置:");
        int beginIndex = scanner.nextInt();
        System.out.println("请输入子串的长度:");
        int length = scanner.nextInt();
        int endIndex = beginIndex + length;
        if (beginIndex -1 +length > str.length()) {
            System.out.println("数据输入有误!");
            return;
        }
        String sonStr = str.substring(beginIndex - 1, endIndex - 1);
        System.out.println("截取的子串为:" + sonStr);
    }
}

9.从键盘上输入一个字符串和一个字符,从该字符串中删除给定的字符。

import java.io.IOException;
import java.util.Scanner;

public class test09 {
    public static void main(String[] args) throws IOException {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入一个字符串:");
        String str = scanner.nextLine();
        System.out.println("请输入一个字符:");
//        char c = scanner.next().charAt(0);
        char c = (char)System.in.read();
        int len = str.length();
        String newStr = "";
        for (int i = 0; i < len; i++) {
            if (str.charAt(i) != c) {
                newStr = newStr + str.charAt(i);
            }
        }
        System.out.println("新字符串为:" + newStr);
    }
}

10.编程统计用户从键盘输入的字符串所包含的字母、数字和其他字符的个数。

import java.util.Scanner;

public class test10 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入一个字符串:");
        String str = scanner.nextLine();
        int len = str.length();
        int letter = 0;
        int character = 0;
        int other = 0;
        for (int i = 0; i < len; i++) {
            if (str.charAt(i) >= 'a' && str.charAt(i) <= 'z' || str.charAt(i) >= 'A' && str.charAt(i) <= 'Z') {
                letter++;
            } else if (str.charAt(i) >= '0' && str.charAt(i) <= '9') {
                character++;
            } else {
                other++;
            }
        }
        System.out.println("字母字符个数为:" + letter);
        System.out.println("数字字符个数为:" + character);
        System.out.println("其他字符个数为:" + other);
    }
}

11.将用户从键盘输入的每行数据都显示输出,知道输入“exit"字符串,程序运行结束。

public class test11 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String str;
        System.out.println("请输入字符串,输入 exit 退出:");
        do {
            str = scanner.nextLine();
            System.out.println(str);
        } while (!str.equals("exit"));
    }
}

第五章面试题:

1.String、StringBuffer与StringBuilder之间区别、以及使用场景?

(1)解释:

  • String:字符串常量

    • String类被final修饰,在Java中,被final修饰的类是不允许被继承的,并且该类中的成员方法都默认为final方法
    • String类底层是通过 char[] 数组来保存字符串
    • 对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象
  • StringBuffer:字符创变量

  • StringBuilder:字符创变量

三者在执行速度方面的比较:StringBuilder > StringBuffer > String

why?

String s = "abc";
s = s + "def";			// 实际上没有改变变量s
System.out.print(s); // result : abcdef

例如上面这段代码,我们明明就是改变了String型的变量 s 的,为什么说是没有改变呢? 其实这是一种障眼法。JVM是这样解析这段代码的:首先创建对象s,赋予一个abc,然后再创建一个新的对象 s 用来执行第二行代码,也就是说我们之前对象s并没有变化(new String(“abc”) 开辟的内存空间地址并没有变化),所以我们说String类型是不可改变的对象了,由于这种机制,每当用String操作字符串时,实际上是在不断的创建新的对象,而原来的对象就会变为垃圾被GC回收掉,可想而知这样执行效率会非常低。

而StringBuffer与StringBuilder就不一样了,他们是字符串变量,是可改变的对象,每当我们用它们对字符串做操作时,实际上是在一个对象上操作的,这样就不会像String一样创建一些而外的对象进行操作了,当然速度就快了。

(2)相同点:

  • jdk的实现中StringBuffer与StringBuilder都继承自AbstractStringBuilder。
    • AbstractStringBuilder的实现原理:我们使用StringBuffer等无非就是为了提高java中字符串连接的效率,因为直接使用 + 进行字符串连接的话,jvm会创建多个String对象,因此造成一定的开销。
    • AbstractStringBuilder中采用一个 char[] 数组来保存需要append的字符串, char[] 数组有一个初始大小,当append的字符串长度超过当前 char[] 数组容量时,则对 char[] 数组进行动态扩展,一般是申请当前2倍的内存空间。
  • StringBuffer 与 StringBuilder 中的方法和功能完全是等价的,只是StringBuffer 中的方法大都采用了 synchronized 关键字进行修饰,因此是线程安全的,而 StringBuilder 没有这个修饰,可以被认为是线程不安全的。

(3)不同点:

  • StringBuffer 线程安全;StringBuilder 线程不安全。
  • StringBuffer 始于 JDK 1.0,StringBuilder 始于 JDK 1.5,从 JDK 1.5 开始,带有字符串变量的连接操作(+),JVM 内部采用的是StringBuilder 来实现的,而之前这个操作是采用 StringBuffer 实现的。

(4)使用场景:

  • 如果要操作少量的数据用 = String

  • 单线程操作字符串缓冲区 下操作大量数据 = StringBuilder

  • 多线程操作字符串缓冲区 下操作大量数据 = StringBuffer

2.Java创建一个数组,在内存中的执行情况?

Java中,使用关键字new创建数组对象。格式为:new 数组元素的类型[数组元素的个数]


第六章、

1.类与对象的区别是什么?

在面向对象的程序设计语言里,类就是把事物的数据与相关功能封装在一起,形成一种特殊的数据结构,用以表达真实事物的一种抽象。类是由成员对象与成员方法封装而成的,其中,成员变量表示类的属性,成员方法表示类的行为。而对象则是该类事物具体的个体,也称为实例。所以说类描述了对象的属性和行为。

我的理解:

类是同类型事物的统称,是抽象的,该类事物具有共同属性。比如:水果,具有颜色属性、重量、价格属性。具有解渴、补充营养等行为。对象是类的实体,是具体的。比如:苹果、橘子、西瓜等。

2.如何定义一个类?类的结构是怎么样的?

(1)定义类要使用关键字class;

(2)类的内部既可以定于成员变量,也可以定义成员方法。其一般的语法结构如下:

[类修饰符] class 类名称 {
    [修饰符] 数据类型 成员变量名称;
    	...
    [修饰符] 返回值的数据类型 方法名(参数1, 参数2, 参数3, ...) {
        语句序列;
        return [表达式];
    } 
    	...
}

3.定义一个类时所使用的修饰符有哪几个?每个修饰符的作用是什么?是否可以混用?

(1)类修饰符分为公共访问修饰符、抽象类说明符、最终类说明符和缺省访问控制符四种。

(2)每个修饰符的作用:

  • 公共访问控制符:public,表示讲一个类声明为公共类。
  • 抽象类说明符:abstract,表示将一个类声明为抽象类。
  • 最终类说明符:final,表示将一个类生命为最终类。
  • 缺省访问控制符:表示只有在相同包中的对象才能访问该类。

(3)有些修饰符可混用,但abstract和final相互对立,所以不能同时应用在一个类的定义中。

4.成员变量的修饰符有哪些?各修饰符的作用是什么?是否可以混用?

(1)成员变量的修饰符有访问控制符、静态修饰符、最终修饰符、过渡修饰符和易失修饰符等。

(2)每个修饰符的作用:

  • 访问控制符,四个:

    • public:公共访问控制符,指定该变量为公共的;
    • private:私有访问控制符,指定该便令只允许自己类的方法访问,其他任何类(包括子类)中的方法均不能访问次变量;
    • protected:保护访问控制符,指定该变量只可以被它自己的累及其子类或同一包中的其他类访问;
    • 缺省:缺省访问控制符,表示在该成员变量只能被同一个包中的类所访问,而其他包中的类不能访问该成员变量。
  • 静态修饰符:static,指定该变量是隶属于类的变量,可被所有对象共享。

  • 最终修饰符:final,指定此变量的值不能改变。

  • 过度修饰符:transient,指定该变量是一个系统保留、暂无特别作用的临时性变量。

  • 易失修饰符:volatile,指定该变量可以同时被几个线程控制和修改。

(3)一个成员变量可以同时被两个以上的修饰符同时修饰,但有些修饰符是不能同时定义在一起的。

5.成员方法的修饰符有哪些?各修饰符的功能是什么?是否可以混用?

(1)成员方法的修饰符包括访问控制修饰符、静态修饰符、抽象修饰符、最终修饰符、同步修饰符和本地修饰符等。

(2)各修饰符的作用:

  • 访问控制修饰符,四个:
    • public:公共访问控制符,指定该方法为公共的;
    • private:私有访问控制符,指定该方法只允许自己类的方法访问,其他任何类(包括子类)中的方法均不能访问此方法;
    • protected:保护访问控制符,指定该方法只可以被它的类及其子类或同一包中的其他类访问;
    • 缺省:缺省访问控制符,表示该成员方法只能被同一个包中的类所调用,而其他包中的类不能访问该成员方法。
  • 静态修饰符:static,指定该方法是隶属于类的方法,可以直接使用类型调用该方法。
  • 抽象修饰符:abstract,指定该方法只声明方法头,而没有方法体,抽象方法需在子类中被覆盖并实现。
  • 最终修饰符:final,指定该方法不能被覆盖。
  • 同步修饰符:synchronized,在多线程程序中,该修饰符对于同步资源加锁。
  • 本地修饰符:native,指定此方法的方法体是用其他语言(如C)在程序外部编写的。

6.成员变量与局部变量的区别有哪些?

类的成员变量和方法中的局部变量的区别主要有如下几个方面:

  • 从语法形式上看,成员变量属于类的,而局部变量是在方法中定义的变量或是方法的参数。成员变量可以被public、private、static等修饰符所修饰,而局部变量则不能被访问控制符及static所修饰;成员变量和局部变量都可以被final所修饰;
  • 从变量在内存中的存储方式上看,成员变量是对象的一部分,而对象是存在于堆内存的,而局部变量是存在于栈内村的;
  • 从变量在内存中的生存时间上看,成员变量是对象的一部分,它随着对象的创建而存在,而局部变量随着方法的调用而产生,随着方法调用的结束而自动消失;
  • 成员变量如果没有被赋初值,则会自动以类型的默认值赋值;而局部变量则不会自动赋值,必须显示的赋值后才能使用。

7.创建一个对象使用什么运算符?对象实体和对象引用有何不同?

(1)声明对象时使用new运算符创建对象。

(2)创建的对象实体是存放在堆内存中的,而对象的引用变量是存放在栈内存中的,引用变量中存放的是对象在堆内存中的首地址。

8.对象的成员如何表示?

对象成员主要是通过对象名引用的。其格式如下:

对象名.对象成员

9.在成员变量或成员方法前加上关键字this表示什么含义?

在成员变量或成员方法前加上关键字this,表示强调对象本身的成员,即此时this表示调用此成员的对象。

10.什么是方法的返回值?返回值在类的方法里的作用是什么?

方法的返回值是指在方法体中进行的计算并能回传的值,其作用就是将其计算结果传回给调用该方法的语句。

11.在方法调用中,使用对象作为参数进行传递时,是“传值”还是“传址”?对象作为参数起到什么作用?

在方法调用中,使用对象作为参数进行传递时,即当参数是引用型的变量时,则是传址方式调用。对象作为参数传递给方法后,方法实际是通过引用变量对存放在堆中的对象进行操作的。所以其引用变量的值不会改变。

12.什么叫匿名对象?一般在什么情况下使用匿名对象?

(1)当一个对象被创建之后,在调用该对象的方法时,也可以不定义对象的引用变量,而直接调用这个对象的方法,这样的对象叫做匿名对象。

(2)使用匿名对象通常有如下两种情况:

  • 如果对一个对象只需进行一次方法调用,那么就可以使用匿名对象;
  • 将匿名对象作为实参传递给一个方法调用。

13.以m行n列二维数组为参数进行方法调用,分别计算二维数组各列元素之和,返回并输出所计算的结果。

public class SumNum {
    public int[] colNum(int[][] array) {
        int[] ints = new int[array[0].length];
        for (int i = 0; i < array[0].length; i++) {
            for (int j = 0; j < array.length; j++) {
                ints[i] = ints[i] + array[j][i];
            }
        }
        return ints;
    }
}
import java.util.Scanner;

public class test13 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入二维数组的行数m:");
        int m = scanner.nextInt();
        System.out.println("请输入二维数组的列数n:");
        int n = scanner.nextInt();
        System.out.println("请输入一个" + m + "*" + n + "的二维数组:");

        int[][] ints = new int[m][n];
        int[] col;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                ints[i][j] = scanner.nextInt();
            }
        }
        SumNum sumNum = new SumNum();
        col = sumNum.colNum(ints);
        for (int i = 0; i < col.length; i++) {
            System.out.println("第" + (i + 1) + "列数的和 = " + col[i]);
        }
    }
}

七、Java语言类的特性

1.一个类的公共成员和私有成员有何区别?

公共成员不仅可以被该类自身访问,还可以被所有其他的类所访问;私有成员无法从该类的外部访问到,而只能被该类的自身所访问和修改,而不能被任何其他类(包括该类的子类)获取或引用。

2.什么是方法的重载?

在面向对象的程序设计语言中,有一些方法的含义相同,但带有不同的参数,这些方法使用相同名字,这就叫方法的重载(Overload)。

  • 换句话说,重载是指在同一个类内具有相同名称的多个方法,这些同名方法如果参数个数不同,或者参数个数相同但类型不同,则这些同名的方法就具有不同的功能,这就是方法的重载。

  • 方法的重载中参数的类型是关键,仅仅参数的变量名不同是不行的,也不允许参数个数或参数类型完全相同而只有返回值类型不同的重载。

    方法重载的条件:

    条件一:在同一个类中

    条件二:方法名相同

    条件三:形参列表不同(个数,类型)

  • 什么是方法的重写?子类继承父类以后,继承过来的方法不能满足子类当前的业务需求,子类有权利对这个方法进行重新编写,这就是方法的重写(又称为方法的覆盖)。

    方法重写的条件:

    条件一:两个类要有继承关系
    条件二:重写后的方法和之前的方法具有:相同的返回值类型、相同的方法名、相同的形参列表
    条件三:重写的方法的访问权限不能更低
    条件四:重写之后的方法抛出的异常类型不能大于父类抛出的异常类型

3.一个类的构造方法的作用是什么?若一个类没有声明构造方法,该程序能正确执行吗?为什么?

(1)构造方法是一种特殊的、与类名相同的方法,分为无参构造方法和有参构造方法,作用是专门用于创建对象时,完成初始化工作;

(2)若一个类没有声明构造方法,依然可以创建新的对象,并能正确执行程序,这是因为如果省略构造方法,Java编译器会自动为该类生成一个默认的构造方法,程序在创建对象时会自动调用默认的构造方法。默认的构造方法没有参数,在其方法体中也没有任何代码,即什么都不做。

4.构造方法有哪些特性?

构造方法的特殊性主要包括如下几方面:

  • 构造方法的方法名与类型相同;
  • 构造方法没有返回值,但不能些void;
  • 构造方法的主要作用是完成对类对象的初始化工作;
  • 构造方法一般不能由编程人员显式地直接调用,而是用new来调用;
  • 在创建一个类的对象的同时,系统对自动调用该类的构造方法,完成新对象的初始化。

5.在一个构造方法内可以调用另一个构造方法吗?如果可以,如何调用?

(1)如果一个类定义了多个构造方法,则在类内从某一个构造方法内可以调用另一个构造方法;

(2)在某一个构造方法内调用另一个构造方法,是在第1行中使用语句 this() 来调用另一个构造方法的。

6.静态变量与实例变量有哪些不同?

  • 实例变量是隶属于对象的,而每个不同的对象均有各自的存储空间来保存自己的值,而不与其他对象共享,也就是说不同对象的成员变量各自独立,且存在不同的内存之中;

  • 静态变量是里属于类的变量,而不是属于任何一个类的具体对象。也就是说,对于该类的任何一个具体对象而言,静态变量是一个公共的存储单元,不是保存在某个对象的内存空间中,而是保存在类的内存空间的公共存储单元中,所以任何一个对象对静态变量进行修改,该类的所有对象的这个静态变量均被修改。实例变量必须由对象名调用,而静态变量即可用对象名调用,也可用类名调用。

7.静态方法和实例方法有哪些不同?

与静态变量的意义相同,今天方法也是隶属于整个类的方法,所以静态方法不但可以通过对象名来调用,也可以直接用类名来调用。而实例方法是属于某个具体对象的方法,所以必须先创建对象,然后再通过对象名来访问实例方法。

8.在一个静态方法内调用一个非静态成员为什么是非法的?

由于静态是属于整个类的,所以它不能操纵和处理某个对象的成员,而只能处理属于整个类的成员,即静态方法只能访问静态成员变量或调用静态成员方法,所以在静态方法中不能访问实例变量与实例方法。由于非静态成员只能通过对象来访问,因此可以现在静态方法中创建该类的一个对象,然后再通过该对象名调用静态成员进行访问。例如:

public class Test {
    public static void main(String[] args) {
        Test test = new Test();
        System.out.println(test.sayHello());
    }

    public String sayHello() {
        return "Hello";
    }
}

9.对象的相等与指向他们的引用相同有什么不同?

对象的相等一般指对象本身所包含的内容相等,指向对象的引用相等则是指指向对象的首地址相同,两者有着本质的不同。

10.什么是静态初始化器?其作用是什么?静态初始化器由谁在何时执行?它与构造方法有何不同?

(1)静态初始化器是由关键字 static 修饰的一对花括号“{ }”括起来的语句组;

(2)静态初始化器的作用是对类自身进行初始化;

(3)静态初始化器在所属的类被加载入内存时由系统自动调用执行;

(4)静态初始化器与构造方法的不同主要有如下几个方面:

  • 构造方法是对每个新创建的对象初始化,而静态初始化器是对类自身进行初始化;
  • 构造方法是在用new运算符创建新对象时由系统自动调用执行,而静态初始化器不能由程序来调用,它是在所属的类被加载入内存时由系统自动调用执行;
  • 用new运算符创建多少个新对象,构造方法就会被调用多少次,而静态初始化器在类被加载入内存时只执行一次,与创建多少个对象无关。
  • 不同于构造方法,静态初始化器不是方法,因为没有方法名、返回值和参数。

11.Java语言怎样清除没有被引用的对象?能否控制Java系统中垃圾回收器的时间?

(1)Java运行环境提供了一个垃圾回收器县城,负责自动回收哪些没有被引用的对象所占用的内存,这种清除无用对象进行内存回收的过程叫做垃圾回收。

(2)垃圾回收器负责释放没有引用与之关联的对象所占用的内存,但是在任何时候,程序员都不能通过程序强迫垃圾回收器立即执行。


八、继承、抽象类、接口和枚举

1.子类将继承父类的所有成员吗?为什么?

子类可以从父类那里继承所有非private的成员作为自己的成员。因为父类中所有的private成员只允许父类自己的方法访问。

2.在子类中可以调用父类的构造方法吗?若可以,如何调用?

(1)在子类的构造方法中,可以调用父类的构造方法。

(2)调用方式是在子类构造方法的第一行使用super()语句来调用父类特定的构造方法。例如:

public class Fathtr {
    public Fathtr() {
    }
}
public class Son extends Fathtr {
    public Son() {
        super();
    }
}

3.在调用子类的构造方法之前,若没指定调用父类的特定构造方法,则会先自动调用父类中没有参数的构造方法,其目的是什么?

Java程序在执行子类的构造方法之前,若在子类的构造方法中没有用super()语句调用父类中特定的构造方法,则会先自动调用父类中没有参数的构造方法,其目的是为了帮助继承自父类的成员做到初始化化的操作。(tips:不先调用父类的无参构造,子类从父类继承的得到成员就没初始化。)

4.在子类中可以访问父类的成员吗?若可以,用什么方式访问?

(1)在子类中使用关键字super可以访问父类中的非private成员;

(2)在子类中访问父类成员的格式如下:

super.变量名;
super.方法名();

5.用父类对象变量可以访问子类的成员方法吗?若可以,则只限于什么情况?

通过父类的对象访问子类的成员方法,只限于“覆盖”的情况放生时。也就是说,父类与子类的方法名、参数个数与类型必须完全相同,才可以通过父类的对象调用子类的方法。

6.什么是多态机制?Java语言中是如何实现多态的?

(1)多态是指一个程序中同名的多个方法共存的情况;

(2)Java语言的多态主要是通过方法重载和覆盖的方式来实现的。

多态存在的三个必要条件:

  • 要有继承
  • 要有重写
  • 父类引用指向子类对象

7.方法的覆盖与方法的重载有何不同?

(1)重载,是指在同一个类内定义名称相同、但参数个数或类型不同的过个方法,Java虚拟机可以根据参数的个数或类型的不同来调用相应的方法;

(2)覆盖,是指在子类中定义名称、参数个数与类型均与父类完全相同的方法,用以重写父类里同名方法的功能。覆盖又称为方法重写。

8.this和super分别有什么特殊的含义?

  • 如果要强调是对象本身的成员,则可以在成员名前加this,级“this.成员名”,此时this表示调用此成员的对象,如果同一类的成员变量与局部变量的名称相同,也可以利用this来调用同一类内的成员变量,另外可以在构造方法内用this()来调用同一类内的其他构造方法;

  • 在子类中可以用“super.成员名”的形式来访问父类中的成员变量活成员方法,还可以在子类的构造方法中利用super()语句来调用父类的构造方法。

9.什么是最终类与最终方法?他们的作用是什么?

(1)如果一个类被final修饰符所修饰,则说明这个类不能再被其他类所继承,即该类不可能有子类,这种类被称为最终类;如果一个成员方法用final修饰,则该成员方法不能再被子类的同名方法所覆盖,即该方法为最终方法。

(2)最终类与最终方法主要用于增加代码的安全性。

10.什么是抽象类与抽象方法?使用时应注意哪些问题?

  • Java语言的抽象类是用abstract修饰符来修饰的类,抽象类是专门用来当做父类的。抽象类类似“模板”的作用,其目的是要用户根据它的格式来修改并创建新的类。抽象类的方法可以分为两种:一种是一般方法;另一张是以关键字abstract开头的抽象方法。抽象类中不一定包含抽象方法,但包含抽象方法的类一定要声明为抽象类。

  • 抽象类本身不具备实际的功能,只能用于派生其子类,而定义为抽象的方法必须在子类派生时被覆盖。所以说一个类被定义为抽象类,则该类就不能用new运算符创建对象,而必须通过覆盖的方式来实现抽象类中的方法。一个类不能既是最终类,又是抽象类,即关键字abstract与final不能合用,另外,abstract不能与private、static或native并列修饰同一种方法。

11.什么是接口?为什么要定义接口?

(1)接口是一个特殊的数据结构,他的结构与抽象类非常相似。接口本身也具有数据成员、抽象方法、静态方法和默认方法。

(2)Java语言定义接口的目的主要是可以帮助实现类似于类的多重继承功能。

12.如何定义接口?接口与抽象类有哪些异同?

(1)接口定义的语法结构如下:

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

(2)接口与抽象类对比:

  • 相同点:都具有成员;
  • 不同点:一是接口的数据成员必须是静态的且一定要初始化化,且此值不能再被修改,若省略数据成员的修饰符,则系统默认为public static final,而抽象类的数据成员则可以不进行初始化;二是接口中的方法除了用abstract声明的抽象方法外还可以有默认方法和静态方法,但不能有一般方法,若抽象方法前省略了修饰符,系统默认为public abstract;而抽象类中可以定义一般方法,也可以声明abstract方法。

13.在多个父接口的实现类中,多个接口中的方法名冲突问题有几种形式?如何解决?

如果一个类实现了两个接口,其中一个接口有默认方法,另一个接口中也有个名称和参数都相同的方法(默认或非默认方法),此时发生方法名冲突,要解决方法名冲突问题,有如下几种情况:

(1)在多个父接口的实现中解决同名默认方法的名字冲突问题有两种方法:一种是提供同名方法的一个新实现;另一种是委托一个父接口的默认方法;

(2)如果有两个父接口中有一个提供的不是默认方法,而是抽象方法,则只需在接口的实现类中提供同名方法的一个新实现或者委托父接口中的默认方法;

(3)如果两个父接口中的同名方法都是抽象方法,则不会发生名字冲突,实现接口的类可以实现该同名方法即可,或者不实现该方法而将自己也声明为抽象类;

(4)如果一个类继承一个父类并实现一个接口,而从父类和接口中继承了同名的方法,此时采用“类比接口优先”的原则,即只继承父类的方法,而忽略来自接口的默认方法。

14.编程题:定义一个表示一周七天的枚举,并在主方法main()中遍历枚举的所有成员。

enum Week {
    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FIRDAY,
    SATURDAY,
    SUNDAY
}
public class Test8_14 {
    public static void main(String[] args) {
        Week day = Week.MONDAY;
        Week day1 = Week.valueOf("MONDAY");
        System.out.println(day);
        System.out.println(" " + day1);
        for (Week d: Week.values())
        System.out.println("序号:" + d.ordinal() + " 的值为:" + d.name());
    }
}

15.什么是包?他的作用是什么?如何创建包?如何引用包中的类?

(1)所谓的包就是Java语言提供的一种区别于类名空间的机制,是类的组织方式。每个包对应一个文件夹,包中还可以再有包,称为包等级。

(2)若要创建自己的包,就必须以package语句作为Java源文件的第一条语句,知名该文件中定义的类所在的包,并将源文件存放到该包所对应的文件夹中。

(3)如果要使用Java包中的类,必须在源程序中用import语句导入所需要的包和类。


九、异常处理

1.什么是异常?简述Java语言的异常处理机制?

(1)异常是指程序在运行过程中发生由于算法考虑不周全活软件设计错误等导致的程序异常事件,这个事件将中断程序的正常执行。

(2)Java语言的异常处理机制是程序执行时发现异常的代码可以抛出一个异常,运行系统捕获该异常,并交由程序员编写的响应代码进行异常处理。

2.Throwable类的两个直接子类Error和Exception的功能各是什么?用户可以捕获到的异常是哪个类的异常?

(1)Error类及子类的对象是由Java虚拟机生成并抛出给系统,这种错误有内存溢出错误、栈溢出错误、动态链接错误等。通常Java程序不对这种错误进行直接处理,必须交由操作系统处理,而Exception子类则是供应用程序使用的,它是用户程序能捕捉到的异常情况。

(2)用户可以捕获到的异常通常是指由Exception类及其子类所产生的异常。

3.Exception类有何作用?Exception类的每个子类对象代表了什么?

Exception类对象是Java程序抛出和处理的对象,Exception类有各种不同的子类分别对应于各种不同类型的异常类。每个异常类的对象都代表一种运行错误,异常类对象中包含了该运行错误的信息和处理错误的方法等内容。

4.什么是运行时异常?什么是非运行时异常?

(1)在Exception类中有一个子类RuntimeException代表运行时异常,它是程序运行时自动地对某些错误做出反应而产生的,所以RuntimeException可以不编写异常处理的程序代码,依然可以成功编译,因为它是在程序运行时才有可能产生,这类异常应通常通过程序调试尽量避免,而不是使用try-catch-finally语句去捕获它;

(2)除RuntimeException之外,其他则是非运行期异常,这种异常经常是在程序运行过程中,由环境原因造成的异常,这类异常必须在程序中使用try-catch-finally语句去捕获它,并进行相应的处理,否则编译不能通过。

5.抛出异常有哪两种方式?

(1)系统自动抛出异常。所有系统定义的运行时异常都可以由系统自动抛出。

(2)指定方法抛出异常。指定方法抛出异常需要使用关键字throw或throws来明确指定在方法内抛出异常。

6.在捕获异常时,为什么要在catch()括号内有一个变量e?

catch()后面的括号内的异常类后边都有一个变量,该变量一般用字母e表示,但用其他字母也可以,其作用是如果捕获到异常,Java系统会创建该异常类的一个对象然后传递给catch(),变量e的作用就是接收这个异常类对象,录用此变量e便能进一步提取有关异常的信息。例如:

public class ExceptionTest {
    public static void main(String[] args) {
        int a = 100;
        int b = 0;
        try {
            System.out.println(a/b);
        } catch (Exception e) {
            e.printStackTrace();    // 在命令行打印异常信息在程序中出错的位置及原因
        }
        System.out.println("方法结束");
    }
}

7.在异常处理机制中,用catch()括号内的变量e接收异常类对象的步骤有哪些?

可以将catch()括号里的内容想象成是方法的参数,因此变量e就是相应异常类的变量,变量e接收到由异常类所产生的对象之后,利用此变量便能进一步提取有关异常的信息,然后进入到相应的catch()块进行处理。

8.在什么情况下,方法的头部必须列出可能抛出的异常?

如果不想在当前方法中使用try-catch语句来处理异常,也没有在方法内使用throw语句说明抛出异常,则必须在方法声明的头部使用throws语句列出可能抛出的异常。

9.若try语句结构中有多个catch()字句,这些子句的排列顺序与执行效果是否有关?为什么?

(1)有关。

(2)由于异常对象与catch块的匹配是按照catch块的先后排列顺序进行的,所以在处理多异常时应注意认真设计各catch块的排列顺序。一般地,将处理较具体、较常见异常的catch块应放在前面,而可以与多种异常类型相匹配的catch块应放在较后的位置。若将子类异常的catch语句放在父类异常catch语句块的后面,则编译不能通过。

10.什么是抛出异常?系统定义的异常如何抛出?用户自定义的异常又如何抛出?

(1)在一个程序运行过程中,如果发生了异常事件,则产生代表该异常的一个异常对象,并把它交给运行系统,再由运行系统寻找响应的代码来处理这一异常,生成异常对象并把它提交给运行系统的过程称为抛出异常。

(2)所有系统定义的异常都可以由系统自动抛出或使用关键字throw或throws来明确在指定方法内抛出异常。

(3)用户自定义的异常不可能依靠系统自动抛出,这种情况必须借助于throw或throws语句抛出用户自定义的异常。

11.自动关闭资源语句,为什么只能关闭实现java.lang.AutoCloseable接口的资源?

因为java.lang.AutoCloseable接口中只包含一个抽象方法close(),实现AutoCloseable接口的类都实现了close()方法。若在自动关闭资源的try-with-resources语句中所使用的是实现了AutoCloseable接口的资源res,则在退出该语句块时会自动调用res.close()方法关闭资源。

12.系统定义的异常与用户自定义的异常有何不同?如何使用这两类异常?

(1)系统定义的异常类主要用来处理系统可以预见的较常见的运行错误,对于某个应用程序所特有的运行错误,则需要编程人员根据程序的特殊逻辑关系,在用户程序中自己创建用户自定义的异常类和异常对象。

(2)这两类异常的使用方法基本相同,只是用户自定义异常不能由系统自动抛出,因而必须借助于throw和throws语句来定义何种情况算是传神了此种异常对应的错误,并应该抛出这个异常类的对象。


十、Java语言的输入输出与文件处理

1.什么是数据的输入与输出?

将数据从外设或外存(如屏幕、打印机、文件等)传递到应用程序称为数据的输入;将数据从应用程序传递到外设或外存称为数据的输出。

2.什么是流?Java语言中分为哪两种流?这两种流有何差异?

(1)流是指计算机各部件之间的数据流动。

(2)按数据的传输方向,Java语言将流分为输入流与输出流良两种;从流的内容上划分,流分为字节流和字符流。字节流每次读写8位二进制数,由于它只能将数据以二进制的原始方式读写,因此字节流又被成为二进制字节流(binary byte stream)或位流(bits stream);而字符流一次读写16位二进制数,并将其作为一个字符而不是二进制位来处理。

3.InputStream和OutputStream、Reader和Writer四个类在功能上有何异同?

InputStream和OutputStream类是Java语言里用来处理以位(bit)为主的字节流,它除了可用来处理二进制文件(图片、音频、视频等)数据之外,也可用来处理文本文件;Reader和Writer类则是用来处理字符流的,也就是文本文件。

4.利用基本输入输出流实现从键盘上读入一个字符,然后显示在屏幕上。

import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class StreamTest {
    public static void main(String[] args) {
        char ch;
        FileInputStream fin = new FileInputStream(FileDescriptor.in);
        FileOutputStream fout = new FileOutputStream(FileDescriptor.out);
        System.out.println("请输入一个字符:");
        try {
            ch = (char) fin.read();
            System.out.println("您输入的字符为:");
            fout.write(ch);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

5.顺序流与管道流的区别是什么?

(1)顺序输入流SequenceInputStream是InputStream的直接子类,其功能是将多个输入流顺序连接在一起,形成单一的输入数据流,没有对应的输出数据流存在;

(2)管道流用来将一个程序或线程的的输出连接到另一个程序或线程作为输入,使得相连线程能够通过PipedInputStream和PipedOutputStream流进行数据交换,从而可以实现程序内部线程间的通信或不同程序间的通信。

6.Java语言中定义的三个标准输入输出流是什么?他们对应什么设备?

(1)Java语言中定义的三个标准输入输出流分别是System.in、System.out和System.err。

(2)其中,System.in对应于输入流,通常指键盘输入设备;System.out对应于输出流,指显示器、打印机或磁盘文件等信息输出设备;System.err对应于标准错误输出设备,通常是显示器。

7.利用文件输出流创建一个文件file.txt,写入字符“文件已被成功创建!”,然后用记事本打开该文件,看一下是否正确写入。

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class FileTest {
    public static void main(String[] args) throws IOException {
        File file = new File("D:/个人文件/file.txt");
        file.createNewFile();
        FileWriter fw = new FileWriter(file);
        fw.write("文件已被成功创建!");
        fw.close();
    }
}

8.利用文件输入流打开10.7题中创建的文件file.txt,读出其内容并显示在屏幕上。

import java.io.FileReader;
import java.io.IOException;

public class FileReaderTest {
    public static void main(String[] args) throws IOException {
        char[] c = new char[500];
        FileReader fileReader = new FileReader("D:/个人文件/file.txt");
        int num = fileReader.read(c);
        String str = new String(c, 0, num);
        System.out.println(str);
        fileReader.close();
    }
}

9.利用文件输入流输出流打开1.7题创建的文件file.txt,然后在文件的末尾追加一行字符串“又添加了一行文字!”。

import java.io.FileWriter;
import java.io.IOException;

public class FileWriterTest {
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("D:/个人文件/file.txt", true);
        fw.write("\r\n又添加了一行文字!");
        fw.close();
    }
}

10.产生15个20~9999的随机整数,然后利用BufferedWriter类将其写入文件file2.txt中,之后再读取该文件中的数据并将它们升序排序。

import java.io.*;
import java.util.Arrays;
import java.util.Random;

public class BufferedWriterTest {
    public static void main(String[] args) throws IOException {
        Random rand = new Random();
        BufferedWriter output = new BufferedWriter(new FileWriter("D:/个人文件/file2.txt"));
        int[] num1 = new int[15];
        for (int i = 0; i < num1.length; i++) {
            num1[i] = 20+rand.nextInt(9979);
            output.write(num1[i] + "");
            output.newLine();
        }
        output.close();

        BufferedReader input = new BufferedReader(new FileReader("D:/个人文件/file2.txt"));
        int[] num2 = new int[15];
        for (int i = 0; i < num2.length; i++) {
            num2[i] = Integer.parseInt(input.readLine());
        }
        input.close();
            Arrays.sort(num2);
        for (int j : num2) {
            System.out.println(j);
        }
    }
}

11.Java语言中使用什么类来对文件与文件夹进行管理?

Java语言中使用File类和RandomAccessFile类来对文件与文件夹进行管理和对文件进行随机访问。


十一、多线程

1.简述线程的基本概念。程序、进程、线程的关系是什么?

进程是一个执行中的程序,但线程是一个比进程更小的执行单位。

  • 程序:是含有指令和数据的文件,被存储在磁盘或其他的数据存储设备中,程序是静态的代码;

  • 进程:是程序的一次执行过程,是系统运行程序的基本单位,进程是动态的;

  • 线程:一个进程在其执行过程中可以产生多个线程,形成多条执行路径。

    • 线程是程序执行时的最小单位,它是进程的一个执行流,是CPU调度和分派的基本单位;
    • 一个进程可以由很多个线程组成,线程间共享进程的所有资源,每个线程有自己的堆栈和局部变量。

2.什么是多线程?为什么程序的多线程功能是必要的?

(1)所谓的多线程就是同时执行一个以上的线程,执行一个线程时不必等待另一个线程执行完后才进行,所有线程都可以发生在同一时刻;

(2)由于每一个进程的内部数据和状态都是完全独立的,所以即使它们是同一个程序所产生的,也必须重复许多的数据复制工作,而且在交换彼此的数据时,也要再使用一些进程间通信的机制,这样就增加了系统负担。由于同一个进程的各个线程之间可以共享相同的内存空间,并利用这些共享内存来完成数据交换、实时通信及必要的同步工作,所以各线程之间的通信速度很快,线程之间进行切换所占用的系统资源也比较少,所以引用多线程功能是必要的。

3.多线程与多任务的差异是什么?

多任务与多线程是两个完全不同的概念。

  • 多任务是针对操作系统而言的,表示操作系统可以同时运行多个应用程序;
  • 多线程是指一个进程而言的,表示在一个进程内部可以同时执行多个线程。

4.线程有哪些基本状态?这些状态是如何定义的?

线程有五种基本状态,分别是:

(1)新建状态:是当一个Thread类或其子类的对象被声明并创建,但还未被执行的这段时间里,处于一种特殊的新建状态;

  • 用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新建状态。处于新建状态的线程有自己的内存空间,通过调用start方法进入就绪状态(runnable);
  • 不能对已经启动的线程再次调用start()方法,否则会出现Java.lang.IllegalThreadStateException异常。

(2)就绪状态:是指处于新建状态的线程被启动后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件;

  • 处于就绪状态的线程,此时还没有分配到CPU的控制权,处于线程就绪队列(尽管是采用队列形式,事实上,把它称为可运行池而不是可运行队列,因为cpu的调度不一定是按照先进先出的顺序来调度的);
  • 等待状态并不是执行状态,当系统选定一个等待执行的Thread对象后,它就会从等待执行状态进入执行状态,系统挑选的动作称之为“cpu调度”。一旦获得CPU,线程就进入运行状态并自动调用自己的run方法。

(3)运行状态:表示线程正在运行,该线程已经拥有了对CPU的控制权;

  • 执行run()方法中的任务;

(4)阻塞状态:是指一个正在执行的线程如果在某些特殊的情况下,将让出CPU并暂时中止自己的执行;

(5)消亡状态:是指线程不具有继续运行的能力。

5.Java程序实现多线程有了那两个途径?

Java语言中实现多线程的方法有两种:一中是继承java.lang包中的Thread类;另一种是用户在定义自己的类时实现Runnable接口。无论用哪种方式实现多线程,都需要将要执行的任务代码编写在run()方法中,然后启动线程从run()方法开始执行。

public class MyThread extends Thread {
    private int ticket = 10;

    public void run() {
        for (int i = 0; i < 20; i++) {
            if (this.ticket > 0) {
                // this.getName()获取当前线程的名称
                System.out.println(this.getName() + " 卖票:ticket" + this.ticket--);
            }
        }
    }
}
public class ThreadTest {
    public static void main(String[] args) {
        // 启动3个线程t1,t2,t3;每个线程各卖10张票!
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        MyThread t3 = new MyThread();
        t1.start();
        t2.start();
        t3.start();
    }
}

MyThread继承于Thread,它是自定义个线程。每个MyThread都会卖出10张票。

主线程main创建并启动3个MyThread子线程。每个子线程都各自卖出了10张票。

6.在什么情况下,必须以类实现Runnable接口来创新建线程?

如果一个类本身已经继承了某个父类,由于Java语言不允许类的多重继承,所以就无法再继承Thread类,特别是小程序,这种情况下若要实现多线程的功能,可以创建一个类,该类必须实现Runnable接口。

7.什么是线程的同步?程序中为什么要实现线程的同步?是如何实现同步的?

(1)同步指的是处理数据的线程不能处理其他线程当前还没有处理完毕的数据;

(2)程序要实现线程的同步的原因是因为多个线程之间共享数据时会使共享的数据不安全或不符合逻辑;

(3)要实现对共享数据的同步,当一个线程对共享的数据进行操作时,应使之成为一个“原子操作”,即在没有完成相关操作之前,不允许其他线程打断它,具体的办法就是使用关键字synchronized来标识同步资源,这样各线程就可互斥地操作该同步资源。

8.假设某家银行可接受顾客的存款,每次进行一次存款,便可计算出存款的总额。现有两名顾客,每人分三次,每次存入100元。试编程来模拟顾客的存款操作。

public class Bank {
    public static int sum = 0;
    public synchronized static void add(int n) {
        sum = sum + n;
        System.out.println("Sum = " + sum);
    }
}
public class Customer extends Thread {
    @Override
    public void run() {
        for (int i = 0; i <= 3; i++) {
            Bank.add(100);
        }
    }
}
public class Test {
    public static void main(String[] args) {
        Customer c1 = new Customer();
        Customer c2 = new Customer();
        c1.start();
        c2.start();
    }
}

如果Bank类中add方法没有被synchronized修饰,那存款不是按照100、200、300、…、600的顺序来的。


十二、泛型与容器类

1.什么是泛型的类型参数?泛型的主要优点是什么?在什么情况下使用泛型方法?泛型类与泛型方法传递类型实参的主要区别是什么?

(1)泛型的实质是指参数化类型的能力。泛型是在定义类、接口或方法时通过为其增加“类型参数”来实现的,即泛型所操作的数据类型被指定为一个参数,这个参数被称为类型参数(Type Parameters)。

(2)使用泛型主要有两个优点:一是泛型使一个类或一个方法可在多种不同类型的对象上进行操作,运用泛型意味着编写的代码可以被很多类型不同的对象所重用。二是能够在编译时而不是在运行时检测出错误,从而减少数据类型转换的潜在错误。

(3)设计泛型方法的目的主要是针对具有容器类型参数的方法的,如果编写的代码并不接受和处理容器类型,就根本不需要使用泛型方法。

(4)泛型方法与泛型类在传递类型实参方面的一个重要差别是,对于泛型方法,不需要吧实际的参数传递给泛型方法,但泛型类却恰恰相反,及必须把实际的类型参数传递给泛型类。

2.已知Integer是Number的子类,GeneralType<Integer>是GeneralType<Number>的子类吗?GeneralType<Object>是GeneralType<T>的父类吗?

虽然Integer是Number的子类,但GeneralType却不是GeneralType的子类,即他们之间没有父子关系,因为在利用泛型进行实例化时,虽然泛型的实际参数的类之间有父子关系,但是参数化后得到的泛型类之间并不具有同样的父子关系。同理可知GeneralType也不是GeneralType的父类。

3.在泛型中,类型通配符的主要作用是什么?

类型通配符 “ ? ” 的主要作用有两个方面:一是用于创建可重新复制但不可修改其内容的泛型对象;二是用在方法的参数中,限制传入不想要的类型实参。

4.分别简述LinkedList与ArrayList、HashSet与TreeSet、HashMap与TreeMap有何异同。

(1)LinkedList与ArrayList都是线性表,且元素可以重复,也可以是空值、null。LinkedList链表类采用链表结构保存对象,使用循环双链表实现List。这种结构在向链表中任意位置插入、删除元素时时不需要移动其他元素,链表的大小是可以动态增加或减小的,但不具有随机存取特性。ArrayList数组列表类使用一维数组实现List,该类实现的是可变数组,允许所有元素,包括null;具有随机存取特性,插入、删除元素时需要移动其他元素,当元素很多时插入、删除操作的速度较慢。

(2)HashSet与TreeSet中的元素不能重复。HashSet根据哈希码来确定元素在集合中的存储位置,HashSet类不保证迭代顺序,但允许元素值为null。TreeSet中的元素总是处于有序状态。

(3)HashMap和TreeMap中的元素提供了键(key)到值(value)的映射,键决定了元素的存储位置,一个键和它所对应的值构成一个条目,及“键-值”对,真正存储的是这个条目。HashMap类是基于哈希表的Map接口的实现,因此对于添加和删除映射关系效率较高,并且允许使用null值和null建,但必须保证键的唯一性;而TreeMap类中的映射关系存在一定的顺序,由于TreeMap类实现的Map集合中的映射关系是根据键对象按照一定的顺序排列的,因此不允许键对象是null。

5.将1~10的整数存放到一个线性表LinkedList的对象中,然后将其下标为4的元素从列表中删除。

import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;

public class Test_12_5 {
    public static void main(String[] args) {
        List<Integer> list = new LinkedList<>();
        for (int i = 0; i < 10; i++) {
            list.add(i);
        }
        System.out.println("链表中的数据:" + list);
        list.remove(4);
        ListIterator<Integer> listIterator = list.listIterator();
        System.out.print("删除元素后的数据:");
        while (listIterator.hasNext()) {
            System.out.print(listIterator.next() + " ");
        }
    }
}

6.利用ArrayList类创建一个对象,并向其添加若干个字符串型元素,然后随机选一个元素输出。

import java.util.ArrayList;
import java.util.List;

public class Test_12_6 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("西施");
        list.add("貂蝉");
        list.add("昭君");
        list.add("玉环");
        int i = (int) (Math.random()*(list.size() -1));
        list.remove(i);
        System.out.println("最优雅的古典美女是:" + list.get(i));
    }
}

7.已知集合A={1,2,3,4}和B={1,3,5,7,9,11},编程求A与B的交集、并集和差集。

import java.util.HashSet;
import java.util.Set;

public class Test_12_7 {
    public static void main(String[] args) {
        boolean yn;
        Set<Integer> a = new HashSet<>();
        Set<Integer> b = new HashSet<>();

        for (int i = 1; i <= 4; i++) {
            a.add(i);
        }
        for (int i = 1; i <= 11; i = i + 2) {
            b.add(i);
        }
        yn = a.retainAll(b);    // a.retainAll(b),取交集,放到集合a中,返回boolean
        System.out.println("A于B的交集:" + a);

        for (int i = 1; i <= 4; i++) {
            a.add(i);
        }
        yn = a.addAll(b);   // a.addAll(b),取并集,并去掉重复值,放到集合a中,返回boolean
        System.out.println("A于B的并集:" + a);

        for (int i = 1; i <= 4; i++) {
            a.add(i);
        }
        yn = a.removeAll(b);
        System.out.println("A于B的差集:" + a);  // a.removeAll(b),取差集,方法集合a中,返回boolean
    }
}

8.利用随机函数生成10个随机数,并将它们存入到一个HashSet对象中,然后利用迭代器输出。

import java.util.HashSet;
import java.util.Iterator;
import java.util.Random;
import java.util.Set;

public class Test_12_8 {
    public static void main(String[] args) {
        Set<Integer> set = new HashSet<>();
        Random random = new Random();
        while (set.size() < 10) {
            int num = random.nextInt() % 100;
            set.add(num);
        }

        Iterator<Integer> iterator = set.iterator();
        while (iterator.hasNext()) {
            System.out.print(iterator.next() + " ");
        }
    }
}

9.利用随机函数生成10个随机数,并将它们有序地存入到一个TreeSet对象中,然后利用迭代器有序地输出。

import java.util.*;

public class Test_12_9 {
    public static void main(String[] args) {
        Set<Integer> set = new TreeSet<>();
        Random random = new Random();
        for (int i = 0; i < 10; i++) {
            set.add(random.nextInt() % 100);
        }
        Iterator<Integer> iterator = set.iterator();
        for (int i = 0; i < set.size(); i++) {
            System.out.print(iterator.next() + " ");
        }
    }
}

10.利用HashMap类对象存储公司电话号码簿,其中包含公司的电话号码和公司名称,然后进行删除一个公司和查询一个公司的操作。

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class Test_10_9 {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("01234567", "大力公司");
        map.put("11234567", "新发公司");
        map.put("21234567", "有为公司");
        map.put("31234567", "恒远公司");

        Set<String> keys = map.keySet();
        Iterator<String> iterator = keys.iterator();
        while (iterator.hasNext()) {
            String phoneNum = (String) iterator.next();
            String nextName = map.get(phoneNum);
            System.out.println(nextName + " : " + phoneNum);
        }
        String phone = map.remove("212345467");
        System.out.println("删除了电话号码为:21234567 的公司");
        Iterator kiter = keys.iterator();
        while (kiter.hasNext()) {
            String phoneNum = (String) kiter.next();
            String nextName = map.get(phoneNum);
            System.out.println(nextName + " : " + phoneNum);
        }
        HashMap<String, String> newMap = new HashMap<>();
        newMap.putAll(map);
        int s = newMap.size();
        System.out.println("删除后剩如下:" + s + " 个公司");
        String name = newMap.get("31234567");
        System.out.println("电话 31234567 是" + name + "公司的");
    }
}

十三、注解、反射、内部类、匿名内部类与Lambda表达式

1.什么是注解?根据注解的作用,注解分为几种?

(1)注解也叫元数据,就是用来描述数据的数据。注解的主要用于告知编译器要做什么事情。在程序中可对任何程序元素进行注解。注解并不影响程序代码的执行,无论真假还是删除注解,代码都始终如一地执行。

(2)根据注解的作用可以将注解分为基本注解、元注解(或称为元数据注解)与自定义注解三种。其中,基本注解5中,元注解有6种。

2.编写一个Java程序,使用JDK的基本注解,对覆盖方法使用@Override,再对另一方法使用@Deprecated。

略。

3.反射的作用是什么?

Java的反射(reflection)机制是指在程序的运行状态中,动态获取程序信息以及动态调用对象的功能。反射可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员和方法,可以调用任意一个对象的属性和方法。

4.编写具有反射功能的Java程序时,可使用哪三种方法获取指定类的Class对象?

在Java中程序获得Class对象有如下三种方式:

(1)使用Class类的静态方法forName(String className),其中参数className表示所需类的全民,如“Class cObj = Class.forName(“java.lang.String”);”。

(2)用类名调用该类的class属性来获得该类对应的Class对象,即“类型.class”。

(3)用对象调用getClass()方法来获得该类对应的Class对象,即“对象.getClass()”。该方法是Object类中的一个方法,因此多有对象调用该方法都可以返回所属类对应的Class对象。

5.内部类有哪几种?分别在什么情况下使用?他们所起的作用有哪些?

类作为成员可分为内部类和匿名内部类两种。内部类是定义在类中的类,所以当一个类作为一个成员使用时,应将该类定义为内部类,此时内部类所起的作用就是一个成员;匿名类是一种特殊的内部类,他没有类名,在定义类的同时,就生成该类的一个对象,由于不会在其他地方用到该类,所以不用取名字,匿名内部类主要用途一是哟很来弥补内部类中没有定义到的方法,二是编写事件处理程序。

6.内部类与外部类的使用有何不同?

包含内部类的类成为外部类,内部类不能与外部类同名,否则编译器将无法区分内部类与外部类。如果内部类中还有内部类,则内部类的内部类不能与它任何一层外部类同名。Java将内部类作为一个成员,就如同成员变量或成员方法。内部类可以被声明为private或protected。因此外部类与内部类的访问原则是:在外部类中,通过一个内部类的对象引用内部类中的成员;反之,在内部类中可以直接引用它的外部类的成员,包括静态成员、实例成员及私有成员;内部类也可以通过创建对象从外部类之外被调用,但必须将内部类声明为public的。

7.怎样创建匿名内部了对象?

匿名内部类是指可利用内部类创建没有名称的对象,它一步完成了声明内部类和创建该类的一个对象,并利用该对象访问到类里的成员。这种类不取名字,直接用其父类的名字或者它所实现的接口的名字,而且匿名内部类的定义与创建该类的一个实例同时进行,即类的定义前面有一个new运算符,而不是使用关键字class,同时带上圆括号“()”表示创建对象。比如:

public interface InterfaceDemo {
    void show();
}
public class Test {
    public static void main(String[] args) {
        InterfaceDemo interfaceDemo = new InterfaceDemo() {
            @Override
            public void show() {
                System.out.println("这里使用了匿名内部类");
            }
        };
        // 调用接口的方法
        interfaceDemo.show();
    }
}

8.什么是Lambda表达式?Lambda表达式的语法是什么样的?

Lambda表达式是可以传递给方法的一段代码。它可以是一条语句,也可以是一个代码块,因不需要方法名,所以说Lambda表大会是一种匿名方法。Lambda表达式通常由参数列表、箭头、和方法体三部分组成,其语法格式如下:

{类型1 参数1, 类型2 参数2, ...} -> {方法体}

比如:

public interface InterfaceDemo {
    void show(); // Lambda表达式只适用于包含一个抽象方法的接口
}
public class Test {
    public static void main(String[] args) {
        InterfaceDemo interfaceDemo = () -> System.out.println("这里使用了匿名内部类");
        // 调用接口的方法
        interfaceDemo.show();
    }
}

9.什么是函数式接口?为什么Lambda表达式只适用于函数式接口?

(1)函数式接口(Function Interface,FI)是指包含一个抽象方法的接口,因此也成为单抽象方法接口,因此也称为但后向方法接口。

(2)由Lambda表达式的语法格式可以看出,Lambda表达式只适用于包含一个抽象方法的接口,对于包含多个抽象方法的接口,编辑器则无法编译Lambda表达式。所以如果要编辑器理解Lambda表达式,接口就必须是指包含一个抽象方法的函数式接口。

10.Lambda表达式与匿名内部类有什么样的关系?函数式接口为什么重要?

(1)每一个Lambda表达式都对应一个函数式接口,可以将Lambda表达式看做是实现函数式接口的匿名内部类的一个对象。函数式接口之所以重要是因为可以使用Lambda表达式创建一个与匿名内部类等价的对象,正因为如此,Lambda表达式可以被看做是使用精简语法的匿名内部类。

(2)Java中任何Lambda表达式必定有对应的函数式接口,正是因为Lambda表达式已经明确要求是在函数式接口上进行的一种操作,所以建议最好在接口上使用@FunctionalInterface注解声明。

11.Java定义了哪四种方法引用方式?对方法引用有什么要求?

(1)对于方法引用,Java定义了如下四种引用方式:

  • 对象名::实例方法名 // 用对象名引用实例方法
  • 类名::静态方法名 // 用类名引用静态方法
  • 类名::实例方法名 // 用类名引用实例方法
  • 类名::new // 用类名引用构造方法

(2)前三种被引用的方法名后不能有圆括号,并且被引用方法的参数列表和返回值类型必须与函数式接口中的抽象方法参数列表和方法返回值类型一致。第四种方式用类名引用构造方法,构造方法的应用不同于其他方法的应用,构造方法引用需使用new运算符。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值