刚从csdn面试出来被问到的10道题给大家看看

这篇博客讨论了Java中TreeMap和TreeSet的排序原理,以及Collections.sort()方法的两种使用方式。通过示例解释了Comparable接口和Comparator接口在元素比较中的作用。此外,还探讨了线程的Sleep()方法和wait()方法的区别,以及多线程编程的实现方式。最后,简要介绍了Java中的序列化、流的类型以及文件操作的实现方法。

1.TreeMap和TreeSet在排序时如何比较元素?Collections工具类中单sort()方法如何比较元素?

TreeSet要求存放的对象所属的类必须事先Comparable接口,该接口提供了比较元素的compareTo()方法,当插入元素时会调该方法比较元素的大小。TreeMap要求存放的键值对映射的键必须事先Comparable接口从而根据键对元素进行排序。Collections工具类的sort方法有两种重载的形式,第一种要求传入的待排序容器中存放的对象比较实现Comparable接口以实现元素的比较,第二种不强制性的要求容器中的元素必须可比较但是要求传入第二个参数 参数是Comparator接口的子类型(需要重写Compare方法实现元素的比较)相当于一个临时定义的排序规则 其实就是通过接口转入比较元素大小的算法 也是对回调和模式的应用

public class Student implements Comparable<Student> {
    private String name;        // 姓名
    private int age;            // 年龄

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + "]";
    }

    @Override
    public int compareTo(Student o) {
        return this.age - o.age; // 比较年龄(年龄的升序)
    }

}


import java.util.Set;
import java.util.TreeSet;

class Test01 {

    public static void main(String[] args) {
        Set<Student> set = new TreeSet<>();     // Java 7的钻石语法(构造器后面的尖括号中不需要写类型)
        set.add(new Student("Hao LUO", 33));
        set.add(new Student("XJ WANG", 32));
        set.add(new Student("Bruce LEE", 60));
        set.add(new Student("Bob YANG", 22));

        for(Student stu : set) {
            System.out.println(stu);
        }
//      输出结果: 
//      Student [name=Bob YANG, age=22]
//      Student [name=XJ WANG, age=32]
//      Student [name=Hao LUO, age=33]
//      Student [name=Bruce LEE, age=60]
    }
}

例子2:

public 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 int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + "]";
    }

}






import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

class Test02 {

    public static void main(String[] args) {
        List<Student> list = new ArrayList<>();     // Java 7的钻石语法(构造器后面的尖括号中不需要写类型)
        list.add(new Student("Hao LUO", 33));
        list.add(new Student("XJ WANG", 32));
        list.add(new Student("Bruce LEE", 60));
        list.add(new Student("Bob YANG", 22));

        // 通过sort方法的第二个参数传入一个Comparator接口对象
        // 相当于是传入一个比较对象大小的算法到sort方法中
        // 由于Java中没有函数指针、仿函数、委托这样的概念
        // 因此要将一个算法传入一个方法中唯一的选择就是通过接口回调
        Collections.sort(list, new Comparator<Student> () {

            @Override
            public int compare(Student o1, Student o2) {
                return o1.getName().compareTo(o2.getName());    // 比较学生姓名
            }
        });

        for(Student stu : list) {
            System.out.println(stu);
        }
//      输出结果: 
//      Student [name=Bob YANG, age=22]
//      Student [name=Bruce LEE, age=60]
//      Student [name=Hao LUO, age=33]
//      Student [name=XJ WANG, age=32]
    }
}

2.Thread类的Sleep()方法和对象的wait()方法都可以让线程暂停执行,它们有什么区别?

sleep()方法(休眠)是线程类(Thread)的静态方法 调用此方法会让当前线程暂停执行指定的时间,将执行机会 CPU让给其他线程但是对象的锁依然保持,因此休眠时间结束后会自动恢复 线程回到就绪状态 wait()是Objcet类的方法 调用对象的wait()方法导致当前线程放弃对象的锁 线程暂停执行 进入对象的等待池 wait pool 只有调用对象的notify ()方法 或者ontifyAll()方法 时才能唤醒等待池中的线程进入等锁池 lock pool 如果线程重新获得对象的锁就可以进入就绪状态

