zl程序教程

您现在的位置是:首页 >  Java

当前栏目

java八股文--java基础

2023-04-18 14:05:52 时间

java基础

1.什么是面向对象,谈谈对面向对象的理解

和面向过程相比,它们是两种不同的处理问题的角度。
面向过程更注重事情的每一个步骤及顺序,面向对象更注重事情有哪些参与者以及它们各自需要做什么。

比如洗衣机洗衣服:
面向过程会将任务拆解成一系列的步骤(函数):打开洗衣机 ---> 放衣服 ---> 放洗衣粉 ---> 清洗 ---> 烘干
面向对象会拆出人和洗衣机两个对象:人:打开洗衣机、放衣服、放洗衣粉  洗衣机:清洗、烘干

面向对象三大特性:封装、继承、多态
封装:明确标识出允许外部使用的所有成员函数和数据项,内部细节对外部调用透明,外部调用无需关心内部实现
继承:子类继承父类的方法,并且可以做出自己的改变和拓展
多态:基于对象所属类不同,外部对同一个方法的调用,实际执行逻辑不同。
     继承 方法重写 父类引用指向子类对象,  父类类型 变量名 = new 子类对象;  变量名.方法名();

2.JDK JRE JVM的区别与联系

JDK:java开发工具,包含JRE和java开发所需要的工具

JRE:java运行时环境,包含JVM和类库

JVM:java虚拟机,运行编译好的字节码文件,每个系统的JVM不同,因此可以实现一处编译处处运行

3.==和equals

(1)==
在比较基本数据类型的时候,== 比较的是变量值;在比较引用类型的时候,== 比较的是堆中内存对象的地址

(2)equals
在没有重写时,equals和==相同,但是equals通常会重写。
比如String中equlas被重写,比较的是两个字符串的内容。

4.hashCode与equals

hashCode的作用是获取哈希码,也称为散列码,哈希码的作用是确定该对象在哈希表中的索引位置。散列表中存储时的是键值对,
能根据key快速的检索出对应的value。

以HashSet检查重复为例:
对象加入HashSet时,HashSet会先计算对象的hashCode值来判断对象加入的位置,看该位置是否有值。如果没有,hashSet
会假设对象没有重复出现。如果有值,这时会调用equals()方法来检查两个对象的值是否真的相同,如果两者相同,HashSet
就不会让其加入集合;如果不同,就会重新散列到其他位置。这样就大大减少了equals的次数,相应就大大提高了执行速度。

如果两个对象相等,则hashCode一定也相同
两个对象相等,对两个对象分别调用equals方法返回true
两个对象有相同的hashCode值,它们也不一定相等

5.String StringBuffer StringBuilder的区别

String是不可变的,如果尝试修改,会新生成一个字符串对象,StringBuffer 和StringBuilder是可变的。
StringBuffer 是线程安全的,StringBuilder线程不安全,所以在单线程下StringBuilder效率更高

6.重载和重写的区别

重载发生在同一个类中,方法名相同,参数列表不同,返回值类型和访问修饰符可以不同

重写发生在子类和父类中,子类重写父类方法,方法名相同,参数列表相同,返回值范围 <= 父类,抛出异常的范围 <= 父类,
访问修饰符的范围 >= 父类,子类不能重写父类私有方法

7.接口和抽象类

抽象类中可以定义抽象方法,也可以定义普通方法。     接口中只能定义抽象方法。

抽象类中的成员变量可以是各种类型的。     接口中的成员变量只能是public static final类型的。

抽象类的设计目的,是代码复用。      接口的设计目的,是对类的行为进行约束。

一个类只能继承一个抽象类,在这个抽象类中必须继承或编写出其所有子类的所有共性。因此定义抽象类的代价高。
一个类可以实现多个接口,因此在设计阶段难度会降低。

8.List和Set的区别

List:有序(按对象进入的顺序保存对象) 不去重 允许多个null元素对象。

Set:无序 去重 最多允许有一个null元素对象。

9.ArrayList和LinkedList

