最全的java面试题,java基础,mysql,jvm,计算机网络,vue

javase知识面试题

1.基础知识

1.Integer与int的区别

int是java提供的8种原始数据类型之一,Java为每个原始类型提供了封装类,Integer是java为int提供的封装类。

int的默认值为0,而Integer的默认值为null,即Integer可以区分出未赋值和值为0的区别,int则无法表达出未赋值的情况,例如,要想表达出没有参加考试和考试成绩为0的区别,则只能使用Integer。在JSP开发中,Integer的默认为null,所以用el表达式在文本框中显示时,值为空白字符串,而int默认的默认值为0,所以用el表达式在文本框中显示时,结果为0,所以,int不适合作为web层的表单数据的类型。

另外,Integer提供了多个与整数相关的操作方法,例如,将一个字符串转换成整数,Integer中还定义了表示整数的 最大值和最小值的常量。

2.== 和 equals 的区别是什么?

  • 基本类型:比较的是值是否相同;
  • 引用类型:比较的是地址是否相同;
String x = "string";
String y = "string";
String z = new String("string");
System.out.println(x==y); // true
System.out.println(x==z); // false
System.out.println(x.equals(y)); // true
System.out.println(x.equals(z)); // true

equals 本质上就是 ==,只不过 String 和 Integer 等重写了 equals 方法

那问题来了,两个相同值的 String 对象,为什么返回的是 true?代码如下:

String s1 = new String("老王");
String s2 = new String("老王");
System.out.println(s1.equals(s2)); // true

equals的底层代码如下:

 public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        } else {
            if (anObject instanceof String) {
                String aString = (String)anObject;
                if (this.coder() == aString.coder()) {
                    return this.isLatin1() ? StringLatin1.equals(this.value, aString.value) : StringUTF16.equals(this.value, aString.value);
                }
            }

            return false;
        }
    }

@HotSpotIntrinsicCandidate
public static boolean equals(byte[] value, byte[] other) {
    if (value.length == other.length) {
        for(int i = 0; i < value.length; ++i) {
            if (value[i] != other[i]) {
                return false;
            }
        }

        return true;
    } else {
        return false;
    }
}

3.两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?

不对,两个对象的 hashCode()相同,equals()不一定 true。

String str1 = "通话";
String str2 = "重地";
System.out.println(String.format("str1:%d | str2:%d",  str1.hashCode(),str2.hashCode()));
System.out.println(str1.equals(str2));
public int hashCode() {
    int h = this.hash;
    if (h == 0 && this.value.length > 0) {
        this.hash = h = this.isLatin1() ? StringLatin1.hashCode(this.value) : StringUTF16.hashCode(this.value);
    }
    return h;
}
public static int hashCode(byte[] value) {
    int h = 0;
    int length = value.length >> 1;

    for(int i = 0; i < length; ++i) {
        h = 31 * h + getChar(value, i);
    }
    return h;
}

运行结果

str1:1179395 | str2:1179395

false

代码解读:很显然“通话”和“重地”的 hashCode() 相同,然而 equals() 则为 false,因为在散列表中,hashCode()相等即两个键值对的哈希值相等,然而哈希值相等,并不一定能得出键值对相等。

3.1 hashcode是什么?

​ 答:就是在hash表中对应的位置

​ hashcode是通过hash函数得来的,通俗的说,就是通过某一种算法得到的

​ 通过对象的内存地址(也就是物理地址)转换成一个整数,然后该整数通过hash函数的算法得到hashcode

3.1.1加法hash

所谓的加法Hash就是把输入元素一个一个的加起来构成最后的结果。

这里的prime是任意的质数,看得出,结果的值域为[0,prime-1]。

static int additiveHash(String key, int prime)
 {
  int hash, i;
  for (hash = key.length(), i = 0; i < key.length(); i++)
   hash += key.charAt(i);
  return (hash % prime);
 }

4.2*8的最高效率算法

使用位运算来实现效率最高。位运算符是对操作数以二进制比特位为单位进行操作和运算,操作数和结果都是整型 数。

对于位运算符“<<”, 是将一个数左移n位,就相当于乘以了2的n次方,那么,一个数乘以8只要将其左移3位即可,位运算cpu直接支持的,效率最高。所以,2乘以8等于几的最效率的方法是2 << 3

5.final和abstract关键字的作用

final和abstract是功能相反的两个关键字,可以对比记忆

abstract可以用来修饰类和方法,不能用来修饰属性和构造方法;

使用abstract修饰的类是抽象类,需要被继承,使用abstract修饰的方法是抽象方法,需要子类被重写。

final可以用来修饰类、方法和属性,不能修饰构造方法。

使用final修饰的类不能被继承,使用final修饰的方法不能被重写,使用final修饰的变量的值不能被修改,所以就成了常量。

特别注意:final修饰基本类型变量,其值不能改变,由原来的变量变为常量;但是final修饰引用类型变量,栈内存中的引用不能改变,但是所指向的堆内存中的对象的属性值仍旧可以改变。

6. final 在 java 中有什么作用?

  • final 修饰的类叫最终类,该类不能被继承。

  • final 修饰的方法不能被重写。

  • final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。

7.final、finally、finalize的区别

final修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承例如:String 类、Math类等。将变量或方法声明为final,可以保证它们在使用中不被改变。被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。被声明为final的方法也同样只能使用,不能重写,但是能够重载。使用final修饰的对象,对象的引用地址不能变,但是对象的值可以变!

finally在异常处理时提供 finally 块来执行任何清除操作。如果有finally的话,则不管是否发生异常,finally语句都会被执行。一般情况下,都把关闭物理连接(IO流、数据库连接、Socket连接)等相关操作,放入到此代码块中.。

finalize方法名。Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要清理工作。finalize() 方法是在垃圾收集器删除对象之前被调用的。它是在 Object 类中定义的,因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。 一般情况下,此方法由JVM调用,程序员不要去调用!

8.写出java.lang.Object类的六个常用方法

(1) public boolean equals(java.lang.Object)比较对象的地址值是否相等,如果子类重写,则比较对象的内容是否相等;

(2) public native int hashCode() 获取哈希码

(3) public java.lang.String toString() 把数据转变成字符串

(4) public final native java.lang.Class getClass() 获取类结构信息

(5) protected void finalize() throws java.lang.Throwable垃圾回收前执行的方法

(6)protected native Object clone() throws java.lang.CloneNotSupportedException 克 隆

(7)public final void wait() throws java.lang.InterruptedException多线程中等待功能

(8)public final native void notify() 多线程中唤醒功能

(9)public final native void notifyAll() 多线程中唤醒所有等待线程的功能

9. java 中的 Math.round(-1.5) 等于多少?

等于 -1,因为在数轴上取值时,中间值(0.5)向右取整,所以正 0.5 是往上取整,负 0.5 是直接舍弃。

10. String 属于基础的数据类型吗?

String 不属于基础类型

基础类型有 8 种:byte、boolean、char、short、int、float、long、double,

而 String 属于对象。~

11. java 中操作字符串都有哪些类?它们之间有什么区别?

操作字符串的类有:String、StringBuffer、StringBuilder。

String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象

StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。

StringBuffer 和 StringBuilder 最大的区别在于

  • StringBuffer 是线程安全的,

  • 而 StringBuilder 是非线程安全的,

  • 但 StringBuilder 的性能却高于 StringBuffer,

所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。

12. String str="i"与 String str=new String(“i”)一样吗?

不一样,因为内存的分配方式不一样。String str="i"的方式,java 虚拟机会将其分配到常量池中;

而 String str=new String(“i”) 则会被分到堆内存中。

13. 如何将字符串反转?

使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。

// StringBuffer reverse
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("abcdefg");
System.out.println(stringBuffer.reverse()); // gfedcba
// StringBuilder reverse
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("abcdefg");
System.out.println(stringBuilder.reverse()); // gfedcba

14. String 类的常用方法都有那些?

indexOf():返回指定字符的索引。
charAt():返回指定索引处的字符。
replace():字符串替换。
trim():去除字符串两端空白。
split():分割字符串,返回一个分割后的字符串数组。
getBytes():返回字符串的 byte 类型数组。
length():返回字符串长度。
toLowerCase():将字符串转成小写字母。
toUpperCase():将字符串转成大写字符。
substring():截取字符串。
equals():字符串比较

15.抽象类必须要有抽象方法吗?

不需要,抽象类不一定非要有抽象方法。

示例代码:

abstract class Cat {
    public static void sayHi() {
        System.out.println("hi~");
    }
}

16.普通类和抽象类有哪些区别?

  • 普通类不能包含抽象方法,抽象类可以包含抽象方法。
  • 抽象类不能直接实例化,普通类可以直接实例化。

17.抽象类能使用 final 修饰吗?

不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象

18.接口和抽象类有什么区别?

实现:抽象类的子类使用 extends 来继承;接口必须使用 implements 来实现接口。
构造函数:抽象类可以有构造函数;接口不能有。
main 方法:抽象类可以有 main 方法,并且我们能运行它;接口不能有 main 方法。
实现数量:类可以实现很多个接口;但是只能继承一个抽象类。
访问修饰符:接口中的方法默认使用 public 修饰;抽象类中的方法可以是任意访问修饰符。

19.java.sql.Date和java.util.Date的联系和区别

1) java.sql.Date是java.util.Date的子类,是一个包装了毫秒值的瘦包装器,允许 JDBC 将毫秒值标识为 SQL DATE 值。毫秒值表示自 1970 年 1 月 1 日 00:00:00 GMT 以来经过的毫秒数。 为了与 SQL DATE 的定义一致,由java.sql.Date 实例包装的毫秒值必须通过将时间、分钟、秒和毫秒设置为与该实例相关的特定时区中的零来“规范化”。 说白了,java.sql.Date就是与数据库Date相对应的一个类型,而java.util.Date是纯java的Date。

2) JAVA里提供的日期和时间类java.sql.Date和java.sql.Timestamp,只会从数据库里读取某部分值,这有时会 导致丢失数据。例如一个包含2002/05/22 5:00:57 PM的字段,读取日期时得到的是2002/05/22,而读取时间时得到的是5:00:57 PM. 你需要了解数据库里存储时间的精度。有些数据库,比如MySQL,精度为毫秒,然而另一些数据

库,包括Oracle,存储SQL DATE类型数据时,毫秒部分的数据是不保存的。

以下操作中容易出现不易被发现的BUG:获得一个JAVA里的日期对象。 从数据库里读取日期 , 试图比较两个日期对 象是否相等。如果毫秒部分丢失,本来认为相等的两个日期对象 , 用Equals方法可能返回false。.sql.Timestamp类比java.util.Date类精确度要高。

java.sql.Date 和java.util.Date 最大的不同在于java.sql.Date 只记录日期,而没有具体这一天的时间。所以举例来说,如果当前是2009-12-24 23:20,你创建一个 java.sql.Date 将只记下2009-12-24这个信息。若你需要保留时间进行JDBC操作,请使用 java.sql.Timestamp 代替。

总之,java.util.Date 就是Java的日期对象,而java.sql.Date 是针对SQL语句使用的,只包含日期而没有时间部分。

20.java 中 IO 流分为几种?

按功能来分:输入流(input)、输出流(output)。

按类型来分:字节流和字符流。

字节流和字符流的区别是:字节流按 8 位传输以字节为单位输入输出数据,字符流按 16 位传输以字符为单位输入输出数据。

21. Files的常用方法都有哪些?

Files.exists():检测文件路径是否存在。
Files.createFile():创建文件。
Files.createDirectory():创建文件夹。
Files.delete():删除一个文件或目录。
Files.copy():复制文件。
Files.move():移动文件。
Files.size():查看文件个数。
Files.read():读取文件。
Files.write():写入文件。

22. &和&&的区别?

虽然二者都要求运算符左右两端的布尔值都是true整个表达式的值才是true。

&&之所以称为短路运算是因为,如果&&左边的表达式的值是false,右边的表达式会被直接短路掉,不会进行运算。很多时候我们可能都需要用&&而不是&,例如在验证用户登录时判定用户名不是null而且不是空字符串,应当写为:username != null &&!username.equals(“”),二者的顺序不能交换,更不能用&运算符,因为第一个条件如果不成立,根本不能进行字符串的equals比较,否则会产生NullPointerException异常。注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此。

23. switch 是否能作用在byte 上,是否能作用在long 上,是否能作用在String上?

可以是byte、short、char、int、enum、String类型,但是long类型不能