补充:可能不少人 对什么是线程 什么是进城还比较模糊 对于为什么需要多线程编程也不是特别理解 简单的说 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动 是操作系统进行资源分配和调度的一个独立单位 线程是进程的一个实体 是CPU调度和分派的基本单位 是比进程更小的能独立运行基本单位 线程的划分尺度小于进程 这使得多线程的并发性高 进程在执行是通常用于独立的内存单元 而线程之间可以共享内存 使用多线程的编程通常能够带来更好的性能和用户体验 但是多线程的程序对于其他程序是不友好的 因为它可能占用了更多的CPU资源 当然也是现成越多 程序的性能就越好 因为线程之间的调度和切换也会浪费CPU时间 时下很时髦的Node.js就采用了单线程异步I/O工作模式

3.编写多线程程序有几种实现方式?

Java 5以前实现多线程有两种实现方式 一种是继承Theard类 另一种是实现 Runnable接口 两种方式都要通过重写run()方法来定义线程的行为 推荐使用后者 因为Java中的继承是单继承 一个类有一个父类 如果继承了Thread类就无法在继承其他类了 显然使用Runnable 接口更为灵活

补充:Java 5以后创建多线程还有第三种方式:实现Callable接口 该接口中的call方法可以在县城执行结束时产生一个返回值 代码如下所示

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;


class MyTask implements Callable<Integer> {
    private int upperBounds;

    public MyTask(int upperBounds) {
        this.upperBounds = upperBounds;
    }



    @Override
    public Integer call() throws Exception {
        int sum = 0; 
        for(int i = 1; i <= upperBounds; i++) {
            sum += i;
        }
        return sum;
    }

}

class Test {

    public static void main(String[] args) throws Exception {
        List<Future<Integer>> list = new ArrayList<>();
        ExecutorService service = Executors.newFixedThreadPool(10);
        for(int i = 0; i < 10; i++) {
            list.add(service.submit(new MyTask((int) (Math.random() * 100))));
        }

        int sum = 0;
        for(Future<Integer> future : list) {
            // while(!future.isDone()) ;
            sum += future.get();
        }

        System.out.println(sum);
    }
}

4.synchronized关键字的用法?

synchronized关键字可以将对象或者方法标记为同步 以实现对对象和方法的互斥访问 可以用synchroinzed(对象){...} 定义同步代码块,或者在声明方法是将sychronized作为方法的修饰符

5、简述synchronized和Java.util.concurrent.locks.Lock的异同?

Lock是Java 5以后引入的新的API 和关键字synchronized相比主要相同点:Lock能完成synchronized所实现的所有功能 主要不同点:Lock有比synchronized更精确的线程语义和更好的性能,而且不强制性的要求一定要获得锁 synchronized会自动释放锁 而Lock一定要求程序员手工释放 并且最好在finally块中释放

6.Java中如何实现序列化 有什么意义?

序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化 可以对流化后的对象进行读写操作 也可将流化后的对象传输于网络之间 序列化是为了解决对象流读写操作时可能引发的问题 如果不进行序列化可能会存在数据乱序的问题

要实现序列化 需要让一个类实现 Serializable 接口 该接口是一个标识性接口 标注该类对象是可被序列化的 然后使用一个输出流来构造一个对象输出并通过 writeObject(Object)方法就可以将实现对象写出(即保存其状态)如果需要反序列化则可以用一个输入流建立对想输入流,然后通过readObject方法从流中读取对象 序列化除了能够实现对的持久化之外 还能够用于对象的深度克隆

7.Java中有几种类型的流?