ArrayList底层是基于数组实现的。    LinkedList底层是基于链表实现的。

ArrayList更适合随机查找。   LinkedList更适合删除和添加。

ArrayList和LinkedList都实现了List接口,但LinkedList还额外实现了Deque接口,可以当作队列来使用。

10.HashMap和HashTable的区别,底层实现是什么

HashMap是非线程安全的。   HashTable的方法有synchronized关键字修饰,是线程安全的。

HashMap允许key和value为null。    HashTable不允许。

HashMap底层实现:
JDK1.7:数组+链表
JDK1.8:数组+链表+红黑树,链表高度到8、数组长度超过64,链表会转为红黑树。
(1)计算key的hash值,二次hash然后对数组长度取模,对应到数组下标。
(2)如果没有产生hash冲突(下标位置没有元素),则直接创建Node存入数组。
(3)如果产生hash冲突,先进行equals比较,相同则取代该元素;不同,则判断链表高度插入链表,链表高度达到8并且数组
长度到64则转变为红黑树,长度低于6则将红黑树转回到链表。
(4)key如果为null,存在下标0的位置。

11.谈谈ConcurrentHashMap的扩容机制

JDK1.7:
(1)1.7版本的ConcurrentHashMap是基于Segment分段实现的
(2)每个Segment相当于一个小型的HashMap
(3)每个·Segment内部会进行扩容,和HashMap的扩容逻辑类似
(4)先生成新的数组,然后转移原数到新数组中
(5)扩容的判断也是每个Segment内部单独判断的,判断是否超过阈值

JDK1.8:
(1)1.7版本的ConcurrentHashMap不再基于Segment实现,而是以每个数组下标作为节点实现
(2)当某个线程进行put时,如果发现ConcurrentHashMap正在进行扩容那么该线程一起进行扩容
(3)如果某个线程put时,发现没有正在进行扩容,则将key-value添加到ConcurrentHashMap中,然后判断是否超过阈值,
超过了则进行扩容
(4)ConcurrentHashMap支持多个线程同时扩容
(5)扩容之前也先生成一个新的数组
(6)在转移元素时,先将原数组分组,将每组分给不同的线程来进行元素的转移,每个线程负责一组或多组的元素转移工作

12.从jdk1.7到jdk1.8虚拟机发生了什么变化

JDK1.7中存在永久代,JDK1.8中永久代变为元空间。元空间所占的内存不是在JVM内部,而是在本地内存空间。

不管是永久代还是元空间都是方法区的具体实现。

原因:
方法区所存储的类信息通常是比较难确定的,所以对于方法区的大小是比较难指定的,太小了容易出现方法区溢出,太大了又会
占用太多JVM的内存空间,而转移到本地内存后则不会影响JVM所占用的内存。

13.说一下HashMap的put方法

1、根据key通过二次hash,使用二次hash得到的值对数组长度取模,运算得到数组下标

2、如果该数组下标位置为空,则将key和value封装为对象,并放入该位置

3、如果该数组下标位置元素不为空,则:
  (1)如果是JDK1.7,则先判断是否需要扩容,如果需要扩容就进行扩容,如果不需要扩容就生成对象,并使用头插法添加到
       当前链表中
  (2)如果是JDK1.8,则会先判断当前位置上的类型,看是红黑树还是链表
      a.如果是红黑树,则将key和value封装为一个红黑树节点并添加到红黑树去,在这个过程中会判断红黑树是否存在当前
        key,如果存在则更新value
      b.如果是链表,则将key和value封装为链表节点并通过尾插法插入到链表的最后位置去,在过程中遍历链表,判断是否
        存在当前key,如果存在就更新value。插入到链表后,会看当前链表的结点个数,如果节点个数超过了8,并且数组长
        度超过了64,则将链表转成红黑树
      c.将key和value插入到链表或红黑树中后,再判断是否需要进行扩容,如果需要就扩容,如果不需要就结束put方法

14.泛型中extends和super的区别

<? extends T> 表示包括T在内的任何T的子类

