海康面试(高可用方向)

1、垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回

对于GC来说,当程序员创建对象时,GC就开始监控这个对象的地址、大小以及使用情况。通常,GC采用有向图的方式记录和管理堆(heap)中的所有对象。通过这种方式确定哪些对象是"可达的",哪些对象是"不可达的"。当GC确定一些对象为"不可达"时,GC就有责任回收这些内存空间。

(1. 不可用不可达------>这种情况GC会帮我们回收掉,而C++不会

2. 不可用可达 ------>这种情况会存在内存泄露

3. 可用可达 ------>正常使用

 

不可用不可达就是我们的变量作用域结束了,不可用不可达)

 

可以。

 

程序员可以手动执行System.gc(),通知GC运行,但是Java语言规范并不保证GC一定会执行。

2、数据库查询两张表,一张学生表,一张班级表,查询每个班级男女性别:

(一张学生表和一张班级表查询这个某个班的男生数sql该怎么写?)

 select count(*) from 学生表 where class_id  in  (select class_id from 班级表 where grade=3 and class_no=5) and sex='男'

3、重载和重写区别

方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。

重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;

重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的参数列表,有兼容的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。

重载对返回类型没有特殊的要求,不能根据返回类型进行区分。

4、八大排序算法

(1)快速排序

package demo2;

import java.util.Arrays;

public class QuickSort {

  public static void main(String[] args) {

    int[] arr=new int[] {9,4,6,7,2,7,2,8,0,10};

    quickSort(arr, 0, arr.length-1);

    System.out.println(Arrays.toString(arr));

}

  public static void quickSort(int[] arr,int start,int end){

      if(start<end){

      //把数组中第0个数字作为标准数

      int stard=arr[start];

      //记录需要排序的下标

      int low=start;

      int high=end;

      //循环找比标准数大的数和比标准数小的数

      while(low<high){

          //右边数字比标准数大

          while(low<high&&stard<=arr[high]){

              high--;

          }

          //使用右边的数字替换左边的数字

          arr[low]=arr[high];

          // 如果左边的数字比标准数小

          while(low<high&&arr[low]<=stard){

              low++;

          }

          arr[high]=arr[low];

      }

      //把标准数据赋值给所在的位置的元素

      arr[low]=stard;

      //处理所有小的数字

      quickSort(arr, start, low);

      //处理所有大的数字

      quickSort(arr, low+1, end);

 

  }

}

}

(2)冒泡排序

package demo2;

import java.util.Arrays;

public class BubbleSort {

    public static void main(String[] args) {

   int[] arr=new int[] {5,7,2,9,4,1,0,5,7};

   System.out.println("排序前的数组为:"+Arrays.toString(arr));

       bubbleSort(arr);

       System.out.println("排序后的数组为:"+Arrays.toString(arr));

    }

    public static void bubbleSort(int[] arr){

        //控制共比较多少轮

        for(int i=0;i<arr.length-1;i++){

            //控制比较的次数

            for(int j=0;j<arr.length-1-i;j++){

                if(arr[j]>arr[j+1]){

                    int temp=arr[j];

                    arr[j]=arr[j+1];

                    arr[j+1]=temp;

                }

              

            }

         

        }

     }

}

(3)选择排序

package demo2;

 

import java.util.Arrays;

 

public class SelectSort {

 

    public static void main(String[] args) {

 

        int[] arr=new int[] {2,5,7,4,9,1,5,0};

        selectSort(arr);

        System.out.println(Arrays.toString(arr));

    }

/**

 * 选择排序

 * @param arr

 */

    public static void selectSort(int[] arr){

        //遍历所有数

        for(int i=0;i<arr.length;i++){

            int minIndex=i;

            //把当前遍历的数和后面所有的数依次进行比较,并记录最小的数

            for(int j=i+1;j<arr.length;j++){

                //如果后面的数比最小的数小

                if(arr[minIndex]>arr[j]){

                minIndex=j;

            }

        }

        //如果最小的数和当前遍历的数下标不一致,说明下标为minIndex的数比当前遍历的数更小

        if(i!=minIndex){

            int temp=arr[i];

            arr[i]=arr[minIndex];

            arr[minIndex]=temp;

        }

    }

 }

}