字节流和字符流 字节流继承于InputStream, OutputStream 字符流继承于Reader, Writer 在Java.io包中还有许多其他的流 主要为了提高性能和使用方便 关于Java的I/O需要注意的有两点:一是两种对称性(输入和输出的对称性,字节和字符的对称性)二是两种设计模式(适配器模式和装潢模式) 另外Java中的流不同于C#的是它只有一个维度一个方向


    面试题 - 编程实现文件拷贝。(这个题目在笔试的时候经常出现,下面的代码给出了两种实现方案)

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public final class MyUtil {

    private MyUtil() {
        throw new AssertionError();
    }

    public static void fileCopy(String source, String target) throws IOException {
        try (InputStream in = new FileInputStream(source)) {
            try (OutputStream out = new FileOutputStream(target)) {
                byte[] buffer = new byte[4096];
                int bytesToRead;
                while((bytesToRead = in.read(buffer)) != -1) {
                    out.write(buffer, 0, bytesToRead);
                }
            }
        }
    }

    public static void fileCopyNIO(String source, String target) throws IOException {
        try (FileInputStream in = new FileInputStream(source)) {
            try (FileOutputStream out = new FileOutputStream(target)) {
                FileChannel inChannel = in.getChannel();
                FileChannel outChannel = out.getChannel();
                ByteBuffer buffer = ByteBuffer.allocate(4096);
                while(inChannel.read(buffer) != -1) {
                    buffer.flip();
                    outChannel.write(buffer);
                    buffer.clear();
                }
            }
        }
    }
}

    注意:上面用到Java 7的TWR,使用TWR后可以不用在finally中释放外部资源 ,从而让代码更加优雅。

8.写一个方法,输入一个文件名和一个字符串 统计这个字符串在这个文件中出现的次数:

import java.io.BufferedReader;

import java.io.FileReader;

public final class MyUtil{

    //工具类中更多方法都是静态方法访问的因此将构造器私有不允许创建对象(绝对好习惯)

  private MyUtil(){

     throw new AssertionError();

}

/**
     * 统计给定文件中给定字符串的出现次数
     * 
     * @param filename  文件名
     * @param word 字符串
     * @return 字符串在文件中出现的次数
     */
    public static int countWordInFile(String filename, String word) {
        int counter = 0;
        try (FileReader fr = new FileReader(filename)) {
            try (BufferedReader br = new BufferedReader(fr)) {
                String line = null;
                while ((line = br.readLine()) != null) {
                    int index = -1;
                    while (line.length() >= word.length() && (index = line.indexOf(word)) >= 0) {
                        counter++;
                        line = line.substring(index + word.length());
                    }
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return counter;
    }

}

}

 

9.如何用Java代码列出一个目录下所有的文件?

如果只要求列出当前文件夹下的文件 代码如下所示

import java.io.File;

class Test12 {

    public static void main(String[] args) {
        File f = new File("/Users/Hao/Downloads");
        for(File temp : f.listFiles()) {
            if(temp.isFile()) {
                System.out.println(temp.getName());
            }
        }
    }
}


如果需要对文件夹继续展开,代码如下所示:

import java.io.File;

class Test12 {

    public static void main(String[] args) {
        showDirectory(new File("/Users/Hao/Downloads"));
    }

    public static void showDirectory(File f) {
        _walkDirectory(f, 0);
    }

    private static void _walkDirectory(File f, int level) {
        if(f.isDirectory()) {
            for(File temp : f.listFiles()) {
                _walkDirectory(temp, level + 1);
            }
        }
        else {
            for(int i = 0; i < level - 1; i++) {
                System.out.print("\t");
            }
            System.out.println(f.getName());
        }
    }
}

在Java 7中可以使用NIO.2的API来做同样的事情,代码如下所示:

class ShowFileTest {

    public static void main(String[] args) throws IOException {
        Path initPath = Paths.get("/Users/Hao/Downloads");
        Files.walkFileTree(initPath, new SimpleFileVisitor<Path>() {

            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
                    throws IOException {
                System.out.println(file.getFileName().toString());
                return FileVisitResult.CONTINUE;
            }
        });
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值