<? super T> 表示T在内的任何T的父类

15.深拷贝和浅拷贝

深拷贝和浅拷贝就是指对象的拷贝,一个对象中会存在两种类型的属性:一种是基本数据类型,一种是引用数据类型

(1)浅拷贝:只会拷贝基本数据类型的值,以及实例对象的引用地址,并不会复制一份引用地址所指向的对象

(2)深拷贝:既会拷贝基本数据类型的值,也会针对实例对象的引用地址所指向的对象进行复制

16.HashMap的扩容机制

JDK1.7:
(1)当数组中的容量达到总容量的0.75的时候开始扩容,扩容为旧数组的两倍容量,先生成新的数组
(2)遍历旧数组中的每个位置上的链表上的每个元素
(3)取每个元素的key,并基于新数组的长度,计算出每个元素在新数组中的下标
(4)将所有元素添加到新数组中
(5)所有元素转移完了之后,将新数组赋值给HashMap对象的table属性

JDK1.8:
(1)当数组中的容量达到总容量的0.75的时候开始扩容,扩容为旧数组的两倍容量,先生成新的数组
(2)遍历旧数组中的每个位置上的链表或者红黑树
(3)如果是链表,遍历链表上的每个元素,并根据它们的key重新计算数组下标,并添加到新数组中去
(4)如果是红黑树,则先遍历红黑树,根据每个元素的key计算出该元素在新数组中的下标位置
    a.统计每个下标位置的元素个数
    b.如果该位置下的元素个数超过了8,则生成一个新的红黑树,并将根节点添加到新数组的对应位置
    c.如果该位置下的元素个数没有超过8,则生成链表,并将链表的头节点添加到新数组的对应位置
(5)所有元素转移完了之后,将新数组赋值给HashMap对象的table属性

17.CopyOnWriteArrayList的底层原理是怎样的

(1)CopyOnWriteArrayList的内部也是用数组实现的,在向CopyOnWriteArrayList添加元素时,会复制一个新的数组,
    写操作在新数组上进行,读操作在原数组上进行
    
(2)写操作的时候会加锁,防止出现并发写入丢失数据的问题

(3)写操作结束之后会把原数组指向新数组

(4)CopyOnWriteArrayList允许写操作时来读取数据,大大提高了读的性能,因此适合读多写少的应用场景,但是
    CopyOnWriteArrayList会比较占内存,同时可能读到的数据不是实时最新的数据,所以不适合实时性要求很高的场景

18.java中的异常体系

(1)java中的所有异常都是来自顶级父类Throwable,Throwable下有两个子类:Exception和Error

(2)Error:是程序无法处理的错误,一旦出现这个错误,则程序将被迫停止运行

(3)Exception:不会导致程序停止,又分为两个部分:RuntimeException和CheckedException,RuntimeException
    常常发生在程序运行过程中,会导致程序当前线程执行失败;CheckedException常常发生在程序编译过程中,会导致编译
    不通过

19.什么是字节码?字节码的好处是什么

编译器(javac)将java源文件(*.java)编译成为字节码文件(*.class)。可以做到一处编译到处运行,windows上编译好的
class文件,可以直接在linux上运行,通过这种方式可以做到跨平台。

java跨平台有一个前提条件,就是不同的操作系统上安装的JDK和JRE是不一样的,虽然字节码是通用的,但是需要把字节码
解释成各个操作系统的机器码需要不同的解释器,所以针对各个操作系统需要有各自的JDK或JRE

采用字节码的好处:
(1)可以实现跨平台
(2)提高了代码执行的性能,编译器在编译源码时可以做一些编译期的优化,比如锁消除、标量替换、方法内联等

20.数据库连接池的工作原理和实现方案

(1)数据库连接是一种关键的有限的昂贵资源,对数据库连接的管理能显著影响到整个应用程序的伸缩性和
	健壮性,影响到程序的性能指标

(2)数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而
	不是重新建立一个

(3)释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接泄露