24、构造器(constructor)是否可被重写(override)?

答:构造器不能被继承,因此不能被重写,但可以被重载。

25. Java 重载与重写是什么?有什么区别?

答:

重载(Overload)是让类以统一的方式处理不同类型数据的一种手段,实质表现就是多个具有不同的参数个数或者类型的同名函数(返回值类型可随意,不能以返回类型作为重载函数的区分标准)同时存在于同一个类中,是一个类中多态性的一种表现(调用方法时通过传递不同参数个数和参数类型来决定具体使用哪个方法的多态性)。

重写(Override)是父类与子类之间的多态性,实质是对父类的函数进行重新定义,如果在子类中定义某方法与其父类有相同的名称和参数则该方法被重写,不过子类函数的访问修饰权限不能小于父类的;若子类中的方法与父类中的某一方法具有相同的方法名、返回类型和参数表,则新方法将覆盖原有的方法,如需父类中原有的方法则可使用 super 关键字。

26. 是否可以继承String类?

答:String 类是final类,不可以被继承。

27. String s = new String(“xyz”);创建了几个字符串对象?

答:两个对象,一个是静态区的”xyz”,一个是用new创建在堆上的对象。

28 什么是 Java 序列化?什么情况下需要序列化?

Java 序列化是为了保存各种对象在内存中的状态,并且可以把保存的对象状态再读出来。

以下情况需要使用 Java 序列化:

  • 想把的内存中的对象状态保存到一个文件中或者数据库中时候;
  • 想用套接字在网络上传送对象的时候;
  • 想通过RMI(远程方法调用)传输对象的时候。

29.try{}里有一个return语句,那么紧跟在这个try后的finally{}里的代码会不会被执行,什么时候被执行,在return前还是后?

会执行,在方法返回调用者前执行。

在finally中改变返回值的做法是不好的,因为如果存在finally代码块,try中的return语句不会立马返回调用者,而是记录下返回值待finally代码块执行完毕之后再向调用者返回其值,然后如果在finally中修改了返回值,就会返回修改后的值

2.异常

1.什么是异常?

异常是发生在程序执行过程中阻碍程序正常执行的错误事件。比如:用户输入错误数据、硬件故障、网络阻塞等都会导致出现异常。 只要在Java语句执行中产生了异常,一个异常对象就会被创建,JRE就会试图寻找异常处理程序来处理异常。如果有合适的异常处理程序,异常对象就会被异常处理程序接管,否则,将引发运行环境异常,JRE终止程序执行。 Java异常处理框架只能处理运行时错误,编译错误不在其考虑范围之内。

2.常见的异常

NullPointException:空指针异常,对象是null时会抛出,在调用传入对象时尽量判断是否为null,Jdk8里面可以用Optional对象来避免

IndexOutOfBoundsException:数组下标越界,数组的下标超过了最大值时会抛出,在迭代循环时检查下标是否越界

NumberFormatException:数字类型转化异常,将非数字类型转成数字类型,将类型转化的代码catch住

ClassCastException:类型转换异常,发生在强转时,将不同类型转成同一类型,尽量少用强转,或用instanceof(判断继承中子类的实例是否是父类的实现)做类型判断,或多用泛型

FileNotFoundException:找不到指定文件,文件路径错误或文件不存在,可能用了绝对路径检查文件是否存在,路径是否写错,多用相对路径

ClassNotFoundException:在classpath中找不到引用的类缺乏引用当前类的jar或没有设置classpath或jar损坏-,找到jar并放入classpath中或检查jar是否损坏

OutOfMemoryError:内存溢出异常,产生对象太多,内存不够->不要在循环体重创建大量对象,或对象及时回收,增大初始化堆:-Xms 增加最大值:-Xmx

NoClassDefFoundError:找不到相应的类错误,缺乏当前引用类的jar或jar版本不对->找到jar并放入classpath中或找到合适的版本

ConcurrentModificationException:并发修改异常,在集合迭代时修改里面的元素->在迭代时不要修改集合或用并发集合做遍历(如:ConcurrentHashMap)

NoSuchMethodError:类里找不到相应的方法,一般是jar版本不对,当前引用的jar版本中没有这个方法->检查jar版本是否正确

UnsupportedClassVersionError:版本不支持错误,编译class的jdk和运行时候的jdk版本不一致或比较高->将低版本换成高版本

StackOverflowError:栈溢出错误,一般是函数的死循环,或递归调用无法退出->检查死循环的代码,或让递归有退出值,或加大栈初始化参数

3. 编译时异常和运行时异常的区别

最简单的说法:

javac出来的异常就是编译时异常,就是说把源代码编译成字节码(class)文件时报的异常,一般如果用Eclispe,你敲完代码保存的时候就是编译的时候。Java出来的异常就是运行时异常

Java异常可分为3种:

(1)编译时异常:Java.lang.Exception

(2)运行期异常:Java.lang.RuntimeException

(3)错误:Java.lang.Error

编译时异常: 程序正确,但因为外在的环境条件不满足引发。例如:用户错误及I/O问题----程序试图打开一个并不存在的远程Socket端口。这不是程序本身的逻辑错误,而很可能是远程机器名字错误(用户拼写错误)。对商用软件系统,程序开发者必须考虑并处理这个问题。Java编译器强制要求处理这类异常,如果不捕获这类异常,程序将不能被编译。

运行期异常: 这意味着程序存在bug,如数组越界,0被除,入参不满足规范…这类异常需要更改程序来避免,Java编译器强制要求处理这类异常。

错误: 一般很少见,也很难通过程序解决。它可能源于程序的bug,但一般更可能源于环境问题,如内存耗尽。错误在程序中无须处理,而有运行环境处理。

4.java中两种处理方式

一:通过try catch捕捉异常,顺序上先处理子类异常后父类,非特殊情况catch中不能为空,要处理错误,避免使用catch all(不能很准确的定位错误),尽量用catch,jdk7中可以处理并列异常,这个方法虽然简洁,但是也不够好(不同异常处理方法是一致的;多个异常间必须是平级关系)。清理数据必须放到finally里面,无论是否抛异常,或try中有return语句,finally里面的代码一定会执行,除非碰到System.exit(0)(艾克色特)

二:通过throws抛出异常,子类方法中抛出的异常应该是与父类方法中相同,或是父类异常的子类;一定要在main方法里面处理异常,不然jvm可能就退出了

5.常见方法

getMessage:错误信息的字符串解释

getCause:返回异常产生的原因,一般是原始异常如果不知道原因返回null

printStackTrace:打印异常出现的位置或原因

toString:返回String格式的Throwable信息,此信息包括Throwable的名字和本地化信息

initCause:初始化原始异常

PrintStream和PrintWriter作为产生实现重载,这样就能实现打印栈轨迹到文件或流中

6.如何自定义异常

继承Exception是检查性异常,继承RuntimeException是非检查性异常,

一般要复写两个构造方法,用throw抛出新异常

如果同时有很多异常抛出,那可能就是异常链,就是一个异常引发另一个异常,另一个异常引发更多异常,一般我们会找它的原始异常来解决问题,一般会在开头或结尾,异常可通过initCause串起来,可以通过自定义异常

7.文件File,io流

常见方法:

通过将给定路径来创建一个新File实例。
new File(String pathname);   
根据parent路径名字符串和child路径名创建一个新File实例。parent是指上级目录的路径,完整的路径为parent+child.
new File(String parent, String child);      
根据parent抽象路径名和child路径名创建一个新File实例。 parent是指上级目录的路径,完整的路径为parent.getPath()+child.
说明:如果指定的路径不存在(没有这个文件或是文件夹),不会抛异常,这时file.exists()返回false。
new File(File parent, String child);

创建:

//在指定位置创建一个空文件,成功就返回true,如果已存在就不创建然后返回false
createNewFile() 
//在指定位置创建目录,这只会创建最后一级目录,如果上级目录不存在就抛异常。
mkdir()
//在指定位置创建目录,这会创建路径中所有不存在的目录。
mkdirs()
//重命名文件或文件夹,也可以操作非空的文件夹,文件不同时相当于文件的剪切,剪切时候不能操作非空的文件夹。移动/重命名成功则返回true,失败则返回false。
renameTo(File dest)

删除:

//删除文件或一个空文件夹,如果是文件夹且不为空,则不能删除,成功返回true,失败返回false。
delete() 
//在虚拟机终止时,请求删除此抽象路径名表示的文件或目录,保证程序异常时创建的临时文件也可以被删除
deleteOnExit()  

判断:

//文件或文件夹是否存在。
exists()
//是否是一个文件,如果不存在,则始终为false。
isFile()
//是否是一个目录,如果不存在,则始终为false。
isDirectory()
//是否是一个隐藏的文件或是否是隐藏的目录。
isHidden()
//测试此抽象路径名是否为绝对路径名。
isAbsolute()

获取:

//获取文件或文件夹的名称,不包含上级路径。
getName()
//返回绝对路径,可以是相对路径,但是目录要指定
getPath()
//获取文件的绝对路径,与文件是否存在没关系
getAbsolutePath()
// 获取文件的大小(字节数),如果文件不存在则返回0L,如果是文件夹也返回0L。
length()
//返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回null。
getParent()
//获取最后一次被修改的时间。
lastModified()
//列出所有的根目录(Window中就是所有系统的盘符)
staic File[] listRoots()
//返回目录下的文件或者目录名,包含隐藏文件。对于文件这样操作会返回null。
list() 
//返回指定当前目录中符合过滤条件的子文件或子目录。对于文件这样操作会返回null。
list(FilenameFilter filter) 
//返回目录下的文件或者目录对象(File类实例),包含隐藏文件。对于文件这样操作会返回null。
listFiles()
//返回指定当前目录中符合过滤条件的子文件或子目录。对于文件这样操作会返回null
listFiles(FilenameFilter filter)

题目:

1.在指定的路径下新建一个 .txt 文件 “test.txt”,利用程序在文件中写入如下内容:

private static void work1() throws IOException {
        //要求:
        //创建文件
        File file = new File("test.txt");
        file.createNewFile();
        //写入数据
        FileWriter fwriter = new FileWriter(file);
        fwriter.write("Java是一种可以撰写跨平台应用软件的面向对象的程序设计语言.....");
        fwriter.close();
}

2.利用程序读取 test.txt 文件的内容, 并在控制台打印

 private static void work2() throws IOException {
        /**
         * 2.利用程序读取 test.txt 文件的内容, 并在控制台打印
         */
        FileReader reader = new FileReader("test.txt");
        //采用数组来缓冲
        char[] c =new char[100];
        int len ;
        while ((len=reader.read(c))!=-1){
            //遍历数组
            for (int i=0;i<len;i++) {
                System.out.print(c[i]);
            }
		}
 }

3.利用程序复制 test.txt 为 test1.txt

private static void work3() throws IOException {
         /*
        3.利用程序复制 test.txt 为 test1.txt
        因为文件类型是文本文档,所以选择字符流
         */
        File file = new File("test.txt");
        FileReader fr = new FileReader(file);
        FileWriter fw = new FileWriter("test1.txt");
        int len;
        char[] chars = new char[10];
        while((len = fr.read(chars)) != -1){
            fw.write(chars, 0, len);
        }
        fr.close();
        fw.close();
}

4.列出当前目录下全部java文件的名称

 private static void work4() {
        /**
         * 4.列出当前目录下全部java文件的名称
         * 考点:
         *    1.File的list/listFile方法
         *    2.过滤器FilenameFilter/Filefilter
         */
        File file = new File("D:\\AAtemp");
        String[] filenames = file.list(new FilenameFilter() {
            @Override
            //accept会对文件夹的每一个子文件夹进行检测
            public boolean accept(File dir, String name) {
                return name.endsWith(".java");
            }
        });
        for (String f :filenames)
            System.out.println(f);
}

5.列出workspace目录下.class的名字及其大小,并删除其中的一个?

 private static void work5() {
        //列出workspace目录下.class的名字及其大小,并删除其中的一个?
        File file = new File("C:\\Test");
        File file2 = new File("C:\\Test\\WiFi_Log.txt");
        File[] file1 = file.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return name.endsWith(".txt");
            }
        });
        file2.delete();
        for (File l : file1) {
            System.out.println(l);
        }
    }

4.工厂设计模式

1.几个常用的设计模式:

创建型

  • 工厂模式与抽象工厂模式 (Factory Pattern)(Abstract Factory Pattern)

  • 单例模式 (Singleton Pattern)

  • 建造者模式 (Builder Pattern)

  • 原型模式 (Prototype Pattern)