(4)希尔排序

package demo2;

import java.util.Arrays;

public class ShellSort {

    public static void main(String[] args) {

        int[] arr=new int[]{3,5,8,3,0,2,4,6,7};

        System.out.println(Arrays.toString(arr));

        shellSort(arr);

        System.out.println(Arrays.toString(arr));

    }

    /**

     * 希尔排序

     * @param arr

     */

    public static void shellSort(int[] arr){

        int k=1;

        //遍历所有步长

        for(int d=arr.length/2;d>0;d/=2){

            //遍历所有元素

            for(int i=d;i<arr.length;i++){

                //遍历本组中的所有元素

                for(int j=i-d;j>=0;j-=d){

                   //如果当前元素大于加上步长后的那个元素

                    if(arr[j]>arr[j+d]){

                        int temp=arr[j];

                        arr[j]=arr[j+1];

                        arr[j+1]=temp;

                    }

                }

            }

            System.out.println("第"+k+"次排序结果:"+Arrays.toString(arr));

            k++;  

        }

    }

}

(5)堆排序

package demo2;

import java.util.Arrays;

/**

 * Created by chengxiao on 2016/12/17.

 * 堆排序demo

 */

public class HeapSort {

    public static void main(String []args){

        int []arr = {9,8,7,6,5,4,3,2,1};

        sort(arr);

        System.out.println(Arrays.toString(arr));

    }

    public static void sort(int []arr){

        //1.构建大顶堆

        for(int i=arr.length/2-1;i>=0;i--){

            //从第一个非叶子结点从下至上,从右至左调整结构

            adjustHeap(arr,i,arr.length);

        }

        //2.调整堆结构+交换堆顶元素与末尾元素

        for(int j=arr.length-1;j>0;j--){

            swap(arr,0,j);//将堆顶元素与末尾元素进行交换

            adjustHeap(arr,0,j);//重新对堆进行调整

        }

 

    }

    /**

     * 调整大顶堆(仅是调整过程,建立在大顶堆已构建的基础上)

     * @param arr

     * @param i

     * @param length

     */

    public static void adjustHeap(int []arr,int i,int length){

        int temp = arr[i];//先取出当前元素i

        for(int k=i*2+1;k<length;k=k*2+1){//从i结点的左子结点开始,也就是2i+1处开始

            if(k+1<length && arr[k]<arr[k+1]){//如果左子结点小于右子结点,k指向右子结点

                k++;

            }

            if(arr[k] >temp){//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)

                arr[i] = arr[k];

                i = k;

            }else{

                break;

            }

        }

        arr[i] = temp;//将temp值放到最终的位置

    }

    /**

     * 交换元素

     * @param arr

     * @param a

     * @param b

     */

    public static void swap(int []arr,int a ,int b){

        int temp=arr[a];

        arr[a] = arr[b];

        arr[b] = temp;

    }

}

(6)插入排序

package demo2;

import java.util.Arrays;

public class InserSort {

    public static void main(String[] args) {

        int[] arr=new int[] {3,5,7,1,9,0,2,4};

        insertSort(arr);

        System.out.println(Arrays.toString(arr));

    }

    public static void insertSort(int[] arr){

        //遍历所有的数字

        for(int i=1;i<arr.length;i++){

            //如果当前数字比前一个数字小

            if(arr[i]<arr[i-1]){

                //把当前遍历的数字存起来

                int temp=arr[i];

                int j;

                //遍历当前数字前面所有的数字

                for(j=i-1;j>=0&&temp<arr[j];j--){

                    //把前一个数字赋给后一个数字

                    arr[j+1]=arr[j];

                }

                //把临时变量赋给(外层for循环的当前元素)不满足条件的最后一个元素

                arr[j+1]=temp;

            }

        }

    }

}

(7)归并排序

package demo2;

import java.util.Arrays;

public class MergeSort {

