Java程序设计期末练习题 
一、填空题(每空1分,共20分) 
- 引用类型转型时可以使用instanceof操作符来判断转型的合法性。
- 将方法声明为final意味着它不能被继承。
- 自定义异常必须要继承的类是Exception。
- 在子类构造器中通过语句super()可以调用父类中的无参构造器。
- Java使用Thread类及子类对象表示线程。
- 类中构造器this()语句的含义是调用类中的无参构造器。
- 接口中的所有属性都是公开静态常量。
- 可以对象序列化的对象的所属类需要实现Serializable接口。
- 不定长参数定义的语法格式为:数据类型… 参数名。
- 源文件中的每一个类编译后都会生成一个字节码文件,字节码文件的扩展名是.class。
- 避免越界发生的有效方法是利用length属性作为数组下标的上界。
- 开闭原则是指程序对于扩展是开放的,对于修改是关闭的。
- 接口中的成员方法默认的修饰符是public abstract。
- 将对象放入HashSet中时,需要保证该对象所属类中的hashCode()方法与equals()方法要兼容。
- Map内部定义了一个- Entry类,封装了一个键值对,使用- Map的- entrySet()方法可以获取键值对集合。
- 通过Collection接口中的iterator()方法可以获得迭代器。
- 子类方法抛出的异常类型必须是父类方法抛出的异常类型或者父类方法抛出的异常类型的子类。
- Java的标准输入System.in是InputStream类的对象。
- 泛型方法是带有参数类型的方法。
- BufferedReader类构造方法的参数是- InputStreamReader类的对象。
二、判断正误(每题1分,共10分) 
- “float x1=1.414f;”语句中等号右边的‘f’是多余的(错)。
- 可以在抽象类中定义非抽象方法(对)。
- 接口中的所有方法都是公开的抽象方法(对)。
- 静态内部类可以访问外部类的数据成员(错)。
- 泛型初始化过程中,一旦给定了参数类型之后,参数类型必须是定了参数类型的父类或子类(错)。
- 声明String str[]后,数组名str可以直接使用,给指定数组元素赋值(错)。
- Java不允许数值类型和布尔类型之间进行转换(对)。
- IOException异常是非运行时异常,必须在程序中抛弃或捕获(对)。
- 子类不可以调用父类中没有的方法(错)。
- 用布尔逻辑运算符不会产生短路现象(对)。
三、单选题(每题2分,共30分) 
- 以下的变量定义语句中,合法的是(D) - (A)float _*5 = 123.456F;
- (B)byte $_b1 = 12345;
- (C)int _long_ = 123456L;
- (D)double d = Double.MAX_VALUE;
 
- (A)
- 下列说法错误的是(C): - A. 一个Java源文件不允许有多个公共类;
- B. 数组可以赋值给Object类型的对象;
- C. 一个Java源文件生成一个class文件;
- D. 数组可以调用Object类的所有方法;
 
- 类A有一个方法void method(),要求能够通过类名A直接调用method(),则方法定义应该为(A)- A. static void method()
- B. public void method()
- C. final void method()
- D. abstract void method()
 
- A. 
- 下面关于方法的说法,不正确的是(C)。 - (A)Java中的构造方法名必须和类名相同;
- (B)方法体是对方法的实现,包括变量声明和合法语句
- (C)如果一个类定义了构造方法,也可以用该类的默认构造方法
- (D)类的私有方法不能被其他类直接访问
 
- 区分类中重载方法的依据是(A)。 - A.形参列表的类型和顺序
- B.不同的形参名称
- C.返回值的类型不同
- D.访问权限不同
 
- 如果局部变量和成员变量同名,如何在局部变量作用域内引用成员变量?(B) - A.不能引用,必须改名,使它们的名称不相同
- B.在成员变量前加this,使用this访问该成员变量
- C.在成员变量前加super,使用super访问该成员变量
- D.不影响,系统可以自己区分
 
- 已知有定义:String s="I love",下面哪个表达式正确?(A)- A.s += "you";
- B.char c = s[1];
- C.int len = s.length;
- D.String s = s.toLowerCase();
 