结构型

  • 适配器模式 (Adapter Pattern)
  • 装饰器模式 (Decorator Pattern)
  • 桥接模式 (Bridge Pattern)
  • 外观模式 (Facade Pattern)
  • 代理模式 (Proxy Pattern)
  • 过滤器模式 (Filter、Criteria Pattern)
  • 组合模式 (Composite Pattern)
  • 享元模式 (Flyweight Pattern)

行为型

  • 责任链模式(Chain of Responsibility Pattern)

  • 观察者模式(Observer Pattern)

  • 模板模式(Template Pattern)

  • 命令模式(Command Pattern)

  • 解释器模式(Interpreter Pattern)

  • 迭代器模式(Iterator Pattern)

  • 中介者模式(Mediator Pattern)

  • 策略模式(Strategy Pattern)

  • 状态模式(State Pattern)

  • 备忘录模式(Memento Pattern)

  • 空对象模式(Null Object Pattern)

**工厂模式:**工厂类可以根据条件生成不同的子类实例,这些子类有一个公共的抽象父类并且实现了相同的方法,但是这些方法针对不同的数据进行了不同的操作(多态方法)。当得到子类的实例后,开发人员可以调用基类中的方法而不必考虑到底返回的是哪一个子类的实例。

**代理模式:**给一个对象提供一个代理对象,并由代理对象控制原对象的引用。

**适配器模式:**把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起使用的类能够一起工作。

**单例模式:**一个类只有一个实例,即一个类只有一个对象实例。

img

2.简单工厂和抽象工厂有什么区别?

  • 简单工厂:用来生产同一等级结构中的任意产品,对于增加新的产品,无能为力。
  • 工厂方法:用来生产同一等级结构中的固定产品,支持增加任意产品。
  • 抽象工厂:用来生产不同产品族的全部产品,对于增加新的产品,无能为力;支持增加产品族。

3.手写两个单例模式

饿汉式

class Singleton {
 private static Singleton instance=new Singleton();
 private Singleton(){}
 static Singleton getInstance() {
 return instance;
 }
}

懒汉式

class Singleton {
 private static Singleton instance=null;
 private Singleton(){}
 static Singleton getInstance() {
 if(instance==null)
 instance=new Singleton();
 return instance;
 }
}

5.集合面试题

集合的基本概念:

​ 1.一个存储对象的容器
​ 2.集合只能存放对象
​ 3.集合存放的都是多个不同类型的对象
​ 4.对象本身还是存放在堆内存中

  • List集合的使用:
    1.List:有序的,可重复,有下标
    2.ArrayList:数组结构,带下标,长度可变,查询速度快,增删速度较慢
    3.LinkedList:链表结构,查询速度较慢,增删速度快

  • map集合的使用:
    1.Map 集合一一对应(键值对)
    2.基于数组和链表进行存储数据
    3.HashMap 类按哈希算法来存取键对象
    4.TreeMap是基于红黑树实现的,适用于按自然顺序遍历key。

  • Set集合的使用:
    1.Set集合 无序,不重复
    2.无序性(没下标),不能用for循环去遍历,可以用迭代器
    3.HashSet:增加删除时效率高
    4.LinkedHashSet:查看检索时,效率比较高

一些不太常用的Map集合:TreeMap,HashTable

题目:已知数组存放一批QQ号码,QQ号码最。

长为11位,最短为5位String[] strs =
{“12345”,“67891”,“12347809933”,“98765432102”,“67891”,“12347809933”}。
将该数组里面的所有qq号都存放在LinkedList中,将list中重复元素删除,将list中所有元素分别用迭代器打印出来

public static void main(String[] args) {
	//定义数组存放qq号码
	String[] strs = {"12345","67891","12347809933","98765432102","67891","12347809933"};
	
	//定义LinkedList
	LinkedList<String> lis = new LinkedList<String>();
	
	//c.增强for循环遍历数组存到集合
	for(String value : strs){
		lis.add(value);
	}
	
	//d.依次拿到每一个与这一个后的元素进行比较,如果相同则移除
	for(int i = 0; i < lis.size();i++)
		for(int j = i + 1;j < lis.size();j++)
			if(lis.get(i).equals(lis.get(j)))
				lis.remove(j);
	//e.用迭代器遍历集合
	for(Iterator<String> it = lis.iterator();it.hasNext();){
		String value = it.next();
		System.out.println(value + "\t");
}

1.常见的集合有哪些?

答:Map接口和Collection接口是所有集合框架的父接口

  • Collection接口的子接口包括:Set接口和List接口

  • Map接口的实现类主要有:HashMap、TreeMap、Hashtable、ConcurrentHashMap以及Properties等

  • Set接口的实现类主要有:HashSet、TreeSet、LinkedHashSet等

  • List接口的实现类主要有:ArrayList、LinkedList、Stack以及Vector等

2. HashMap 和 Hashtable 有什么区别?

  • hashMap去掉了HashTable 的contains方法,但是加上了containsValue()和containsKey()方法。
  • hashTable同步的,而HashMap是非同步的,效率上比hashTable要高。
  • hashMap允许空键值,而hashTable不允许。
  • HashMap没有考虑同步,是线程不安全的;Hashtable使用了synchronized关键字,是线程安全的;
  • HashMap继承自AbstractMap类;而Hashtable继承自Dictionary类;

3. HashMap的数据结构是什么样子的?

答:HashMap其实也是一个线性的数组实现的,所以可以理解为其存储数据的容器就是一个线性数组。这可能让我们很不解,一个线性的数组怎么实现按键值对来存取数据呢?这里HashMap有做一些处理。

4. HashMap的存取实现

​ 既然是线性数组,为什么能随机存取?这里HashMap用了一个小算法,大致是这样实现:

//存储时:
int hash = key.hashCode();// 这个hashCode方法这里不详述,只要理解每个key的hash是一个固定的int值
int index = hash % Entry[].length;
Entry[index] = value;

//取值时:
int hash = key.hashCode();
int index = hash % Entry[].length*;
return Entry[index];

5. Collection和Collections的区别?

Collection是一个接口,它是Set、List等容器的父接口;

Collections是个一个工具类,提供了一系列的静态方法来辅助容器操作,这些方法包括对容器的搜索、排序、线程安全化等等。

6. List、Map、Set三个接口存取元素时,各有什么特点?

  • List以特定索引来存取元素,可以有重复元素。
  • Set不能存放重复元素(用对象的equals()方法来区分元素是否重复)。
  • Map保存键值对(key-value pair)映射,映射关系可以是一对一或多对一

7. List、Set、Map 之间的区别是什么?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yqw3QxH9-1593962979568)(C:\Users\33066\AppData\Roaming\Typora\typora-user-images\image-20200617133142870.png)]

8. 数组和链表的特点及区别

数组存储区间是连续的,占用内存严重,故空间复杂的很大。但数组的二分查找时间复杂度小,为O(1);

​ 数组的特点是:寻址容易,插入和删除困难;

链表存储区间离散,占用内存比较宽松,故空间复杂度很小,但时间复杂度很大,达O(N)。

​ 链表的特点是:寻址困难,插入和删除容易。

9.遍历集合的方法

List:

遍历List方法一:普通for循环

for(int i=0;i<list.size();i++){//list为集合的对象名
    String temp = (String)list.get(i);
    System.out.println(temp);
}

遍历List方法二:增强for循环(使用泛型!)

for (String temp : list) {
	System.out.println(temp);
}

遍历List方法三:使用Iterator迭代器(1)

for(Iterator iter= list.iterator();iter.hasNext();){
    String temp = (String)iter.next();
    System.out.println(temp);
}

遍历List方法四:使用Iterator迭代器(2)

Iterator  iter =list.iterator();
while(iter.hasNext()){
    Object  obj =  iter.next();
    iter.remove();//如果要遍历时,删除集合中的元素,建议使用这种方式!
    System.out.println(obj);
}

Set:

遍历Set方法一:增强for循环

for(String temp:set){
	System.out.println(temp);
}

遍历Set方法二:使用Iterator迭代器

for(Iterator iter = set.iterator();iter.hasNext();){
    String temp = (String)iter.next();
    System.out.println(temp);
}

Map:

遍历Map方法一:根据key获取value

Map<Integer, Man> maps = new HashMap<Integer, Man>();
Set<Integer>  keySet =  maps.keySet();
for(Integer id : keySet){
	System.out.println(maps.get(id).name);
}

遍历Map方法二:使用entrySet

Set<Entry<Integer, Man>>  ss = maps.entrySet();
for (Iterator iterator = ss.iterator(); iterator.hasNext();) {
    Entry e = (Entry) iterator.next(); 
    System.out.println(e.getKey()+"--"+e.getValue());
}

6.线程面试题

并行和并发

并行:在同一个时刻,有多个指令在单个CPU同时执行
并发:在同一个时刻,有多个指令在单个CPU交替执行

进程和线程

进程:正在运行的软件(就是操作系统中正在运行的一个应用程序)

独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位
动态性:进程的实质是程序的一次执行过程,进程是动态产生的,动态消亡的
并发性:任何进程都可以同其他进程一起并发执行(CPU在多个进程之间进行一个动态的切换)

线程:是进程中的单个顺序控制流,是一条执行路径(就是应用程序中做的事情)

  		单线程:一个线程如果只有一条执行路径,则称为单线程程序 	
  		多线程:一个进程如果有多条执行路径,则成为多线程程序

1.线程和进程的区别?

一个程序下至少有一个进程,一个进程下至少有一个线程,一个进程下也可以有多个线程来增加程序的执行速度。

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

一种是继承Thread类;

另一种是实现Runnable接口。

还有一种是callable

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

runnable 没有返回值,callable 可以拿到有返回值,callable 可以看作是 runnable 的补充。

3. synchronized关键字的用法?

synchronized关键字可以将对象或者方法标记为同步,

以实现对对象和方法的互斥访问,可以用synchronized(对象) { … }定义同步代码块,或者在声明方法时将synchronized作为方法的修饰符。

4.多线程中 synchronized 锁升级的原理是什么?

synchronized 锁升级原理:在锁对象的对象头里面有一个 threadid 字段,在第一次访问的时候 threadid 为空,

jvm 让其持有偏向锁,并将 threadid 设置为其线程 id,再次进入的时候会先判断 threadid 是否与其线程 id 一致,如果一致则可以直接使用此对象,如果不一致,则升级偏向锁为轻量级锁,通过自旋循环一定次数来获取锁,执行一定次数之后,如果还没有正常获取到要使用的对象,此时就会把锁从轻量级升级为重量级锁,此过程就构成了 synchronized 锁的升级。

锁的升级的目的:在 Java 6 之后优化 synchronized 的实现方式,使用了偏向锁升级为轻量级锁再升级到重量级锁的方式,从而减低了锁带来的性能消耗。

5.什么是死锁?

当线程 A 持有独占锁a,并尝试去获取独占锁 b 的同时,线程 B 持有独占锁 b,并尝试获取独占锁 a 的情况下,就会发生 AB 两个线程由于互相持有对方需要的锁,而发生的阻塞现象,我们称为死锁。

5.1 死锁的四个必要条件

互斥条件:一个资源每次只能被一个进程使用,即在一段时间内某 资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。

请求与保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源 已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。

不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能 由获得该资源的进程自己来释放(只能是主动释放)。

循环等待条件: 若干进程间形成首尾相接循环等待资源的关系

这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。

5.2. 死锁避免

  • 加锁顺序(线程按照一定的顺序加锁)

  • 加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)

  • 死锁检测

6.在 Java 程序中怎么保证多线程的运行安全?

  • 方法一:使用安全类,比如 Java. util. concurrent 下的类。
  • 方法二:使用自动锁 synchronized。
  • 方法三:使用手动锁 Lock。

7.sleep() 和 wait() 有什么区别?

  • 类的不同:sleep() 来自 Thread,wait() 来自 Object。

  • 释放锁:sleep() 不释放锁;wait() 释放锁。

  • 用法不同:sleep() 时间到会自动恢复;wait() 可以使用 notify()/notifyAll()直接唤醒。

  • sleep()方法(休眠)是线程类(Thread)的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会让给其他线程,但是对象的锁依然保持,因此休眠时间结束后会自动恢复。

  • wait()是Object类的方法,调用对象的wait()方法导致当前线程放弃对象的锁(线程暂停执行),进入对象的等待池,只有调用对象的notify()方法(或notifyAll()方法)时才能唤醒等待池中的线程进入等锁池(lock pool),如果线程重新获得对象的锁就可以进入就绪状态。