(4)数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小
	数据库连接数来设定的。无论这些数据库连接是否被使用,连接池都将一直保证至少这么多的连接数量

(5)连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接
	数超过最大连接数量时,这些请求将被加入到等待队列中

21.什么是反射

(1)反射是用来描述类的信息的

(2)对于任意一个类,都能够知道这个类的所有属性和方法

(3)对于任意一个对象,都能够调用它的任意一个方法和属性

(4)这种动态获取类的信息以及动态调用对象的方法的功能称为java语言的发射机制


22.switch的作用范围

(1)可以用在:byte short char int enum String

(2)不可以:long

23.&和&&的区别

(1)&:逻辑与     &&:短路与

(2)它们在程序中最终的运算结果是完全一致的,如果是&运算发:不管左边是true还是false,右边表达式
	一定会执行   如果是&&:如果左边表达式的结果为false,右边表达式不执行

(3)&运算符还可以使用在二进制位运算上,按位与

24.short类型与int类型计算

(1)short s1 = 1; s1 = s1+1; 这个是错误的,由于1是int类型,因此 s1+1的运算结果也是int
	类型,需要强制类型转换才能赋值给short类型

(2)short s1 = 1; s1 += 1;  这个是正确的,因为s1 += 1相当于s1 =(short)(s1+1),其中
	有隐含的强制类型转换

25.数组和String有没有length()方法

数组中只有length属性
String有length()方法

26.最有效率的方法计算2乘以8

2<<3,将2左移3位

27.Integer常量池的范围在 -128~127 之间

public class Test {

    public static void main(String[] args) {
        Integer t1=100,t2=100,t3=130,t4=130;
        System.out.println(t1==t2);//true
        System.out.println(t3==t4);//false
        //因为t1/t2的值在 -128~127 之间,那么不会new新的Integer对象
        //而是直接引用常量池中的Integer对象,所以结果是true
    }

}

28.java中实现多态的机制是什么

父类或接口定义的引用变量可以指向子类或者具体实现类的实例对象,而程序调用的方法在运行期才动态绑定,
就是引用变量所指向的具体实例对象的方法,也就是内存里正在运行的这个对象的方法,而不是引用变量的类
型中定义的方法

29.构造方法有哪些特性

(1)名字与类名相同

(2)没有返回值

(3)生成类的对象时自动执行,无需调用

(4)不可以被重写,的那可以被重载

30.值传递和引用传递的区别

(1)值传递:指在调用方法时将实际参数复制一份到方法中,这样的话如果方法对其传递过来的形式参数
	进行修改,将不会影响到实际参数

(2)引用传递:指在调用方法时将对象的地址直接传递到方法中,如果在对形式参数进行修改,将影响
	到实际参数的值

31.final关键字

(1)被final关键字修饰的类不能被继承。final类中的成员变量可以根据需要设为final,但是final
	类中的所有成员方法都会被隐式地指定为final方法

(2)被final修饰地方法不能被重写,以防任何继承类修改它

(3)被final修饰的基本数据类型的变量,其数值一旦在初始化之后便不能更改
	被final修饰的引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象,但是它指向的对象
	的内容可以改变

31.fianl和static的区别

static数于类级别的不可变,而final是对象级别的不可变

32.静态变量和实例变量的区别

(1)静态变量:是不属于任何实例对象,属于类的,在内存中只会有一份,在类的加载过程中,JVM只为
	静态变量分配一次内存空间

(2)实例变量:每次创建对象,都会为每个对象分配成员变量内存,实例变量是属于实例对象的,在内存
	中,创建几次对象,就有几份成员变量

33.静态方法和实例方法有何不同

(1)外部调用静态方法时,可以使用 类名.方法名 和 对象名.方法名 两种方式
	而实例方法只有后面这种方式

(2)静态方法在访问本类成员时,只允许访问静态成员(即静态变量和静态方法)
	而实例方法则无此限制
public class Test {

    public static void main(String[] args) {
        A b=new B();//执行到此处,结果为 1a2b
        b=new B();执行到此处,结果为 1a2b2b
    }

}