- A.
- 对应try和catch子句的排列方式,下列哪一项是正确的?(A)- A.子类异常在前,父类异常在后
- B.父类异常在前,子类异常在后
- C.只能有子类异常
- D.父类和子类不能同时出现在try语句块中
 
- 下列代码中给出正确的在方法体内抛出异常的是(B)。 - A.new throw Exception(" ");
- B.throw new Exception(" ");
- C.throws IOException();
- D.throws IOException;
 
- A.
- 若要删除一个文件,应该使用下列哪个类的实例(B)? - A.RandomAccessFile
- B.File
- C.FileOutputStream
- D.FileReader
 
- A.
- 下列说法中,错误的一项是(A)。 - A.Thread类中没有定义run()方法
- B.可以通过继承Thread类来创建线程
- C.Runnable接口中定义了run()方法
- D.可以通过实现Runnable接口创建线程
 
- A.
- 下列说法中,错误的一项是(A) - A. 在迭代器中可以使用ArrayList中的remove()方法 ;
- B. 处理流用于在节点流的基础上进行加工处理;
- C. JVM使用serialVersionUID来验证对象版本
- D. static修饰的是类变量,不会被序列化。
 
- A. 在迭代器中可以使用
- 下列赋值语句中,正确的是:(D) - A. byte b1 = 10, b2 = 20; byte b=b1+b2;
- B. float c=1.3;
- C. byte b1 = 10, byte b=b1+10;
- D. byte b1 = 10; byte b=++b1;
 
- A. 
- 单例模式的实现必须满足的条件包括(C) - A. 类中的构造方法的访问权限必须设置为public
- B. 类中的构造方法必须用protected修饰
- C. 必须在类中创建该类的静态私有对象
- D. 提供一个公有的静态方法用于创建获取静态私有对象.
 
- A. 类中的构造方法的访问权限必须设置为
- A类继承自- B类,那么- A类中不可以使用(C)- A、B类的保护方法
- B、B类的公有属性
- C、B类的私有属性
- D、A类的私有属性
 