8.线程的sleep()方法和yield()方法有什么区别?

① sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;
② 线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态;
③ sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常;

④ sleep()方法比yield()方法(跟操作系统CPU调度相关)具有更好的可移植性。

9.线程同步和互斥的区别

  1. 互斥是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
  2. 同步是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。
  3. 同步其实已经实现了互斥,所以同步是一种更为复杂的互斥。
  4. 互斥是一种特殊的同步。

所谓互斥,就是不同线程通过竞争进入临界区(共享的数据和硬件资源),为了防止访问冲突,在有限的时间内只允许其中之一独占性的使用共享资源。如不允许同时写

同步关系则是多个线程彼此合作,通过一定的逻辑关系来共同完成一个任务。一般来说,同步关系中往往包含互斥,同时对临界区的资源会按照某种逻辑顺序进行访问。如先生产后使用

总的来说,两者的区别就是:
互斥是通过竞争对资源的独占使用,彼此之间不需要知道对方的存在,执行顺序是一个乱序。
同步是协调多个相互关联线程合作完成任务,彼此之间知道对方存在,执行顺序往往是有序的。

lock与unlock方法,替换synchronized,这就是互斥锁的体现。消费者生产者模式就是同步锁的体现。

10.线程的实现

三种方式的对比:
在这里插入图片描述


方式1.继承Thread类

1> 定义一个类MyThread继承Thread类
2> 在MyThread类中从写run()方法
3> 创建MyThread类的对象
4> 启动线程

/**
 * @program: javase
 * @description: 继承Thread类实现多线程
 * @Author: 小白白
 * @create: 2019/12/06 - 12:54
 **/
public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++){
            System.out.println("线程开启了" + i);
        }
    }
}
/**
 * @program: javase
 * @description: 继承Thread类的测试类
 * @Author: 小白白
 * @create: 2019/12/06 - 12:56
 **/
public class MyThread_text {
    public static void main(String[] args) {
        //创建一个线程对象
        MyThread t1 = new MyThread();
        //创建一个线程对象
        MyThread t2 = new MyThread();
        //开启一个线程
        t1.start();
        //开启第二条线程
        t2.start();
    }
}

运行结果为两个线程交替运行,交替去争抢CPU的资源

思考:

为什么要重写run()方法?
----------因为run()用来封装被线程执行的代码

run()方法和start()方法的区别?
---------run:封装线程执行的代码,直接调用,相当于普通方法的调用,并没有开启线程
---------start:启动线程,然后由JVM调用此线程的run()方法

start方法的底层源码。-----------native表示调取本地方法
在这里插入图片描述

方式2.实现Runnable接口

1.定义一个类MyRunnable实现Runnable接口
2.在MyRunnable类中重写run()方法
3.创建Runnable类的对象
4.创建Thread类的对象,把MyRunnbable独享作为构造方法的参数
5.启动线程
package com.zxh.Thread.Demo;

/**
 * @program: javase
 * @description: 实现Runnable接口
 * @Author: 小白白
 * @create: 2019/12/06 - 14:01
 **/
public class MyRunnable implements Runnable{
    @Override
    public void run() {
        //线程启动后执行的代码
        for (int i = 0; i < 100;i++){
            System.out.println("第二种方法实现多线程" + i);
        }
    }
}
/**
 * @program: javase
 * @description: 实现Runnable接口的测试类
 * @Author: 小白白
 * @create: 2019/12/06 - 14:03
 **/
public class MyRunnable_test {
    public static void main(String[] args) {
        //创建了一个参数的对象
        MyRunnable myRunnableTest = new MyRunnable();
        //创建了一个线程对象,并把参数传递给这个线程
        //在线程启动之后,执行的就是参数里面的run方法
        Thread t= new Thread(myRunnableTest);
        //开启线程
        t.start();
    }
}

方式3:通过Callable接口进行实现

1.定义一个类MyCallable实现Callable接口
2.在MyCallable中重写call()方法
3.创建MyCallable类的对象
4.创建Future的是实现类FutureTask对象,把MyCallable对象作为构造方法的参数
5.创建Thread类的对象,把FutureTask对象作为构造方法的参数
6.启动线程

/**
 * @program: javase
 * @description: 实现Callable接口
 * @Author: 小白白
 * @create: 2019/12/06 - 18:59
 **/
public class MyCallable implements Callable<Object> {

    //在MyCallable中重写call()方法
    @Override
    public Object call() throws Exception {
        for (int i = 0; i < 100; i++) {
            System.out.println("跟女孩表白" + i);
        }
        //返回值表示线程运行完毕之后的结果
        return "答应";
    }
}
import java.util.concurrent.*;

/**
 * @program: javase
 * @description: 实现Callable接口的测试类
 * @Author: 小白白
 * @create: 2019/12/06 - 19:03
 **/
public class MyCallable_text {
    public static void main(String[] args) {
        //创建MyCallable类的对象
        MyCallable mc = new MyCallable();
        //创建Future的是实现类FutureTask对象,把MyCallable对象作为构造方法的参数
        FutureTask<Object> ft = new FutureTask<Object>(mc);
        //创建Thread类的对象,把FutureTask对象作为构造方法的参数
        Thread t1 = new Thread(ft);
        //启动线程
        t1.start();
    }
}


jvm面试题

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FLaWA9ei-1593962979571)(C:\Users\33066\AppData\Local\YNote\data\qq1510077B57FCC357FB1D93412160B8DD\215c102c7e76422d8f52648d9a1fac23\clipboard.png)]

1.Java中JVM,JRE和JDK的区别

jvm

JVM是Java Virtual Machine(Java虚拟机)的缩写,它是整个Java实现跨平台的最核心的部分,所有的java程序会首先被编译为.class的类文件,这种类文件可以在虚拟机上执行,也就是说class并不直接与机器的操作系统相对应,而是经过虚拟机间接 与操作系统交互,由虚拟机将程序解释给本地系统执行。JVM是Java平台的基础,和实际的机器一 样,它也有自己的指令集,并且在运行时操作不同的内存区域。 JVM通过抽象操作系统和CPU结构,提供了一种与平台无关的代码执行方法,即与特殊的实现方法、主机硬件、主机操作系统无关。JVM的主要工作是解释自己的指令集

(即字节码)到CPU的指令集或对应的系统调用,保护用户免被恶意程序骚扰。 JVM对上层的Java源文件是不关心的,它关注的只是由源文件生成的类文件(.class文件)。

jre

JRE是java runtime environment(java运行环境)的缩写。光有JVM还不能让class文件执行,因为在解释class的时候JVM需要调用解释所需要的类库lib。在JDK的安装目录里你可以找到jre目录,里面有两个文件夹bin和lib,在这里可以 认为bin里的就是jvm,lib中则是jvm工作所需要的类库,而jvm和lib和起来就称为jre。所以,在你写完java程序编译 成.class之后,你可以把这个.class文件和jre一起打包发给朋友,这样你的朋友就可以运行你写程序了(jre里有运

行.class的java.exe)。JRE是Sun公司发布的一个更大的系统,它里面就有一个JVM。JRE就与具体的CPU结构和操作系统有关,是运行Java程序必不可少的(除非用其他一些编译环境编译成.exe可执行文件……),JRE的地位就象一台PC机一样,我们写好的Win32应用程序需要操作系统帮我们运行,同样的,我们编写的Java程序也必须要JRE才能运行。

jdk

JDK是java development kit(java开发工具包)的缩写。每个学java的人都会先在机器上装一个JDK,那 让我们看一下JDK的安装目录。在目录下面有六个文件夹、一个src类库源码压缩包、和其他几个声明文件。其中,真正在运行java时起作用的是以下四个文件夹:bin、include、lib、jre。现在我们可以看出这样一个关系,JDK包含JRE,而JRE 包含JVM。

*bin:* 最主要的是编译器(javac.exe) *include:* java和JVM交互用的头文件****lib********:****类库

*jre:* java运行环境

(注意:这里的bin、lib文件夹和jre里的bin、lib是不同的)

总的来说JDK是用于java程序的开发,而jre则是只能运行class而没有编译的功能。eclipse、idea等其他IDE有自己的编 译器而不是用JDK bin目录中自带的,所以在安装时你会发现他们只要求你选jre路径就ok了。

三者关系概括如下:

jdk是JAVA程序开发时用的开发工具包,其内部也有JRE运行环境JRE。JRE是JAVA程序运行时需要的运行环境,就是说 如果你光是运行JAVA程序而不是去搞开发的话,只安装JRE就能运行已经存在的JAVA程序了。JDk、JRE内部都包含JAVA虚拟机JVM,JAVA虚拟机内部包含许多应用程序的类的解释器和类加载器等等。

2. 说一下堆栈的区别?

  • 功能方面:堆是用来存放对象的,栈是用来执行程序的。
  • 共享性:堆是线程共享的,栈是线程私有的。
  • 空间大小:堆大小远远大于栈。

3. 描述一下JVM加载class文件的原理机制?

答:JVM中类的装载是由类加载器(ClassLoader)和它的子类来实现的,Java中的类加载器是一个重要的Java运行时系统组件,它负责在运行时查找和装入类文件中的类。类的加载是指把类的.class文件中的数据读入到内存中,通常是创建一个字节数组读入.class文件

4. Java 中会存在内存泄漏吗,请简单描述。

理论上Java因为有垃圾回收机制(GC)不会存在内存泄露问题(这也是Java被广泛使用于服务器端编程的一个重要原因);然而在实际开发中,可能会存在无用但可达的对象,这些对象不能被GC回收,因此也会导致内存泄露的发生。例如hibernate的Session(一级缓存)中的对象属于持久态,垃圾回收器是不会回收这些对象的,然而这些对象中可能存在无用的垃圾对象,如果不及时关闭(close)或清空(flush)一级缓存就可能导致内存泄露

5. 什么是虚拟机?

一台虚拟的计算机。是一款软件,用来执行一系列虚拟计算机指令。

例:

Visual Box,VMware属于系统虚拟机,完全是对物理计算机的仿真,提供了可运行完整操作系统的软件平台

java虚拟机是程序虚拟机,专门为执行单个计算机程序而设计,在java虚拟机中执行的指令我们成为java字节码指令

5.1 什么是jvm虚拟机?

  • JVM 是 java虚拟机,是用来执行java字节码(二进制的形式)的虚拟计算机
  • JVM 是运行在操作系统之上的,与硬件没有任何关系

5.2 jvm虚拟机的作用:

答: java虚拟机就是二进制字节码的运行环境,负责装载字节码到其内部,解释/编译为对应平台上的机器指令执行。每一条java指令,java虚拟机规范中都有详细定义,如怎么取操作数,怎么处理操作数,结果放在哪里

5.3 jvm虚拟机特点:

  • 一次编译,到处运行
  • 自动内存管理
  • 自动垃圾回收功能

5.4 什么是内存溢出?

答:指程序申请内存时,没有足够的内存供申请者使用,或者说,给了你一块存储int类型数据的存储空间,但是你却存储long类型的数据,那么结果就是内存不够用,此时就会报错OOM,即所谓的内存溢出。

5.5 什么是内存泄漏?

指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,

造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。

6. 简述Java的垃圾回收机制

传统的C/C++语言,需要程序员负责回收已经分配内存。

显式回收垃圾回收的缺点:

1) 程序忘记及时回收,从而导致内存泄露,降低系统性能。

2) 程序错误回收程序核心类库的内存,导致系统崩溃。

Java语言不需要程序员直接控制内存回收,是由JRE在后台自动回收不再使用的内存,称为垃圾回收机制,简称GC;

1)可以提高编程效率。

2) 保护程序的完整性。

3) 其开销影响性能。Java虚拟机必须跟踪程序中有用的对象,确定哪些是无用的。

垃圾回收机制的特点

1) 垃圾回收机制回收JVM堆内存里的对象空间,不负责回收栈内存数据。

2) 对其他物理连接,比如数据库连接、输入流输出流、Socket连接无能为力。

3) 垃圾回收发生具有不可预知性,程序无法精确控制垃圾回收机制执行。

4) 可以将对象的引用变量设置为null,暗示垃圾回收机制可以回收该对象。 现在的JVM有多种垃圾回收实现算法,表现各异。

垃圾回收机制回收任何对象之前,总会先调用它的finalize方法(如果覆盖该方法,让一个新的引用变量重新引用该对象,则会重新激活对象)。