class A{
    static {
        System.out.println("1");
    }
    public A(){
        System.out.println("2");
    }
}

class B extends A{
    static {
        System.out.println("a");
    }
    public B(){
        System.out.println("b");
    }
}

34.String str = “i” 和String str = new String(“1”)一样吗

不一样,因为内存的分配方式不一样
(1)String str = “i”  的方式JVM会将其分配在常量池
(2)String str = new String("i") 的方式JVM会将其分配在堆内存中
	String str = new String("i")是创建了两个对象,一个是静态存储区的"i",
											  一个是用new创建在堆上的对象

35.String类的常用方法

public class Test {

    public static void main(String[] args) {
        String str="abcd123";
        int a = str.indexOf('a');//返回指定字符的索引
        char c = str.charAt(2);//返回指定索引处的字符
        str.replace('a','A');//字符串替换 a:oldChar A:newChar
        str.trim();//去除字符串两端空格
        //将字符串从"d"处分割,返回分割后的字符串数组 ["abc","123"]
        String[] ds = str.split("d");
        byte[] bytes = str.getBytes();//返回字符串byte类型数组[97 98 99 100 49 50 51]
        int length = str.length();//返回字符串长度
        char[] chars = str.toCharArray();//返回字符串char类型的数组
        String s = str.toLowerCase();//将字符串转换为小写字母
        String s1 = str.toUpperCase();//将字符串转换为大写字母
        //截取字符串 1:开始位置 3:结束位置,1位置的字符会被截取到 3位置的不会被截取到
        String substring = str.substring(1, 3);
        boolean equals = str.equals("1234");//比较字符串是否相等 
    }

}

36.String中的equals是如何重写的

(1)首先会判断要比较的两个字符串它们的引用是否相等,如果相等的话直接返回true,不相等的话继续判断

(2)然后再判断被比较的对象是否是String的实例,如果不是的话直接返回false

(3)如果是的话,再比较两个字符串的长度是否相等,如果不相等的话直接返回false

(4)长度如果相同,会比较字符串中的每个字符是否相等,一旦有一个字符不相等,就直接返回false

37.Object类中finalize()方法作用

垃圾回收器回收对象前,会调用此方法,可以在此方法中做释放资源等清理操作

38.System.gc() 和 Runtime.gc() 作用

提示JVM要进行垃圾回收

39.内部类可以引用它的包含类(外部类)的成员吗

一个内部类对象可以访问创建它的外部类对象的成员,包括私有成员

40.简单概述下异常

(1)Throwable是java语言中所有错误和异常的超类

(2)Error类及其子类:程序无法处理的错误,表示运行应用程序中出现了严重的错误

(3)Exception:程序本身可以捕获并且处理的异常。Exception又分为两类:运行时异常和编译时异常

(4)运行时异常:都是RuntimeException类及其子类异常,如空指针异常、下标越界异常等,这些异常
	是不检查异常,程序中可以选择捕获处理也可以不处理。javac编译器不会检查它,即使不捕获它或抛出
	它,也会编译通过

(5)编译异常:从语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、
	SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。

41.什么是序列化以及序列化的优势

(1)序列化就是指把对象转换为字节序列的过程,序列化后的字节流保存了对象的状态以及相关的描述信息,
	从而方便从网络上传输或者保存在本地文件中,达到对象状态的保存与重建的目的

(2)反序列化:客户端从文件或网络上获得序列化后的对象字节流后,根据字节流中所保存的对象状态及描述
	信息,通过反序列化重建对象

优势:
(1)实现了数据的持久化,通过序列化可以把数据永久的保存在硬盘上
(2)利用序列化实现远程通信,即在网络上传送对象的字节序列
(3)通过序列化在进程间传递对象

42.集合和数组的区别

(1)数组是固定长度的     集合是可变长度的

(2)数组可以存储基本数据类型,也可以存储引用数据类型    集合只能存储引用数据类型

(3)数组存储的元素必须是同一个数据类型    集合存储的对象可以是不同数据类型