每日五题-java面试题220817

发布于:2023-01-12 ⋅ 阅读:(384) ⋅ 点赞:(0)


1、UML 中有哪些常用的图

UML 定义了多种图像化的符号来描述软件系统部分或全部的静态结构和动态结构,包括:用例图(use case diagram)、类图(class diagram)、时序图(sequence diagram)、协作图(collaboration diagram)、状态图(statechart diagram)、活动图(activity diagram)、构件图(component diagram)、部署图(deployment diagram)等。在这些图形化符号中,有三种图最为重要,分别是:用例图(用来捕获需求,描述系统的功能,通过该图可以迅速的了解系统的功能模块及其关系)、类图(描述类以及类与类之间的关系,通过该图可以快速了解系统)、时序图(描述执行特定任务时对象之间的交互关系以及执行顺序,通过该图可以了解对象能接收的消息也就是说对象能够向外界提供的服务)。用例图和类图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-16SxN1Dp-1660835281470)(image-20220817224004620.png)]

时序图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U46dFF4q-1660835281471)(image-20220817224028948.png)]

2、用 Java 写一个冒泡排序

冒泡排序几乎是个程序员都写的出来,但是面试的时候如何写一个逼格高的冒泡排序却不是每个人都能做到,下面提供一个参考代码:

package day220817;

import java.util.Comparator;

public interface Sorter {

    /**
     * 排序
     * @param list 待排序的数组
     * @param <T>
     */
    public < T extends Comparable<T>> void sort(T[] list);

    /**
     * 排序
     * @param list 待排序的数组
     * @param comparator 比较两个对象的比较器
     * @param <T>
     */
    public <T> void sort(T[] list, Comparator<T> comparator);



}
package day220817;

import java.util.Comparator;

/**
 * @ProjectName: java_test_01
 * @Package: day220817
 * @ClassName: BubbleSorter
 * @Author: [Lucky Star]
 * @Date: 2022/8/17 22:48
 * @Version: V1.0
 **/
public class BubbleSorter implements Sorter{
    @Override
    public <T extends Comparable<T>> void sort(T[] list) {
        Boolean swapped = true;
        for (int i = 1, len = list.length; i < len && swapped; ++i) {
            swapped = false;
            for (int j = 0; j < len - i; ++j){
                if (list[j].compareTo(list[j+1]) > 0) {
                    T temp = list[j];
                    list[j] = list[j+1];
                    list[j+1] = temp;
                    swapped = true;
                }
            }
        }
    }

    @Override
    public <T> void sort(T[] list, Comparator<T> comparator) {
        Boolean swapped = true;
        for (int i = 1, len = list.length ; i < len && swapped; ++i) {
            swapped = false;
            for (int j = 0; j < len -i; ++ j){
                if (comparator.compare(list[j], list[j + 1])>0) {
                    T temp = list[j];
                    list[j] = list[j + 1];
                    list[j + 1] = temp;
                    swapped = true;
                }
            }
        }
    }
}

3、用 Java 写一个折半查找

折半查找,也称为二分查找、二分搜索,是一种在有序数组中查找某一特定元素的搜索算法。搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组已经为空,则表示找不到指定的元素。这种搜索算法每一次比较都是搜索范围缩小一半,其时间复杂度是O(logN).

public class MyUtil {
    public static <T extends Comparable<T>> int binarySearch(T[] x, T key){
        return binarySearch(x, 0, x.length-1, key);
    }

    // 使用循环实现的二分查找  泛型方法
    public static <T> int binarySearch(T[] x, T key, Comparator<T> comparator){
        int low = 0;
        int high = x.length - 1;
        while (low <=high) {
            // 逻辑右移,是不带符号位的右移
            int mid = (low + high) >>> 1;
            int cmp = comparator.compare(x[mid],key);
            if (cmp < 0){
                low = mid + 1;
            }else if (cmp > 0){
                high = mid - 1;
            }else {
                return mid;
            }
        }
        return -1;
    }

    // 使用递归实现的二分查找
    private static <T extends Comparable<T>> int binarySearch(T[] x, int low, int high, T key){
        if (low <= high) {
            int mid = low + ((high-low) >> 1);
            if ( key.compareTo(x[mid]) == 0){
                return mid;
            }else if (key.compareTo(x[mid])< 0){
                return binarySearch(x, low, mid -1, key);
            }else {
                return binarySearch(x, mid + 1, high, key);
            }
        }
        return -1;
    }
}

说明:上面的代码中给出了折半查找的两个版本,一个用递归实现,一个用循环实现。需要注意的是计算机中间位置时不时应该使用(high+low)/2的方式,因为加法运算可能导致整数越界,这里应该使用一下三种方式之一: low+(high-low)/2 或 low + ()(high-low)>>1) 或 (low + high) >>>1 ( >>>是逻辑右移,是不带符号位的右移)。

4、Java 中能创建 volatile 数组吗?

能, Java 中可以创建 [volatile](Java中Volatile关键字详解 - 郑斌blog - 博客园 (cnblogs.com)) 类型数组,不过只是一个指向数组的引用,而不是整个数组。我的意思是,如果改变引用指向的数组,将会受到 volatile 的保护,但是如果多个线程同时改变数组的元素,volatile 标识符就不能起到之前的保护作用了。

5、volatile 能使得一个非原子操作变成原子操作吗?

一个典型的列子是在类中有一个 long 的类型的成员变量。 如果你知道该成员变量会被多个线程访问,如计数器、价格等,你最好是将其设置为 volatile。 为什么? 因为 Java 中读取 long 类型变量不是原子的, 需要分成两步,如果一个线程正在修改该long 变量的值,另一个线程可能只能看到该值的一半(前32位)。但是对一个 volatile 型的 long 或 double 变量的读写是原子的。

源链接-掘金


网站公告

今日签到

点亮在社区的每一天
去签到