程序员可以通过System.gc()或者Runtime.getRuntime().gc()来通知系统进行垃圾回收,会有一些效果,但是系统是 否进行垃圾回收依然不确定。

永远不要主动调用某个对象的finalize方法,应该交给垃圾回收机制调用。


数据库面试题

//创建数据库
CREATE DATABASE booksales;
//创建表格
CREATE TABLE `cb` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '主键',
);
//插入数据
insert  into `customer`(`cid`,`cname`,`sex`,`email`,`tel`,`address`) values ('zb01','Jack','男','[email protected]','13100010001','上海市杨浦区国顺路288号');
//删除一行数据
delete  from book where bid = 'b123';
//修改一行数据
UPDATE book SET price = '九折' where press = '清华大学出版社出版';
1.creat table student():在table中建立一个student的表

2.alter  table student add 年龄 number(3):往表中插入一个列

3.alter table student drop column age:删除表中的age列

4.alter table student rename column 年龄 to age:修改年龄为age

5.alter table student modify age varchar2(20):修改列名属性

6.rename student to stu:修改表名

7.drop table stu:删除表

8.instert into teachers values(1,'小小'):插入数据

9.update teachers set name = '托尼' where ids = 9:修改name为托尼
1.select * from emp where deptno in(10,20) : 查询某一区间

2.select * from emp where ename like '%A%':查询是否含有某个字符

3.select * from emp where ename like '__':查询是否含有两个字符的名字

4.select * from emp where sal > 2000 or sal < 1000:只要有一个成立即可

5.select sal + nvl(comm,0) 总工资 from emp:查询相加运算

6.select rownum,E.* from emp e where rownum < 15:进行排序(伪列)

7.select * from emp order by sal desc:将工资进行降序排列(ASC是默认升序)
 

1. 数据库的三范式是什么?

  • 第一范式:强调的是列的原子性,即数据库表的每一列都是不可分割的原子数据项。

  • 第二范式:属性完全依赖于主键

  • 第三范式:任何非主属性不依赖于其它非主属性。

    ​ 数据不能存在传递关系,即每个属性都跟主键有直接关系而不是间接关系。从而建立冗余较小、结构合理的数据库。

2. 一张自增表里面总共有 7 条数据,删除了最后 2 条数据,重启 MySQL 数据库,又插入了一条数据,此时 id 是几?

  • 表类型如果是 MyISAM ,那 id 就是 8。

  • 表类型如果是 InnoDB,那 id 就是 6。

    InnoDB 表只会把自增主键的最大 id 记录在内存中,所以重启之后会导致最大 id 丢失。

3. MySQL 的内连接、左连接、右连接有什么区别?

内连接关键字:inner join;左连接:left join;右连接:right join。

内连接是把匹配的关联数据显示出来;

左连接是左边的表全部显示出来,右边的表显示出符合条件的数据;

右连接正好相反。

4.什么是事务?

  1. 事务是数据库系统区别于其他一切文件系统的重要特性之一
  2. 事务是一组具有原子性的SQL语句,或是一个独立的工作单元

事务需要符合以下特点:

  • 原子性(ATOMICITY),定义:
    一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败,对于一个事务来说,不可能只执行其中的一部分操作

  • 一致性(CONSISTENCY),定义:
    一致性是指事务讲数据库从一种一致性状态转换到另外一种一致性状态,在事务开始之前和事务结束后数据库数据的完整性没有被破坏

  • 隔离性(ISOLATION),定义:
    隔离性要求一个事务对数据库中数据的修改,在未提交完成前对于其它事务是不可见的

SQL标准中定义的四种隔离级别

未提交读(READ UNCOMMITED) ,即脏读
已提交读(READ COMMITED),大多数数据库中的默认,mysql列外
可重复读(REPEATABLE READ)
可串行化(SERIALIZABLE),在读取的每一行数据都加锁,会导致大量的锁超时、锁征用问题,在实际业务中很少用到这个隔离级别,除非严格要求数据一致性、可以接受在没有并发的前提下才会考虑使用这种隔离界别

5. MySQL 索引是怎么实现的?

索引是满足某种特定查找算法的数据结构,而这些数据结构会以某种方式指向数据,从而实现高效查找数据。

具体来说 MySQL 中的索引,不同的数据引擎实现有所不同,但目前主流的数据库引擎的索引都是 B+ 树实现的

B+ 树的搜索效率,可以到达二分法的性能,找到数据区域之后就找到了完整的数据结构了,所有索引的性能也是更好的。

6. Mysql四种常见数据库引擎

InnoDB存储引擎

事务型数据库的首选引擎,支持事务安全表(ACID),支持行锁定和外键,上图也看到了,InnoDB是默认的MySQL引擎。

1、InnoDB给MySQL提供了具有提交、回滚和崩溃恢复能力的事物安全(ACID兼容)存储引擎。InnoDB锁定在行级并且也在SELECT语句中提供一个类似Oracle的非锁定读。这些功能增加了多用户部署和性能。在SQL查询中,可以自由地将InnoDB类型的表和其他MySQL的表类型混合起来,甚至在同一个查询中也可以混合

2、InnoDB是为处理巨大数据量的最大性能设计。它的CPU效率可能是任何其他基于磁盘的关系型数据库引擎锁不能匹敌的

3、InnoDB存储引擎完全与MySQL服务器整合,InnoDB存储引擎为在主内存中缓存数据和索引而维持它自己的缓冲池。InnoDB将它的表和索引在一个逻辑表空间中,表空间可以包含数个文件(或原始磁盘文件)。这与MyISAM表不同,比如在MyISAM表中每个表被存放在分离的文件中。InnoDB表可以是任何尺寸,即使在文件尺寸被限制为2GB的操作系统

4、InnoDB支持外键完整性约束,存储表中的数据时,每张表的存储都按主键顺序存放,如果没有显示在表定义时指定主键,InnoDB会为每一行生成一个6字节的ROWID,并以此作为主键

5、InnoDB被用在众多需要高性能的大型数据库站点上

InnoDB不创建目录,使用InnoDB时,MySQL将在MySQL数据目录下创建一个名为ibdata1的10MB大小的自动扩展数据文件,以及两个名为ib_logfile0和ib_logfile1的5MB大小的日志文件

MyISAM存储引擎

它是在Web、数据仓储和其他应用环境下最常使用的存储引擎之一。MyISAM拥有较高的插入、查询速度,但不支持事物

MEMORY存储引擎

MEMORY存储引擎将表中的数据存储到内存中,未查询和引用其他表数据提供快速访问。

7. 说一下数据库的事务隔离?

ySQL 的事务隔离是在 MySQL. ini 配置文件里添加的,在文件的最后添加:

transaction-isolation = REPEATABLE-READ

可用的配置值:READ-UNCOMMITTED、READ-COMMITTED、REPEATABLE-READ、SERIALIZABLE。

  • READ-UNCOMMITTED:未提交读,最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读)。
  • READ-COMMITTED:提交读,一个事务提交后才能被其他事务读取到(会造成幻读、不可重复读)。
  • REPEATABLE-READ:可重复读,默认级别,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(会造成幻读)。
  • SERIALIZABLE:序列化,代价最高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读、幻读。

脏读 :表示一个事务能够读取另一个事务中还未提交的数据。比如,某个事务尝试插入记录 A,此时该事务还未提交,然后另一个事务尝试读取到了记录 A。

不可重复读 :是指在一个事务内,多次读同一数据。

幻读 :指同一个事务内多次查询返回的结果集不一样。比如同一个事务 A 第一次查询时候有 n 条记录,但是第二次同等条件下查询却有 n+1 条记录,这就好像产生了幻觉。发生幻读的原因也是另外一个事务新增或者删除或者修改了第一个事务结果集里面的数据,同一个记录的数据内容被修改了,所有数据行的记录就变多或者变少了。

8. 说一下乐观锁和悲观锁?

  • 乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在提交更新的时候会判断一下在此期间别人有没有去更新这个数据。
  • 悲观锁:每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻止,直到这个锁被释放。

数据库的乐观锁需要自己实现,在表里面添加一个 version 字段,每次修改成功值加 1,这样每次修改的时候先对比一下,自己拥有的 version 和数据库现在的 version 是否一致,如果不一致就不修改,这样就实现了乐观锁。

9. Redis 有哪些功能?

  • 数据缓存功能
  • 分布式锁的功能
  • 支持数据持久化
  • 支持事务
  • 支持消息队列

10. 什么是缓存穿透?怎么解决?

缓存穿透:指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。

解决方案:最简单粗暴的方法如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们就把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。

11. Redis 支持的数据类型有哪些?

Redis 支持的数据类型:string(字符串)、list(列表)、hash(字典)、set(集合)、zset(有序集合)。

12. 怎么保证缓存和数据库数据的一致性?

  • 合理设置缓存的过期时间。
  • 新增、更改、删除数据库操作时同步更新 Redis,可以使用事物机制来保证数据的一致性。

13. Redis 怎么实现分布式锁?

Redis 分布式锁其实就是在系统里面占一个“坑”,其他程序也要占“坑”的时候,占用成功了就可以继续执行,失败了就只能放弃或稍后重试。

占坑一般使用 setnx(set if not exists)指令,只允许被一个程序占有,使用完调用 del 释放锁。

14.简述mysql主从复制原理?

(1) master将改变记录到二进制日志(binary log)中(这些记录叫做二进制日志事件,binary log events);
(2) slave将master的binary log events拷贝到它的中继日志(relay log);
(3) slave重做中继日志中的事件,将改变反映它自己的数据。

15.数据库优化的几点:

  1. 建立和优化使用索引

  2. 减少子查询和联表查询

  3. 主从分离

  4. 用临时表代替大表插入

16. redis是什么?

Redis是一个开源的基于内存的,key-value数据结构的缓存数据库,支持数据持久化,m-s复制,常用数据类型有string set hash list,
最佳应用场景:适用于数据变化快且数据库大小可遇见(适合内存容量)的应用程序。
例如:股票价格、数据分析、实时数据搜集、实时通讯。
Redis只能使用单线程,性能受限于CPU性能,故单实例CPU最高才可能达到5-6wQPS每秒(取决于数据结构,数据大小以及服务器硬件性能,日常环境中QPS高峰大约在1-2w左右)


框架面试题

1.spring 容器的启动加载流程

​ 首先解析 spring.xml 配置文件,把其中 <bean> 解析为 BeanDefinition, 存入beanFactory

​ 执行beanFactory的后处理器

​ 接下来 由 beanFactory 创建每个类对应的单例对象, 利用了反射根据类名创建每个类的实例对象(构造,初始化方法)

​ 执行 bean 的后处理器, 它其中有两个方法,会在 bean 的初始化方法前后被调用
​ 会把这些结果 存入 beanFactory 的 singletonObjects 这样一个map集合里

​ 执行依赖注入
​ 主要为了解决循环引用问题

2 . Servlet的运行过程?

Web容器加载Servlet并将其实例化后,Servlet生命周期开始,

容器运行其init()方法进行Servlet的初始化;

请求到达时调用Servlet的service()方法,

service()方法会根据需要调用与请求对应的doGet或doPost等方法;

当服务器关闭或项目被卸载时服务器会将Servlet实例销毁,此时会调用Servlet的destroy()方法。

3. 讲解JSP中的四种作用域。

  • page代表与一个页面相关的对象和属性。
  • request代表与Web客户机发出的一个请求相关的对象和属性。一个请求可能跨越多个页面,涉及多个Web组件;需要在页面显示的临时数据可以置于此作用域。
  • session代表与某个用户与服务器建立的一次会话相关的对象和属性。跟某个用户相关的数据应该放在用户自己的session中。
  • application代表与整个Web应用程序相关的对象和属性,它实质上是跨越整个Web应用程序,包括多个页面、请求和会话的一个全局作用域。

4. forward 和 redirect 的区别?

forward 是转发 和 redirect 是重定向:

  • 地址栏 url 显示:foward url 不会发生改变,redirect url 会发生改变;

  • 数据共享:forward 可以共享 request 里的数据,redirect 不能共享;

  • 效率:forward 比 redirect 效率高。

5. 什么是反射?

反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。

6.动态代理是什么?有哪些应用?

动态代理是运行时动态生成代理类。

动态代理的应用有 spring aop、hibernate 数据查询、测试框架的后端 mock、rpc,Java注解对象获取等。

