【原创】Java基础面试题②

2023-03-03 20:42:46 浏览数 (1)

排序算法

1.冒泡排序算法

代码语言:javascript复制
int[] array= {10,9,8,7,6,5,4,3,2,1};
int temp=0;
for(int i=1;iarray[j 1]) {
                temp=array[j];
                array[j]=array[j 1];
                array[j 1]=temp;
                }
        } 
}

2.选择排序算法

代码语言:javascript复制
Integer[] a ={1,2,3,2,1,6,7,8,5,3,2,5,3,6,3};
for (int i = 0; i < a.length-1; i  ) {
        int temp = a[i];
        int flag = i; // 将当前下标定义为最小值下标
        for (int j = i   1; j < a.length; j  ) {
            if (a[j] < temp) {// a[j] < temp 从小到大排序;a[j] > temp 从大到小排序
                temp = a[j];
                flag = j; // 如果有小于当前最小值的关键字将此关键字的下标赋值给flag
            }
        }
        if (flag != i) {
            a[flag] = a[i];
            a[i] = temp;
        }
}

3.二分查找法

代码语言:javascript复制
public class _00BinarySearch {
    public static void main(String[] args) {
        int[] arr = {6, 12, 33, 87, 90, 97, 108, 561};
        System.out.println("循环查找:"   binarySearch(arr, 87));
        System.out.println("递归查找:"   binSearch(arr, 0, arr.length - 1, 87));
    }

    //循环实现二分查找算法arr 已排好序的数组x 需要查找的数-1 无法查到数据
    public static int binarySearch(int[] srcArray, int des) {
        //定义初始最小、最大索引
        int low = 0;
        int high = srcArray.length - 1;
        //确保不会出现重复查找,越界
        while (low <= high) {
            //计算出中间索引值
            int middle = (high   low) >>> 1;//防止溢出
            if (des == srcArray[middle]) {
                return middle;
                //判断下限
            } else if (des < srcArray[middle]) {
                high = middle - 1;
                //判断上限
            } else {
                low = middle   1;
            }
        }
        //若没有,则返回-1
        return -1;
    }

    /**
     * 二分查找递归实现。
     * @param srcArray 有序数组
     * @param start    数组低地址下标
     * @param end      数组高地址下标
     * @param key      查找元素
     * @return 查找元素不存在返回-1
     */
    public static int binSearch(int srcArray[], int start, int end, int key) {
        int mid = (end - start) / 2   start;
        int mid = (end - start) >>> 1;
        if (srcArray[mid] == key) {
            return mid;
        }
        if (start >= end) {
            return -1;
        } else if (key > srcArray[mid]) {
            return binSearch(srcArray, mid   1, end, key);
        } else if (key < srcArray[mid]) {
            return binSearch(srcArray, start, mid - 1, key);
        }
        return -1;
    }
}

常见的设计模式

1. 单例设计模式: 整个应用程序中只有该类的一个实例,类的构造方法私有化,提供一个方法,该类自己创建实例,提供公共静态方法返回该类的实例 单例设计模式分为懒汉式和饿汉式。 饿汉式:在使用该类之前就已经将该类的对象创建完成,使用公共方法返回,没有线程安全问题,占用了内存空间。 懒汉式:需要的时候在进行创建,多线程环境下存在线程安全问题,需要在返回公共对象的方法上添加同步代码块 DCL:双检锁机制。第一次进入方法时检查对象是否已经被实例化,如果没有则进入到同步代码块中,在实例化该对象之前,再次进行检查对象是否已经被实例化。 使用场景:Jsp中的Application对象就是单例设计模式,该类用于项目中加载Xml文件,完成多个客户端之间的数据共享。 2. 工厂设计模式: 工厂设计模式提供了一种创建对象的一种方法。 隐藏创建对象的逻辑,实现创建对象和对象的使用过程解耦。 简单工厂:在工厂类中定义方法用于创建某个接口的实现类,方法的参数为对象所在类的接口,根据传入该接口的实现了创建不同的对象。角色:抽象产品,具体产品,具体工厂。 工厂方法:将工厂提取成一个接口或抽象类,具体生产什么产品由子类决定。角色:抽象产品类,具体产品类,抽象工厂类,具体工厂类。 抽象工厂:为创建一组相关或者是相互依赖的对象提供的一个接口,不需要指定具体类。角色:和工厂方法一致。 3. 代理设计模式: 一个类代表另外一个类的功能,这种类型的设计模式属于结构型模式。为对象提供一个代理用于控制对该对象的访问。 静态代理实现方式:①定义一个接口以及实现类,②定义一个代理类实现该接口。③在代理类中重写接口方法时调用实现类的方法。 JDK实现动态代理(通过反射实现,获得目标类的实例和调用目标类的方法)。实现方式:①创建一个接口以及实现类。②定义一个代理类实现InvocationHandler接口,重写invoke方法。使用代理对象调用方法,完成对目标方法功能的增强。 CGLib实现动态代理(CGLib通过继承的方式实现动态代理。核心为MethodInterceptor接口和Enhancer类)。实现方式:①创建代理类实现MethodInterceptor接口,生成方法的拦截器。②CGLib定义的intercept()方法(MethodInterceptor中的方法),拦截所有目标类的调用。③使用Enhancer类提供的方法完成对代理对象调用方法的拦截和代理对象的创建。 Spring中的AOP使用JDK和CGLib两种方式完成对代理类的创建。 4. 装饰者设计模式: 向一个现有的对象添加新功能,但又不改变其结构。装饰者设计模式本质是创建一个装饰类,用来包装原有类。将已有对象传入另一个类的构造器中创建新的对象来增强实现。就新增功能来说,装饰着设计模式相比生成子类更加灵活。 实现方式:

代码语言:javascript复制
  ①创建一个接口和接口的实体类。
  ②创建一个实现同一接口的抽象装饰类,该抽象类中包括一个该接口的属性,构造方法中使用有参构造,实例化该属性。
  ③为抽象装饰类创建一个实体类,该实体类重写接口中的方法,除了调用接口原有实体类(第一步的实体类)中的方法,新增其他功能。

IO中的节点流和处理流就是一种装饰着设计模式。将节点流作为参数传入处理流。在字节流的基础上添加了缓存等功能。 5. 适配器模式: 将已有的数据转换成需要的数据。适配器模式将原本不兼容而不能一起工作的类变的可以一起工作。 SpringMvc中的处理器适配器就是一种适配器模式,将传入的不同数据转换为ModelAndView。

HTTP和HTTPS协议的区别

HTTP是普通的传输协议,端口号是80,信息是明文传输。 HTTPS是加密传输协议,端口号是445,在HTTP协议上添加了SSL证书,这个证书一般书需要购买的,安全性更高,但也比较耗时。

内存中的栈(stack)、堆(heap)和方法区(method area)的用法

通常定义的基本数据类型的变量,对象的引用都保存在栈内存中,通过new关键字和构造器创建的对象保存在堆内存中。堆是垃圾回收器管理的主要区域,垃圾回收器采用分代收集的算法,所有堆空间中还可以细分为新生代和老年代。再具体点可以分为Eden(伊甸园)、Survivor(可分为FromSurvivor和ToSurvivor)、Tenured(老年代);方法区和堆都是各个线程共享的内存区域,用于存储已经被JVM加载的类信息、常量、静态变量、JIT编译器编译后的代码等数据;程序中的字面量(literal)如直接书写的100、"hello"和常量都是存放在常量池中,常量池是方法区的一部分,栈空间操作起来最快,但是栈的内存很小,通常大量数据都会保存在堆空间,栈和堆的大小都可以通过JVM的启动参数来调整,栈空间用光了会引发StackOverflowError(栈溢出异常),而堆和常量池空间不足会引发OutOfMemoryError(内存溢出异常)

Java中的GC

GC是垃圾收集的意思,Java提供的GC功能可以自动检测对象是否超过作用域从而达到回收内存的目的,Java语言没有提供释放已分配内存的操作方法,垃圾收集器会自动进行管理。如果需要垃圾收集,可以调用System.gc()或Runtime.getRuntime().gc(). 垃圾回收可以有效的防止内存泄漏,有效的使用内存,垃圾回收器通常是作为一个单独的低优先级的线程运行,不可预知的情况下对堆内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收。 垃圾回收机制有很多种,如:分代复制垃圾回收、标记垃圾回收、增量垃圾回收等方式。标准的Java进程既有栈又有堆。栈保存了原始型局部变量,堆保存了要创建的对象。Java平台对堆内存回收和再利用的基本算法被称为标记和清除,Java对其进行了改进,采用"分代式垃圾收集"。这种机制会将Java对象的生命周期将堆内存划分为不同的区域,在垃圾收集过程中,可能会将对象移动到以下不同区域:

代码语言:javascript复制
 伊甸园(Eden):这是对象最初诞生的区域,并且对大多数对象来说,这里是唯一存在过的区域。
 幸存者乐园(Survivor):从伊甸园幸存下来的对象都会被挪到这里。
 老年代(Tenured):这里把保存足够老的幸存对象。
 新生代(Minor-GC):当对象不能放进老年代时,就会出发一次完全收集(Major-GC),这里可能会存在压缩,以便为大对象腾出足够的空间。

0 人点赞