    public static void main(String[] args) {

        int [] arr=new int[]{2,4,6,3,8,1,9,0,3};

        System.out.println(Arrays.toString(arr));

            mergeSort(arr, 0, arr.length-1);

            System.out.println(Arrays.toString(arr));

    }

    //归并排序

    public static void mergeSort(int[]arr,int low,int high){

        int middle=(high+low)/2;

        if(low<high){

            //处理左边数据

            mergeSort(arr, low, middle);

            //处理右边数据

            mergeSort(arr, middle+1, high);

            //归并

            merge(arr, low, middle, high);

        }

       

    }

 public static void merge(int[]arr,int low,int middle,int high){

        //用于存储归并后的临时数组

        int[] temp=new int[high-low+1];

        //记录第一个数组中需要遍历的下标

        int i=low;

        //记录第二个数组中需要遍历的下标

        int j=middle+1;

        //用于存储临时数组中存放的下标

        int index=0;

        //遍历两个数组取出小的数字,放入临时数组中

        while(i<=middle&&j<=high){

            if(arr[i]<=arr[j]){

            //把小的数放入临时数组

            temp[index]=arr[i];

            //让下标向下移一位

            i++;

        }else{

            temp[index]=arr[j];

            j++;

        }

        index++;

    }

    //处理多余数据

    while(j<=high){

        temp[index]=arr[j];

        j++;

        index++;

    }

    while(i<=middle){

        temp[index]=arr[i];

        i++;

        index++;

    }

    //把临时数组中的数据重新存入原数组

    for(int k=0;k<temp.length;k++){

        arr[k+low]=temp[k];

    }

  }

}

(8)基数排序

package demo2;

import java.util.Arrays;

public class RadixSort {

    public static void main(String[] args) {

        int[] arr=new int[]{23,6,189,45,9,287,56,7,1,798,54,67,345};

        radixSort(arr);

        System.out.println(Arrays.toString(arr));

    }

    public static void radixSort(int[] arr){

        //存数组中最大的数字

        int max=Integer.MAX_VALUE;

        for(int i=0;i<arr.length;i++){

            if(arr[i]>max){

                max=arr[i];

            }

        }

        //计算最大数组是几位数

        int maxLength=(max+"").length();

        //用于临时存储数据的数组

        int[][] temp=new int[10][arr.length];

        //用于记录在temp中相应的数组中存放的数字的数量

        int [] counts=new int[10];

        //根据最大长度的数决定比较的次数

        for(int i=0,n=1;i<maxLength;i++,n*=10){

            //把每一个数字分别计算余数

            for(int j=0;j<arr.length;j++){

                //计算余数

                int ys=arr[j]/n%10;

                //把当前遍历的数据放入指定数组中

                temp[ys][counts[ys]]=arr[j];

                //记录数量

                counts[ys]++;

            }

            //记录取的元素需要放的位置

            int index=0;

            //把数字取出来

            for(int k=0;k<counts.length;k++){

                //记录数量的数组中当前余数记录的数量不为0

                if(counts[k]!=0){

                    for(int l=0;l<counts[k];l++){

                        //取出元素

                        arr[index]=temp[k][l];

                        //记录下一个位置

                        index++;

                    }

                    //把数量置位0

                    counts[k]=0;

                }

           

            }

                   

        }

       

    }

}

 

5、单例模式

(1)饿汉模式