7.为什么要使用 spring?

  • spring 提供 ioc 技术,容器会帮你管理依赖的对象,从而不需要自己创建和管理依赖对象了,更轻松的实现了程序的解耦。
  • spring 提供了事务支持,使得事务操作变的更加方便。
  • spring 提供了面向切片编程,这样可以更方便的处理某一类的问题。
  • 更方便的框架集成,spring 可以很方便的集成其他框架,比如 MyBatis、hibernate 等。

8. spring 的优点?

1.降低了组件之间的耦合性 ,实现了软件各层之间的解耦
2.可以使用容易提供的众多服务,如事务管理,消息服务等
3.容器提供单例模式支持
4.容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控等功能
5.容器提供了众多的辅助类,能加快应用的开发
6.spring对于主流的应用框架提供了集成支持,如hibernate,JPA,Struts等
7.spring属于低侵入式设计,代码的污染极低
8.独立于各种应用服务器
9.spring的DI机制降低了业务对象替换的复杂性
10.Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可以自由选择spring的部分或全部

9. 什么是DI机制?

依赖注入(Dependecy Injection)和控制反转(Inversion of Control)是同一个概念,具体的讲:当某个角色
需要另外一个角色协助的时候,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在spring中
创建被调用者的工作不再由调用者来完成,因此称为控制反转。创建被调用者的工作由spring来完成,然后注入调用者
因此也称为依赖注入。
spring以动态灵活的方式来管理对象 , 注入的两种方式,设置注入和构造注入。
设置注入的优点:直观,自然
构造注入的优点:可以在构造器中决定依赖关系的顺序。

10. 解释一下什么是 aop?

1.面向切面编程提供声明式事务管理

2.spring支持用户自定义的切面

Spring AOP(Aspect Oriented Programming,面向切面编程)是OOPs(面向对象编程)的补充,它也提供了模块化。在面向对象编程中,关键的单元是对象,AOP的关键单元是切面,或者说关注点(可以简单地理解为你程序中的独立模块)。一些切面可能有集中的代码,但是有些可能被分散或者混杂在一起,例如日志或者事务。这些分散的切面被称为横切关注点。一个横切关注点是一个可以影响到整个应用的关注点,而且应该被尽量地集中到代码的一个地方,例如事务管理、权限、日志、安全等。

基于Java的主要AOP实现有:

  1. AspectJ
  2. Spring AOP
  3. JBoss AOP

11. Spring AOP 代理是什么?

(1) SpringAOP是动态代理来实现的。有两种代理方式:JDK动态代理与CGLIB动态代理
(2) JDK动态代理:是通过反射来接收被代理类,要求必须实现一个接口
(3) CGLIB动态代理:当被代理类没有实现一个接口的时候,就会使用CGLIB进行动态代理。CGLIB动态代理通过运行时动态生成被代理类的子类,运用继承的方式来实现动态代理。如果被代理类被final修饰了,那么就不能使用CGLIB进行动态代理了。

代理是使用非常广泛的设计模式。简单来说,代理是一个看其他像另一个对象的对象,但它添加了一些特殊的功能
Spring AOP是基于代理实现的。AOP 代理是一个由 AOP 框架创建的用于在运行时实现切面协议的对象
Spring AOP默认为 AOP 代理使用标准的 JDK 动态代理。

这使得任何接口(或者接口的集合)可以被代理。

Spring AOP 也可以使用 CGLIB 代理。这对代理类而不是接口是必须的。
如果业务对象没有实现任何接口那么默认使用CGLIB

12. Spring中有哪些不同的通知类型

通知(advice)是你在你的程序中想要应用在其他模块中的横切关注点的实现。Advice主要有以下5种类型:

  1. 前置通知(Before Advice): 在连接点之前执行的Advice,不过除非它抛出异常,否则没有能力中断执行流。使用 @Before 注解使用这个Advice。
  2. 返回之后通知(After Retuning Advice): 在连接点正常结束之后执行的Advice。例如,如果一个方法没有抛出异常正常返回。通过 @AfterReturning 关注使用它。
  3. 抛出(异常)后执行通知(After Throwing Advice): 如果一个方法通过抛出异常来退出的话,这个Advice就会被执行。通用 @AfterThrowing 注解来使用。
  4. 后置通知(After Advice): 无论连接点是通过什么方式退出的(正常返回或者抛出异常)都会执行在结束后执行这些Advice。通过 @After 注解使用。
  5. 围绕通知(Around Advice): 围绕连接点执行的Advice,就你一个方法调用。这是最强大的Advice。通过 @Around 注解使用。

13. 解释一下什么是 ioc?

ioc:Inversionof Control(中文:控制反转)是 spring 的核心,对于 spring 框架来说,就是由 spring 来负责控制对象的生命周期和对象间的关系。

简单来说,控制指的是当前对象对内部成员的控制权;控制反转指的是,这种控制权不由当前对象管理了,由其他(类,第三方容器)来管理。

14 . spring 常用的注入方式有哪些?

  • 构造方法注入
  • setter注入
  • 基于注解的注入

14.1构造方法注入

spring 配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans 
		http://www.springframework.org/schema/beans/spring-beans.xsd
	    http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
        
	<bean id="bowl" class="constxiong.interview.inject.Bowl" />
	
	<bean id="person" class="constxiong.interview.inject.Person">
		<property name="bowl" ref="bowl"></property>
	</bean>
	
</beans>
package constxiong.interview.inject;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class InjectTest {

	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("spring_inject.xml");
		Person person = (Person)context.getBean("person");
		person.eat();
	}
}

b) 节点 factory-method 参数指定静态工厂方法

工厂类,静态工厂方法

package constxiong.interview.inject;
 
public class BowlFactory {
 
	public static final Bowl getBowl() {
		return new Bowl();
	}
	
}

spring 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans 
		http://www.springframework.org/schema/beans/spring-beans.xsd
	    http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
        
	<bean id="bowl" class="constxiong.interview.inject.BowlFactory" factory-method="getBowl"/>
	
	<bean id="person" class="constxiong.interview.inject.Person">
		<constructor-arg name="bowl" ref="bowl"></constructor-arg>
	</bean>
	
</beans>

c) 非静态工厂方法,需要指定工厂 bean 和工厂方法

工厂类,非静态工厂方法

package constxiong.interview.inject;
 
public class BowlFactory {
 
	public Bowl getBowl() {
		return new Bowl();
	}
	
}

配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans 
		http://www.springframework.org/schema/beans/spring-beans.xsd
	    http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    
    <bean id="bowlFactory" class="constxiong.interview.inject.BowlFactory"></bean>   
	<bean id="bowl" factory-bean="bowlFactory" factory-method="getBowl"/>
	
	<bean id="person" class="constxiong.interview.inject.Person">
		<constructor-arg name="bowl" ref="bowl"></constructor-arg>
	</bean>
	
</beans>

14.2 注解方式注入

bean 的申明、注册

@Component //注册所有bean
@Controller //注册控制层的bean
@Service //注册服务层的bean
@Repository //注册dao层的bean

bean 的注入

@Autowired 作用于 构造方法、字段、方法,常用于成员变量字段之上。
@Autowired + @Qualifier 注入,指定 bean 的名称
@Resource JDK 自带注解注入,可以指定 bean 的名称和类型等

测试代码

e) spring 配置文件,设置注解扫描目录

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans 
		http://www.springframework.org/schema/beans/spring-beans.xsd
	    http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
        
	<context:component-scan base-package="constxiong.interview" />
	
</beans>

class Bowl

package constxiong.interview.inject;
 
import org.springframework.stereotype.Component;
//import org.springframework.stereotype.Controller;
//import org.springframework.stereotype.Repository;
//import org.springframework.stereotype.Service;
 
@Component //注册所有bean
//@Controller //注册控制层的bean
//@Service //注册服务层的bean
//@Repository //注册dao层的bean
public class Bowl {
 
	public void putRice() {
		System.out.println("盛饭...");
	}
 
}

class Person

package constxiong.interview.inject;
 
//import javax.annotation.Resource;
//
import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
 
@Component //注册所有bean
//@Controller //注册控制层的bean
//@Service //注册服务层的bean
//@Repository //注册dao层的bean
public class Person {
 
	@Autowired
//	@Qualifier("bowl")
//	@Resource(name="bowl")
	private Bowl bowl;
 
	public void eat() {
		bowl.putRice();
		System.out.println("开始吃饭...");
	}
	
}

15. spring 中的 bean 是线程安全的吗?

spring 中的 bean 默认是单例模式,spring 框架并没有对单例 bean 进行多线程的封装处理。

实际上大部分时候 spring bean 无状态的(比如 dao 类),所有某种程度上来说 bean 也是安全的,但如果 bean 有状态的话(比如 view model 对象),那就要开发者自己去保证线程安全了,最简单的就是改变 bean 的作用域,把“singleton”变更为“prototype”,这样请求 bean 相当于 new Bean()了,所以就可以保证线程安全了。

  • 有状态就是有数据存储功能。
  • 无状态就是不会保存数据。

16. spring 支持几种 bean 的作用域?

spring 支持 5 种作用域,如下:

  • singleton:spring ioc 容器中只存在一个 bean 实例,bean 以单例模式存在,是系统默认值;
  • prototype:每次从容器调用 bean 时都会创建一个新的示例,既每次 getBean()相当于执行 new Bean()操作;
  • Web 环境下的作用域:
  • request:每次 http 请求都会创建一个 bean;
  • session:同一个 http session 共享一个 bean 实例;
  • global-session:用于 portlet 容器,因为每个 portlet 有单独的 session,globalsession 提供一个全局性的 http session。

17.spring 事务实现方式有哪些?

  • 声明式事务:声明式事务也有两种实现方式,
  • 基于 xml 配置文件的方式和注解方式(在类上添加 @Transaction 注解)。
  • 编码方式:提供编码的形式管理和维护事务。

18.说一下 spring 的事务隔离

spring 有五大隔离级别,默认值为 ISOLATION_DEFAULT(使用数据库的设置),其他四个隔离级别和数据库的隔离级别一致:

ISOLATION_DEFAULT:用底层数据库的设置隔离级别,数据库设置的是什么我就用什么;

ISOLATIONREADUNCOMMITTED:未提交读,最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读);

ISOLATIONREADCOMMITTED:提交读,一个事务提交后才能被其他事务读取到(会造成幻读、不可重复读),SQL server 的默认级别;

ISOLATIONREPEATABLEREAD:可重复读,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(会造成幻读),MySQL 的默认级别;

ISOLATION_SERIALIZABLE:序列化,代价最高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读、幻读。

脏读 :表示一个事务能够读取另一个事务中还未提交的数据。比如,某个事务尝试插入记录 A,此时该事务还未提交,然后另一个事务尝试读取到了记录 A。

不可重复读 :是指在一个事务内,多次读同一数据。

幻读 :指同一个事务内多次查询返回的结果集不一样。比如同一个事务 A 第一次查询时候有 n 条记录,但是第二次同等条件下查询却有 n+1 条记录,这就好像产生了幻觉。发生幻读的原因也是另外一个事务新增或者删除或者修改了第一个事务结果集里面的数据,同一个记录的数据内容被修改了,所有数据行的记录就变多或者变少了。

19.说一下 spring mvc 运行流程?

  • spring mvc 先将请求发送给 DispatcherServlet。
  • DispatcherServlet 查询一个或多个 HandlerMapping,找到处理请求的 Controller。
  • DispatcherServlet 再把请求提交到对应的 Controller。
  • Controller 进行业务逻辑处理后,会返回一个ModelAndView。
  • Dispathcher 查询一个或多个 ViewResolver 视图解析器,找到 ModelAndView 对象指定的视图对象。
  • 视图对象负责渲染返回给客户端。

20. spring mvc 有哪些组件?

  • 前置控制器 DispatcherServlet。
  • 映射控制器 HandlerMapping。
  • 处理器 Controller。
  • 模型和视图 ModelAndView。
  • 视图解析器 ViewResolver。

21. MyBatis 中 #{}和 ${}的区别是什么?

\#{}是预编译处理,${}是字符替换。 在使用 #{}时,MyBatis 会将 SQL 中的 #{}替换成“?”,配合 PreparedStatement 的 set 方法赋值,这样可以有效的防止 SQL 注入,保证程序的运行安全。

22. MyBatis 有几种分页方式?

分页方式:逻辑分页和物理分页。

逻辑分页: 使用 MyBatis 自带的 RowBounds 进行分页,它是一次性查询很多数据,然后在数据中再进行检索。