- A、
四、简答题(每小题5分,共20分) 
- 写出该程序的输出结果
class Insect {
    int i = 9;
    int j;
    int m = prt("Insect.m initialized");
    static int x1 = prt("static Insect.x1 initialized");
    Insect() {
        prt("i = " + i + ", j = " + j);
        j = 39;
    }
    static int prt(String s) {
        System.out.println(s);
        return 47;
    }
}
public class Beetle extends Insect {
    int k = prt("Beetle.k initialized");
    int l = prt("Beetle.l initialized");
    static int x2 = prt("static Beetle.x2 initialized");
    Beetle() {
        prt("k = " + k);
        prt("j = " + j);
    }
    public static void main(String[] args) {
        prt("Beetle constructor");
        Beetle b = new Beetle();
    }
}输出结果:
static Insect.x1 initialized
static Beetle.x2 initialized
Beetle constructor
Insect.m initialized
i = 9, j = 0
Beetle.k initialized
Beetle.l initialized
k = 47
j = 39解析:程序运行时,先加载父类Insect的静态成员,输出static Insect.x1 initialized;接着加载子类Beetle的静态成员,输出static Beetle.x2 initialized 。然后执行main方法,调用prt("Beetle constructor")输出Beetle constructor。创建Beetle对象时,先调用父类构造器,初始化父类成员变量,输出Insect.m initialized,再输出i = 9, j = 0,并将j赋值为39。最后初始化子类成员变量,输出Beetle.k initialized和Beetle.l initialized,在子类构造器中输出k = 47和j = 39。
- 在test1()中调用Collection.sort()方法,通过定制排序比较两个Employee(先按年龄,年龄相同按姓名比)。请用匿名内部类在横线处补齐代码
public class Employee{
    private String name;
    private int age;
    public int getAge(){
        return age;
    }
    public String getName(){
        return name;
    }
    public Employee(String name, int age){
        this.name = name;
        this.age = age;
    }
}
public class TestLambda {
    List<Employee> emps = Arrays.asList(new Employee("张三", 18),
            new Employee("李四", 59),
            new Employee("田七", 38)
    );
    public void test1() {
        Collections.sort(emps,new Comparator<Employee>() {
            public int compare(Employee o1, Employee o2) {
                if (o1.getAge()!=o2.getAge()){
                    return o1.getAge()-o2.getAge();
                }else{
                    return o1.getName().compareTo(o2.getName());
                }
            }
        });
        System.out.println("emps = " + emps);
    }
}解析:在Collections.sort()方法中,通过传入一个实现了Comparator接口的匿名内部类来定义排序规则。compare方法中,先比较年龄,如果年龄不同,返回年龄差值;如果年龄相同,则比较姓名,调用String类的compareTo方法进行比较。
- 创建一个工厂类(Factory),并在该类中定义3个成员变量工厂ID(id)、工厂名(name)以及地址(address),使用单例模式实现仅能创建Factory类的1个实例
public class Factory {
    private static Factory factory;
    private  int id;
    private  String name;
    private  String address;
    private Factory() {
    }
    public int getId() {
        return id;
    }
    public String getName() {
        return name;
    }
    public String getAddress() {
        return address;
    }
    public void setId(int id) {
        this.id = id;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public Factory getInstance(){
        if (factory == null){
            factory = new Factory();
        }
        return factory;
    }
}解析:单例模式通过将构造器设为私有,防止外部直接创建对象。使用静态私有成员变量factory保存唯一实例,通过静态公有方法getInstance来获取该实例。在getInstance方法中,先判断factory是否为null,如果是则创建实例,否则直接返回已有实例。
- 请填充并参考SecondThread代码,用Lambda表达式替换Test中的代码(1)和(2)
public class SecondThread implements Runnable {
    private int i=0;  
    public void run() {
        for(; i < 5; i++) {
            System.out.println(Thread.currentThread().getName());
        }
    }
}
public class Test {
    public static void main(String[] args) {
        // Runnable target = new SecondThread(); (1)
        // Thread t1 = new Thread(target);       (2)
        Thread t1 = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName());
            }
        });
        for(int i = 0; i < 5; i++) {          
            System.out.println(Thread.currentThread().getName() + " :  " + i);
            if(i == 1) t1.start();
        }     
    }
}解析:Lambda表达式简化了实现Runnable接口的方式。原代码中创建SecondThread对象并作为参数传递给Thread构造器,使用Lambda表达式后,直接在Thread构造器中传入一个代码块,该代码块实现了Runnable接口的run方法功能。
五、编程题(共20分) 
- 编写员工Employee类,包含工号id、姓名name、工资salary等属性,令Employee类实现comparable接口,并实现接口中的compareTo()方法,制定比较规则。在测试类中创建几个Employee对象,放入ArrayList集合中,对其进行排序。关于比较的规则先按Name进行升序排序,如果Name相同,按照id进行升序排序
//Employee类
public class Employee implements Comparable<Employee> {
    private int id;
    private String name;
    private double salary;
    public int getId() {
        return id;
    }
    public String getName() {
        return name;
    }
    public double getSalary() {
        return salary;
    }
    public Employee(int id, String name, double salary) {
        this.id = id;
        this.name = name;
        this.salary = salary;
    }
    @Override
    public int compareTo(Employee o) {
        if (this.getName().compareTo(o.getName()) == 0){
            return this.getId() - o.getId();
        }else{
            return this.getName().compareTo(o.getName());
        }
    }
    @Override
    public String toString() {
        return "{"+id+","+name+","+salary+"}";
    }
}
//Test类
public class TestEmployee {
    public static void main(String[] args) {
        List<Employee> emps = Arrays.asList(new Employee(1,"Zhang3", 100.0),
                new Employee(2,"Li4", 200.0),
                new Employee(3, "Wang5", 300.0),
                new Employee(4, "Zhao6", 400.0),
                new Employee(5, "Tian7", 500.0));
        Collections.sort(emps);
        System.out.println(emps);
    }
}解析:Employee类实现Comparable接口,重写compareTo方法定义排序规则。在compareTo方法中,先比较姓名,如果姓名相同再比较工号。在测试类TestEmployee中,创建Employee对象并放入ArrayList集合,调用Collections.sort方法对集合进行排序,最后输出排序后的结果。
2. 使用数据过滤流将1000 - 2000间的素数写到文件中持久化保存;将这些素数从文件读出并打印到控制台
//MyOutputStream类继承FilterOutputStream装饰器类,增加writePrime(int)和writePrimesInRange(int,int)方法
public class MyOutputStream extends FilterOutputStream {
    public MyOutputStream(OutputStream out) {
        super(out);
    }
    public void writePrime(int n) throws IOException{
        if (n < 2){
            return;
        }
        for (int i = 2; i * i <= n; i++) {
            if (n % i == 0){
                return;
            }
        }
        String sp = Integer.toString(n);
        for (int i = 0; i < sp.length(); i++) {
            super.write(sp.charAt(i));
        }
        super.write('\n');
    }
    public void writePrimesInRange(int low, int high) throws IOException {
        for (int i = low; i <= high; i++) {
            writePrime(i);
        }
    }
    public static void main(String[] args) throws IOException {
        MyOutputStream outputStream = new MyOutputStream(new FileOutputStream("primes.txt"));
        outputStream.writePrimesInRange(1000, 2000);
        outputStream.close();
        BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream("primes.txt"));
        int c = 0;
        while ((c = inputStream.read()) != -1){
            System.out.print((char)c);
        }
        inputStream.close();
    }
}解析:
- MyOutputStream类继承自- FilterOutputStream,用于对输出流进行功能扩展。
- writePrime(int n)方法用于判断一个数是否为素数,如果是则将其写入文件。判断素数的逻辑是:从2开始到该数的平方根,如果能被整除则不是素数。将素数转换为字符串后,逐个字符写入流,并在每个素数后写入换行符。
- writePrimesInRange(int low, int high)方法遍历指定范围内的数,调用- writePrime(int n)方法将其中的素数写入文件。
- 在main方法中,创建MyOutputStream对象并传入FileOutputStream,调用writePrimesInRange(1000, 2000)方法将1000到2000之间的素数写入primes.txt文件。然后使用BufferedInputStream读取文件内容,将读取到的字符逐个打印到控制台,直到文件末尾(read()方法返回 -1)。
3. 定义Student类,该类包括ID、姓名和年龄属性,创建一个Student对象持久化到文件,并从文件读出、打印
public class Student implements Serializable {
    int id;
    String name;
    int age;
    public Student(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "{"+id+","+name+","+age+"}";
    }
}
public class TestSerialize {
    public static void main(String[] args) throws Exception {
        File file = new File("student.txt");
        ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(file));
        outputStream.writeObject(new Student(10001, "Zhang3", 18));
        outputStream.close();
        ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(file));
        Student stu = (Student) inputStream.readObject();
        inputStream.close();
        System.out.println(stu);
    }
}解析:
- Student类实现了- Serializable接口,这是对象能够被序列化的必要条件。实现该接口表示- Student类的对象可以被转换为字节流进行存储或传输。
- TestSerialize类的- main方法中:- 首先创建一个File对象,指定存储学生对象的文件名为student.txt。
- 使用ObjectOutputStream将Student对象写入文件。ObjectOutputStream构造函数接受一个FileOutputStream,用于指定输出的目标文件。通过调用writeObject方法将新创建的Student对象写入文件。
- 接着使用ObjectInputStream从文件中读取对象。同样,ObjectInputStream构造函数接受一个FileInputStream来指定读取的源文件。通过调用readObject方法读取文件中的对象,并将其强制转换为Student类型。
- 最后关闭输入输出流,并打印读取到的Student对象,验证对象的持久化和读取操作是否成功。
 
- 首先创建一个