package com.study.DesignPattern01;/** * 创建一个饿汉模式的单例 * @author ZLHome *有些对象,我们只需要一个,如果多了,那么就可能导致数据不一致,    占用资源过多等等,比如:        配置文件、工具类、线程池、缓存、日志对象 */public class Singleton {    //1、构造方法私有化(这样类就不能被实例化了)    private Singleton(){        System.out.println("实例化Singleton类");    }        //2、实例化单例对象,对象为静态的(这样就只会实例化一次),私有的(安全,外部不能直接Singleton.instance调用)    private static Singleton instance = new Singleton();        //3、提供一个静态方法(静态方法,类可以直接调用),用于获取单例    public static Singleton getInstance(){        return instance;    }} 

(2)懒汉模式

package com.study.DesignPattern01;

 

public class Singleton1 {

    //1、将构造方法设置为私有(这样类就不能被外部实例化了)

    private Singleton1(){

        System.out.println("实例化Singleton1");

    }

   

    //2、申明单例对象

    private static Singleton1 instance;

   

    //3、提供一个静态方法(静态方法属于类),用于外部调用

    public static Singleton1 getInstance(){

        if(instance==null){

            System.out.println("第一次实例化Singleton1对象");

            instance=new Singleton1();

        }

        return instance;

    }

}

加锁

public class Singleton{

    private static Singleton singleton;   

    private Singleton(){

    }

    public static Singleton getInstance(){

       if(singleton == null){

           synchronized(Singleton.class){

              if(singleton == null){

                  singleton = new Singleton();

              }

           }

       }

 

    return singleton;

    }

}

6、String、StringBuffer和StringBuilder区别

(1)长度是否可变

String 是被 final 修饰的,他的长度是不可变的,就算调用 String 的concat 方法,那也是把字符串拼接起来并重新创建一个对象,把拼接后的 String 的值赋给新创建的对象

StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象,StringBuffer 与 StringBuilder 中的方法和功能完全是等价的。调用StringBuffer 的 append 方法,来改变 StringBuffer 的长度,并且,相比较于 StringBuffer,String 一旦发生长度变化,是非常耗费内存的!

(2)执行效率

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

(3)应用场景

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

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

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

7、StringBuffer和StringBuilder区别

(1)是否线程安全

StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问),StringBuffer是线程安全的。只是StringBuffer 中的方法大都采用了 synchronized 关键字进行修饰,因此是线程安全的,而 StringBuilder 没有这个修饰,可以被认为是线程不安全的。

(2)应用场景

由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。

然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。 append方法与直接使用+串联相比,减少常量池的浪费。

8、数据库事务特性

·  原子性(Atomicity)

事务的原子性是指事务是一个不可分割的工作单位,这组操作要么全部发生,否则全部不发生。

·  一致性(Consistency)

在事务开始以前,被操作的数据的完整性处于一致性的状态,事务结束后,被操作的数据的完整性也必须处于一致性状态。拿银行转账来说,一致性要求事务的执行不应改变A、B 两个账户的金额总和。如果没有这种一致性要求,转账过程中就会发生钱无中生有,或者不翼而飞的现象。事务应该把数据库从一个一致性状态转换到另外一个一致性状态。

·  隔离性(Isolation)

事务隔离性要求系统必须保证事务不受其他并发执行的事务的影响,也即要达到这样一种效果:对于任何一对事务T1 和 T2,在事务 T1 看来,T2 要么在 T1 开始之前已经结束,要么在 T1 完成之后才开始执行。这样,每个事务都感觉不到系统中有其他事务在并发地执行。

·  持久性(Durability)

一个事务一旦成功提交,它对数据库的改变必须是永久的,即便是数据库发生故障也应该不回对其产生任何影响。

9、数据库三大范式

第一范式(1NF)

定义:如果关系模式R的每个关系r的属性都是不可分的数据项,那么就称R是第一范式的模式。

简单的说,每一个属性都是原子项,不可分割。

1NF是关系模式应具备的最起码的条件,如果数据库设计不能满足第一范式,就不称为关系型数据库。关系数据库设计研究的关系规范化是在1NF之上进行的。

例如(学生信息表):

学生编号 姓名 性别 联系方式

20080901 张三 男 email:zs@126.com,phone:88886666

20080902 李四 女 email:ls@126.com,phone:66668888

 

以上的表就不符合,第一范式:联系方式字段可以再分,所以变更为正确的是:

学生编号 姓名 性别 电子邮件 电话

20080901 张三 男 zs@126.com 88886666

20080902 李四 女 ls@126.com 66668888

第二范式(2NF)

定义:如果关系模式R是1NF,且每个非主属性完全函数依赖于候选键,那么就称R是第二范式。

简单的说,第二范式要满足以下的条件:首先要满足第一范式,其次每个非主属性要完全函数依赖与候选键,或者是主键。也就是说,每个非主属性是由整个主键函数决定的,而不能由主键的一部分来决定。