物理分页: 自己手写 SQL 分页或使用分页插件 PageHelper,去数据库查询指定条数的分页数据的形式。

23.MyBatis 逻辑分页和物理分页的区别是什么?

  • 逻辑分页是一次性查询很多数据,然后再在结果中检索分页的数据。这样做弊端是需要消耗大量的内存、有内存溢出的风险、对数据库压力较大。
  • 物理分页是从数据库查询指定条数的数据,弥补了一次性全部查出的所有数据的种种缺点,比如需要大量的内存,对数据库查询压力较大等问题。

24. MyBatis 是否支持延迟加载?延迟加载的原理是什么?

MyBatis 支持延迟加载,设置 lazyLoadingEnabled=true 即可。

延迟加载的原理的是调用的时候触发加载,而不是在初始化的时候就加载信息。比如调用 a. getB(). getName(),这个时候发现 a. getB() 的值为 null,此时会单独触发事先保存好的关联 B 对象的 SQL,先查询出来 B,然后再调用 a. setB(b),而这时候再调用 a. getB(). getName() 就有值了,这就是延迟加载的基本原理。

25.说一下 MyBatis 的一级缓存和二级缓存?

  • 一级缓存:基于 PerpetualCache 的 HashMap 本地缓存,它的声明周期是和 SQLSession 一致的,有多个 SQLSession 或者分布式的环境中数据库操作,可能会出现脏数据。当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空,默认一级缓存是开启的。
  • 二级缓存:也是基于 PerpetualCache 的 HashMap 本地缓存,不同在于其存储作用域为 Mapper 级别的,如果多个SQLSession之间需要共享缓存,则需要使用到二级缓存,并且二级缓存可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现 Serializable 序列化接口(可用来保存对象的状态)。

开启二级缓存数据查询流程:二级缓存 -> 一级缓存 -> 数据库。

缓存更新机制:当某一个作用域(一级缓存 Session/二级缓存 Mapper)进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear。

26.为什么要用 spring boot?

  • 配置简单
  • 独立运行
  • 自动装配
  • 无代码生成和 xml 配置
  • 提供应用监控
  • 易上手
  • 提升开发效率

27. spring boot 核心配置文件是什么?

spring boot 核心的两个配置文件:

  • bootstrap (. yml 或者 . properties):boostrap 由父 ApplicationContext 加载的,比 applicaton 优先加载,且 boostrap 里面的属性不能被覆盖;
  • application (. yml 或者 . properties):用于 spring boot 项目的自动化配置。

28. spring boot 有哪些方式可以实现热部署?

  • 使用 devtools 启动热部署,添加 devtools 库,在配置文件中把 spring. devtools. restart. enabled 设置为 true;
  • 使用 Intellij Idea 编辑器,勾上自动编译或手动重新编译。

29.说说你对 Spring 的理解,非单例注入的原理?它的生命周期?循环注入的原理, aop 的实现原理,说说 aop 中的几个术语,它们是怎么相互工作的。

AOP与IOC的概念(即spring的核心)

a) IOC:Spring是开源框架,使用框架可以使我们减少工作量,提高工作效率并且它是分层结构,即相对应的层处理对应的业务逻辑,减少代码的耦合度。而spring的核心是IOC控制反转和AOP面向切面编程。IOC控制反转主要强调的是程序之间的关系是由容器控制的,容器控制对象,控制了对外部资源的获取。而反转即为,在传统的编程中都是由我们创建对象获取依赖对象,而在IOC中是容器帮我们创建对象并注入依赖对象,正是容器帮我们查找和注入对象,对象是被获取,所以叫反转。

b) AOP:面向切面编程,主要是管理系统层的业务,比如日志,权限,事物等。AOP是将封装好的对象剖开,找出其中对多个对象产生影响的公共行为,并将其封装为一个可重用的模块,这个模块被命名为切面(aspect),切面将那些与业务逻辑无关,却被业务模块共同调用的逻辑提取并封装起来,减少了系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。

核心组件:bean,context,core,单例注入是通过单例beanFactory进行创建,生命周期是在创建的时候通过接口实现开启,循环注入是通过后置处理器,aop其实就是通过反射进行动态代理,pointcut,advice等。


计算机网络面试题

1.http协议和tcp协议的区别

TCP协议对应于传输层,而HTTP协议对应于应用层,从本质上来说,二者没有可比性。Http协议是建立在TCP协议基础之上的,当浏览器需要从服务器获取网页数据的时候,会发出一次Http请求。

1.支持客户/服务器模式。
2.简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
3.灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
4.无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
5.无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。

2.socket:

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

前人已经给我们做了好多的事了,网络间的通信也就简单了许多,但毕竟还是有挺多工作要做的。以前听到Socket编程,觉得它是比较高深的编程知识,但是只要弄清Socket编程的工作原理,神秘的面纱也就揭开了。
一个生活中的场景。你要打电话给一个朋友,先拨号,朋友听到电话铃声后提起电话,这时你和你的朋友就建立起了连接,就可以讲话了。等交流结束,挂断电话结束此次交谈。 生活中的场景就解释了这工作原理,也许TCP/IP协议族就是诞生于生活中,这也不一定。

3.socket中TCP的三次握手建立连接详解

  • 客户端向服务器发送一个SYN J
  • 服务器向客户端响应一个SYN K,并对SYN J进行确认ACK J+1
  • 客户端再想服务器发一个确认ACK K+1

4.socket中TCP的四次握手释放连接详解

image

5.WebSocket

WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。

HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

6.什么是三次握手四次挥手?tcp为什么要三次握手?

第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
完成三次握手,客户端与服务器开始传送数据
img

  1. 客户端先发送FIN,进入FIN_WAIT1状态,用来关闭Client到Server的数据传送

  2. 服务端收到FIN,发送ACK,进入CLOSE_WAIT状态,客户端收到这个ACK,进入FIN_WAIT2状态

  3. 服务端发送FIN,进入LAST_ACK状态,用来关闭Server到Client的数据传送

  4. 客户端收到FIN,发送ACK,进入TIME_WAIT状态,服务端收到ACK,进入CLOSE状态(等待2MSL时间,约4分钟。主要是防止最后一个ACK丢失。)

    第一次挥手:主动关闭方发送一个FIN,用来关闭主动方到被动关闭方的数据传送,也就是主动关闭方告诉被动关闭方:我已经不 会再给你发数据了(当然,在fin包之前发送出去的数据,如果没有收到对应的ack确认报文,主动关闭方依然会重发这些数据),但是,此时主动关闭方还可 以接受数据。
    第二次挥手:被动关闭方收到FIN包后,发送一个ACK给对方,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号)。
    第三次挥手:被动关闭方发送一个FIN,用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,我的数据也发送完了,不会再给你发数据了。
    第四次挥手:主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,至此,完成四次挥手。

在这里插入图片描述

6.1为什么连接的时候是三次握手,关闭的时候却是四次握手?

答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,“你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。49.简述 tcp 和 udp的区别?

tcp 和 udp 是 OSI 模型中的运输层中的协议。tcp 提供可靠的通信传输,而 udp 则常被用于让广播和细节控制交给应用的通信传输。

两者的区别大致如下:

  • tcp 面向连接,udp 面向非连接即发送数据前不需要建立链接;

  • tcp 提供可靠的服务(数据传输),udp 无法保证;

  • tcp 面向字节流,udp 面向报文;

  • tcp 数据传输慢,udp 数据传输快;

7.get 和 post 请求有哪些区别?

  • get 请求会被浏览器主动缓存,而 post 不会。
  • get 传递参数有大小限制,而 post 没有。
  • post 参数传输更安全,get 的参数会明文限制在 url 上,post 不会。

8.如何实现跨域?

实现跨域有以下几种方案:

  • 服务器端运行跨域 设置 CORS 等于 *;
  • 在单个接口使用注解 @CrossOrigin 运行跨域;
  • 使用 jsonp 跨域;

9.http和https有什么区别?

  1. https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。

  2. http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。

  3. http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。

  4. http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

9.1 HTTPS的工作原理

客户端在使用HTTPS方式与Web服务器通信时有以下几个步骤,如图所示。

(1)客户使用https的URL访问Web服务器,要求与Web服务器建立SSL连接。

(2)Web服务器收到客户端请求后,会将网站的证书信息(证书中包含公钥)传送一份给客户端。

(3)客户端的浏览器与Web服务器开始协商SSL连接的安全等级,也就是信息加密的等级。

(4)客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。

(5)Web服务器利用自己的私钥解密出会话密钥。

(6)Web服务器利用会话密钥加密与客户端之间的通信。

img

10.OSI,TCP/IP,五层协议的体系结构,以及各层协议

OSI分层 (7层):物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。
TCP/IP分层(4层):网络接口层、 网际层、运输层、 应用层。
五层协议 (5层):物理层、数据链路层、网络层、运输层、 应用层。
每一层的协议如下:
物理层:RJ45、CLOCK、IEEE802.3 (中继器,集线器)
数据链路:PPP、FR、HDLC、VLAN、MAC (网桥,交换机)
网络层:IP、ICMP、ARP、RARP、OSPF、IPX、RIP、IGRP、 (路由器)
传输层:TCP、UDP、SPX
会话层:NFS、SQL、NETBIOS、RPC
表示层:JPEG、MPEG、ASII
应用层:FTP、DNS、Telnet、SMTP、HTTP、WWW、NFS
每一层的作用如下:
物理层:通过媒介传输比特,确定机械及电气规范(比特Bit)
数据链路层:将比特组装成帧和点到点的传递(帧Frame)
网络层:负责数据包从源到宿的传递和网际互连(包PackeT)
传输层:提供端到端的可靠报文传递和错误恢复(段Segment)
会话层:建立、管理和终止会话(会话协议数据单元SPDU)
表示层:对数据进行翻译、加密和压缩(表示协议数据单元PPDU)
应用层:允许访问OSI环境的手段(应用协议数据单元APDU)

网络安全面试题

1.什么是SQL注入攻击

攻击者在HTTP请求中注入恶意的SQL代码,服务器使用参数构建数据库SQL命令时,恶意SQL被一起构造,并在数据库中执行。
用户登录,输入用户名 lianggzone,密码 ‘ or ‘1’=’1 ,如果此时使用参数构造的方式,就会出现
select * from user where name = ‘lianggzone’ and password = ‘’ or ‘1’=‘1’

不管用户名和密码是什么内容,使查询出来的用户列表不为空。

如何防范SQL注入攻击使用预编译的PrepareStatement是必须的,但是一般我们会从两个方面同时入手。
Web端
1)有效性检验。
2)限制字符串输入的长度。
服务端
1)不用拼接SQL字符串。
2)使用预编译的PrepareStatement。
3)有效性检验。(为什么服务端还要做有效性检验?第一准则,外部都是不可信的,防止攻击者绕过Web端请求)
4)过滤SQL需要的参数中的特殊字符。比如单引号、双引号。

​ prepareStatement对象防止sql注入的方式是把用户非法输入的单引号用\反斜杠做了转义,从而达到了防止sql注入的目的

img

2.什么是XSS攻击

答; 跨站点脚本攻击,指攻击者通过篡改网页,嵌入恶意脚本程序,在用户浏览网页时,控制用户浏览器进行恶意操作的一种攻击方式。如何防范XSS攻击
1)前端,服务端,同时需要字符串输入的长度限制。
2)前端,服务端,同时需要对HTML转义处理。将其中的”<”,”>”等特殊字符进行转义编码。
防 XSS 的核心是必须对输入的数据做过滤处理。

3.什么是CSRF攻击

答; 跨站点请求伪造,指攻击者通过跨站请求,以合法的用户的身份进行非法操作。可以这么理解CSRF攻击:攻击者盗用你的身份,以你的名义向第三方网站发送恶意请求。CRSF能做的事情包括利用你的身份发邮件,发短信,进行交易转账,甚至盗取账号信息。

如何防范CSRF攻击:

安全框架,例如Spring Security。
token机制。在HTTP请求中进行token验证,如果请求中没有token或者token内容不正确,则认为CSRF攻击而拒绝该请求。
验证码。通常情况下,验证码能够很好的遏制CSRF攻击,但是很多情况下,出于用户体验考虑,验证码只能作为一种辅助手段,而不是最主要的解决方案。
referer识别。在HTTP Header中有一个字段Referer,它记录了HTTP请求的来源地址。如果Referer是其他网站,就有可能是CSRF攻击,则拒绝该请求。但是,服务器并非都能取到Referer。很多用户出于隐私保护的考虑,限制了Referer的发送。在某些情况下,浏览器也不会发送Referer,例如HTTPS跳转到HTTP。
1)验证请求来源地址;
2)关键操作添加验证码;
3)在请求地址添加 token 并验证。

4.DDos 攻击

客户端向服务端发送请求链接数据包,服务端向客户端发送确认数据包,客户端不向服务端发送确认数据包,服务器一直等待来自客户端的确认
没有彻底根治的办法,除非不使用TCP
DDos 预防:
1)限制同时打开SYN半链接的数目
2)缩短SYN半链接的Time out 时间
3)关闭不必要的服务

img

数据结构与算法面试题

1.二分查找。

非递归实现:

public static int biSearch(int []array,int a){
    int lo=0;
    int hi=array.length-1;
    int mid;
    while(lo<=hi){
        mid=(lo+hi)/2;
        if(array[mid]==a){
            return mid+1;
        }else if(array[mid]<a){
            lo=mid+1;
        }else{
            hi=mid-1;
        }
    }
    return -1;
}

递归实现:

public static int sort(int []array,int a,int lo,int hi){
        if(lo<=hi){
            int mid=(lo+hi)/2;
            if(a==array[mid]){
                return mid+1;
            }
            else if(a>array[mid]){
                return sort(array,a,mid+1,hi);
            }else{
                return sort(array,a,lo,mid-1);
            }
        }
        return -1;
    }

排序方式

冒泡排序

public class Bubbling {
    public static void BubblingSort(int[] arr){
        if (arr == null || arr.length < 2){
            return;
        }
        for (int end = arr.length - 1;end > 0;end--){
            for (int i = 0;i < end;i++){
                if (arr[i] > arr[i+1]){
                    swap(arr, i ,i +1);
                }
            }
        }
    }
    public static void swap(int[] arr,int i,int j){
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
}

选择排序:

public class Choose {
    public static  void select(int[] arr){
        if (arr == null || arr.length < 2){
            return;
        }
        for (int i = 0;i < arr.length - 1;i++){
            int minIndex = i;
            for (int j = i + 1;j < arr.length;j++){
                minIndex = arr[j] < arr[minIndex] ? j : minIndex;
            }
            swap(arr,i,minIndex);
        }
    }
    public static void swap(int[] arr,int i,int j){
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
}

插入排序:

private static int[] insertSort(int[] arr) {
	int temp;
    if (arr == null || arr.length < 2){
            return;
    }
    for (int i=1;i<arr.length;i++){
        //待排元素小于有序序列的最后一个元素时,向前插入
        if (arr[i]<arr[i-1]){
            temp = arr[i];
            for (int j=i;j>=0;j--){
                if (j>0 && arr[j-1]>temp) {
                    arr[j]=arr[j-1];
                }else {
                    arr[j]=temp;
                    break;
                }
            }
        }
    }
    return arr;
}

快速排序:

private static int[] quickSort(int[] arr, int low, int high) {
    	if (arr == null || arr.length < 2){
            return;
        }
		if (low < high) {
			int middle = getMiddle(arr, low, high);
			//对左子序列进行排序
			quickSort(arr, low, middle - 1);
			//对右子序列进行排序
			quickSort(arr, middle + 1, high);
		}
		return arr;
}
private static int getMiddle(int[] arr, int low, int high) {
	int temp = arr[low];
	while (low < high) {
		while (low < high && temp <= arr[high]) {
			high--;
		}
		arr[low] = arr[high];
           while (low < high && temp >= arr[low]) {
                low++;
           }
           arr[high] = arr[low];	
	}
	arr[low] = temp;
	return low;
}

堆排序:

 public static void heapSort(int[] arr) {
        if (arr == null || arr.length < 2){
            return;
        }
        //构造大根堆
        heapInsert(arr);
        int size = arr.length;
        while (size > 1) {
            //固定最大值
            swap(arr, 0, size - 1);
            size--;
            //构造大根堆
            heapify(arr, 0, size);
        }
    }
    public static void heapInsert(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            //当前索引
            int currentIndex = i;
            //父结点索引
            int fatherIndex = (currentIndex - 1) / 2;
            //如果当前插入的值大于其父结点的值,则交换值,并且将索引指向父结点
            //然后继续和上面的父结点值比较,直到不大于父结点,则退出循环
            while (arr[currentIndex] > arr[fatherIndex]) {
                swap(arr, currentIndex, fatherIndex);
                currentIndex = fatherIndex;
                fatherIndex = (currentIndex - 1) / 2;
            }
        }
    }
//剩余的数构造成大根堆
public static void heapify(int[] arr, int index, int size) {
    int left = 2 * index + 1;
    int right = 2 * index + 2;
    while (left < size) {
        int largestIndex;
        if (arr[left] < arr[right] && right < size) {
            largestIndex = right;
        } else {
            largestIndex = left;
        }
        //比较父结点的值与孩子中较大的值,并确定最大值的索引
        if (arr[index] > arr[largestIndex]) {
            largestIndex = index;
        }
        //为大根堆时
        if (index == largestIndex) {
            break;
        }
        swap(arr, largestIndex, index);
        index = largestIndex;
        left = 2 * index + 1;
        right = 2 * index + 2;
    }
}
//交换数组中两个元素的值
public static void swap(int[] arr, int i, int j) {
     int temp = arr[i];
     arr[i] = arr[j];
     arr[j] = temp;
}

递归应用题

题目 : 使用递归算法输出某个目录下及其子目录下所有文件.

递归 : 自动调用自己 , 需要定义递归出口.

题目分析 : 参数为一个指定的目录 . 输出所有文件列表;

public class TestPrintDirAndFiles {

    public static void main(String[] args) {
        print(new File("E:/"));
    }
    
    private static void print(File file) {
        System.out.println(file.getAbsolutePath());
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                print(f);
            }
        }
    }
}

前端面试题

1.比较一下Java和JavaSciprt。

基于对象和面向对象:Java是一种真正的面向对象的语言,即使是开发简单的程序,必须设计对象;JavaScript是种脚本语言,它可以用来制作与网络无关的,与用户交互作用的复杂软件。它是一种基于对象(Object-Based)和事件驱动(Event-Driven)的编程语言,因而它本身提供了非常丰富的内部对象供设计人员使用。
- 解释和编译:Java的源代码在执行之前,必须经过编译。JavaScript是一种解释性编程语言,其源代码不需经过编译,由浏览器解释执行。(目前的浏览器几乎都使用了JIT(即时编译)技术来提升JavaScript的运行效率)
- 强类型变量和类型弱变量:Java采用强类型变量检查,即所有变量在编译之前必须作声明;JavaScript中变量是弱类型的,甚至在使用变量前可以不作声明,JavaScript的解释器在运行时检查推断其数据类型。

2.什么是ajax请求?

$.ajax({
     url:"/handle_Ajax/",
     type:"POST",
     data:{username:"Alex",password:123},
     success:function(data){
         console.log(data)
     },
     error: function (jqXHR, textStatus, err) {
         console.log(arguments);
     },
     complete: function (jqXHR, textStatus) {
         console.log(textStatus);
     },
     statusCode: {
        '403': function (jqXHR, textStatus, err) {
               console.log(arguments);
          },
         '400': function (jqXHR, textStatus, err) {
             console.log(arguments);
         }
      }
 })

3.简述jsonp及实现原理?

JSONP 是json用来跨域的一个东西。原理是通过script标签的跨域特性来绕过同源策略。

JsonP解决跨域只能发送get请求,并且实现起来需要前后端交互比较多。

JSONP的简单实现:创建一个回调函数,然后在远程服务上调用这个函数并且将JSON数据作为参数传递,完成回调。

4.列举Http请求中常见的请求方式?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y9etDuq7-1593962979576)(C:\Users\33066\AppData\Roaming\Typora\typora-user-images\image-20200629084508965.png)]

5.列举Http请求中的状态码?

1XX:指示信息–表示请求已接收,继续处理。

2XX Success(成功状态码):成功–表示请求已被成功接收、理解、接受。

200 表示从客户端发来的请求在服务器端被正常处理
204 该状态码表示服务器接收的请求已成功处理,但在返回的响应报文中不含实体的主体部分
206 该状态码表示客户端进行了范围请求,而服务器成功执行了这部分的GET请求

3XX Redirection(重定向状态码):重定向–要完成请求必须进行更进一步的操作。

301 永久性重定向
302 临时性重定向

4XX Client Error(客户端错误状态码):客户端错误–请求有语法错误或请求无法实现。

400 该状态码表示请求报文中存在语法错误
401 该状态码表示发送的请求需要有通过HTTP认证的认证信息
403 该状态码表明对请求资源的访问被服务器拒绝了
404 该状态码表明服务器上无法找到请求的资源

5XX Server Error(服务器错误状态码):服务器端错误–服务器未能实现合法的请求。

500 该状态码表明服务器端在执行请求时发生了错误。
503 该状态码表明服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。

6.vue优点?

答:轻量级框架:只关注视图层
简单易学:国人开发,中文文档,不存在语言障碍 ,易于理解和学习;
双向数据绑定:保留了angular的特点,在数据操作方面更为简单;
组件化:保留了react的优点,实现了html的封装和重用,在构建单页面应用方面有着独特的优势;
视图,数据,结构分离:使数据的更改更为简单,不需要进行逻辑代码的修改,只需要操作数据就能完成相关操作;

7.vue中的路由的拦截器的作用?

拦截器可以在请求发送前和发送请求后做一些处理。

vue路由拦截,针对要先登录才能进入的页面,判断是否有Token值,如果有则next(),否则跳转到登录页面。

在这里插入图片描述

8.列举vue的常见指令

  1. 文本插值:{{ }} Mustache
  2. DOM属性绑定: v-bind
  3. 指令绑定一个事件监听器:v-on
  4. 实现表单输入和应用状态之间的双向绑定:v-model
  5. 控制切换一个元素的显示:v-if 和 v-else
  6. 列表渲染:v-for
  7. 根据条件展示元素:v-show

9. 什么是 vue 生命周期?有什么作用?

答:每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做 生命周期钩子 的函数,这给了用户在不同阶段添加自己的代码的机会。(ps:生命周期钩子就是生命周期函数)例如,如果要通过某些插件操作DOM节点,如想在页面渲染完后弹出广告窗, 那我们最早可在mounted 中进行。

10. 数据绑定在这里插入图片描述

Linux面试题

1.Linux常用操作。

1:man rm———————————————查看命令帮助

2:mkdir———————————————-创建目录

3:touch———————————————-创建文件

4:cd—————————————————切换。

5:ls—————————————————查看目录

6:ls -lh————————————————查看目录详细

7:pwd————————————————-查看当前目录

8:vim————————————————-添加内容

9:echo————————————————追加内容

10:cat————————————————查看文件内容

11:mv————————————————-移动

12:cp————————————————-拷贝

13:mv————————————————重命名

15:find———————————————-搜索

16:rm————————————————-删除数据

17:ping———————————————-查看能不能上网

19:tar cf ————————————————打压缩

20:tar xf——————————————-解压缩

2.git常用命令

  • 1:git init—————————初始化

  • 2:git add .————————-从工作区,添加到版本库

  • 3:git commit -m”xxx”————从暂存区,添加到分支

  • 4:git status————————查看状态

  • 5:git log —————————查看版本库的日志

  • 6:git reflog————————查看所有日志

  • 7:git reset —head 版本号—-切换

  • 8:git stash————————-保存

  • 9:git stash————————-将第一个记录从“某个地方”重新拿到工作区(可能有冲突)

  • git stash list——————————————————————————查看“某个地方”存储的所有记录

  • git stash clear—————————————————————————-清空“某个地方”

  • git stash pop——————————————————————————-将第一个记录从“某个地方”重新拿到工作区(可能有冲突)

  • git stash apply —————————————————————————编号,将指定编号记录从“某个地方”重新拿到工作区(可能有冲突)

  • git stash drop —————————————————————————编号 ,删除指定编号的记录

  • 10:git branch dev—————创建分支

  • 11:git branch -d dev———-删除分支

  • 12:git checkout dev————切换分支

  • 13:git merge dev—————-合并分支

  • 14:git branch———————查看所有分支

  • 15:git clone https:xxx——-克隆

  • 16:git add origin https:xxx-起个别名

  • 17:git push origin dev ——添加到dev分支

  • 18:git pull origin master—拉代码

  • 19:git fetch origin master-去仓库获取

  • 20:git merge origin/master-和网上下的master分支合并

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章