例如(学生选课表):

学生 课程 教师 教师职称 教材 教室 上课时间

李四 Spring 张老师 java讲师 《Spring深入浅出》 301 08:00

张三 Struts 杨老师 java讲师 《Struts in Action》 302 13:30

这里通过(学生,课程)可以确定教师、教师职称,教材,教室和上课时间,所以可以把(学生,课程)作为主键。但是,教材并不完全依赖于(学生,课程),只拿出课程就可以确定教材,因为一个课程,一定指定了某个教材。这就叫不完全依赖,或者部分依赖。出现这种情况,就不满足第二范式。

修改后,选课表:

学生 课程 教师 教师职称 教室 上课时间

李四 Spring 张老师 java讲师 301 08:00

张三 Struts 杨老师 java讲师 302 13:30

课程表:

课程 教材

Spring 《Spring深入浅出》

Struts 《Struts in Action》

所以,第二范式可以说是消除部分依赖。第二范式可以减少插入异常,删除异常和修改异常。

第三范式(3NF)

定义:如果关系模式R是2NF,且关系模式R(U,F)中的所有非主属性对任何候选关键字都不存在传递依赖,则称关系R是属于第三范式。

简单的说,第三范式要满足以下的条件:首先要满足第二范式,其次非主属性之间不存在函数依赖。由于满足了第二范式,表示每个非主属性都函数依赖于主键。如果非主属性之间存在了函数依赖,就会存在传递依赖,这样就不满足第三范式。

上例中修改后的选课表中,一个教师能确定一个教师职称。这样,教师依赖于(学生,课程),而教师职称又依赖于教师,这叫传递依赖。第三范式就是要消除传递依赖。

修改后,选课表:

学生 课程 教师 教室 上课时间

李四 Spring 张老师 301 08:00

张三 Struts 杨老师 302 13:30

教师表:

教师 教师职称

张老师 java讲师

杨老师 java讲师

这样,新教师的职称在没被选课的时候也有地方存了,没人选这个教师的课的时候教师的职称也不至于被删除,修改教师职称时只修改教师表就可以了。

简单的说,

第一范式就 是原子性,字段不可再分割;

第二范式就是完全依赖,没有部分依赖;

第三范式就是没有传递依赖。

10、redis介绍

(1)怎么理解Redis事务

事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。

11、实现多线程

1、继承Thread类创建线程

Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。

2、实现Runnable接口创建线程

如果自己的类已经extends另一个类,就无法直接extends Thread,此时,可以实现一个Runnable接口

3、实现Callable接口通过FutureTask包装器来创建Thread线程

4、使用ExecutorService、Callable、Future实现有返回结果的线程

ExecutorService、Callable、Future三个接口实际上都是属于Executor框架。返回结果的线程是在JDK1.5中引入的新特征,有了这种特征就不需要再为了得到返回值而大费周折了。而且自己实现了也可能漏洞百出。

可返回值的任务必须实现Callable接口。类似的,无返回值的任务必须实现Runnable接口。

执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了。

注意:get方法是阻塞的,即:线程无返回结果,get方法会一直等待。

再结合线程池接口ExecutorService就可以实现传说中有返回结果的多线程了

12、JAVA多线程中start方法与run方法区别

1.start()方法来启动线程,真正实现了多线程运行。这时无需等待run方法体代码执行完毕,可以直接继续执行下面的代码;通过调用Thread类的start()方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运行。 然后通过此Thread类调用方法run()来完成其运行操作的, 这里方法run()称为线程体,它包含了要执行的这个线程的内容, Run方法运行结束, 此线程终止。然后CPU再调度其它线程。

2.run()方法当作普通方法的方式调用。程序还是要顺序执行,要等待run方法体执行完毕后,才可继续执行下面的代码; 程序中只有主线程——这一个线程, 其程序执行路径还是只有一条, 这样就没有达到写线程的目的。

13、swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上?

switch(expr1)中,expr1是一个整数表达式。因此传递给 switch 和 case 语句的参数应该是 int、 short、 char 或者 byte。long,string 都不能作用于swtich。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值