精华内容
下载资源
问答
  • Java是面向对象的编程语言,不同于C语言是面向过程的。对于面向对象和面向过程的区别,举一个简单的例子说明一下(我们以洗衣机洗衣服为例): 面向过程:面向过程的编程方式,程序会将要完成的某一个任务拆解成一...
    • 临近秋招,备战暑期实习,祝大家每天进步亿点点!
    • 本篇总结的是Java基础知识相关的面试题,后续会每日更新~

    在这里插入图片描述


    1、请你说一下什么是面向对象?

    Java是面向对象的编程语言,不同于C语言是面向过程的。对于面向对象和面向过程的区别,举一个简单的例子说明一下(我们以洗衣机洗衣服为例):

    • 面向过程:面向过程的编程方式,程序会将要完成的某一个任务拆解成一系列的小步骤 (函数),如:
      • ① 打开洗衣机:method01()
      • ② 放入要洗的衣服:method02()
      • ③ 放入洗衣服:method03()
      • ④ 清洗:method04()
      • ⑤ 烘干:method05()
    • 面向对象:面向对象的编程方式,程序会将要完成的洗衣机洗衣服的任务拆分成如下两个对象:
      • 人(Person):Person在洗衣机洗衣服这个程序任务中有三个作用,分别是打开洗衣机放入要洗的衣服放入洗衣粉
      • 洗衣机(Machine):Machine在洗衣机洗衣服这个程序任务中有两个作用,分别是清洗烘干

    从上面这个例子能看出,面向过程的编程方式比较直接且高效,而面向对象的编程方式更易复用、扩展和维护


    2、请你简述一下面向对象的三个基本特征?

    • 继承:承是Java中面向对象最显著的一个特征,继承是从已有的类中派生出新的类,新的类可以吸收已有的属性、行为,并扩展新的能力。Java中不支持多继承,但是接口可以支持多实现。

    • 封装:将同一类事物的特征和功能包装在一起,只对外暴露需要调用的接口。封装也称为信息的隐藏,在Java中接口是体现封装最常用的方法,在接口中我们没有任何功能的实现(具体实现都交给实现类),只是定义了一系列抽象的方法声明用于外部调用

    • 多态:封装和继承都是为多态来服务的,多态是指同一个行为具有多个不同的表现形式。在Java中方法的重载和重写是实现多态的2种方式。

      • 重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载。方法重载体现了编译时的多态性
      • 重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型,重载对返回类型没有特殊的要求。方法重写体现了运行时的多态性

      多态的三要素:继承 、重写、父类指向子类引用


    3、为什么说 Java 是一种半解释半编译的程序设计语言呢?

    什么是编译形语言,什么又是解释形语言

    • 编译型语言:把做好的源程序全部编译成二进制代码的可运行程序。然后,就可以直接运行这个程序。执行速度快效率高,依靠编译器,跨平台性稍差
    • 解释型语言:把已经做好的源程序,翻译一句,执行一句,直到结束。执行速度慢效率低,依靠编译器,但是跨平台性稍好

    那么为什么说Java 是编译型语言呢

    第一个观点认为 Java 是编译型语言,因为Java程序想要运行,那么第一步就是要使用Javac进行编译(将Java源文件编译成.class二进制文件)。没有经过编译的.java文件,是没办法运行的!

    那么为什么又说Java 是解释型语言呢

    那么第二个观点则是认为Java是解释型语言,Java经过编译,Javac.java源文件编译成.class二进制文件之后,仍然需要借助 JVM 的解释执行。

    综合上面两个观点来看,Java似乎既有编译型语言的特点,又有解释型语言的特点,也没有看到哪本权威的书籍上认定Java就是哪一种类型的语言。


    4、请你说一下Java中的8大基本类型是那些?

    如图所示:

    8种基本数据类型和取值范围

    基本类型 大小(位/bit) 字节数(byte) 最小值 最大值 默认值 包装器类型
    boolean - - false true false Boolean
    char 16 bits 2 bytes Unicode 0 Unicode 2^16-1 Character
    byte 8 bits 1 byte -2^7 2^7-1 0 Byte
    short 16 bits 2 bytes -2~15 2^15-1 0 Short
    int 32 bits 4 bytes -2^31 2^31-1 0 Integer
    long 64 bits 8 bytes -2^63 2^63-1 0 Long
    float 32 bits 4 bytes 0.0 Fload
    double 64 bits 8 bytes 0.0 Double

    注意:对于boolean值,在Java规范中并没有给出其储存大小,在《Java虚拟机规范》中给出了4个字节,和boolean数组1个字节的定义,具体还要看虚拟机实现是否按照规范来,所以1个字节、4个字节都是有可能的。除了void之外,其他8种基本数据类型被称为八大基本数据类型

    图中从左向右的转换都是隐式转换,无需再代码中进行强制转换。从右向左均要进行强制类型转换,才能通过编译。强制转换会丢失精度。


    5、请你讲讲抽象类和接口有什么区别?

    • (一) 继承方面

      • 抽象类只能单继承;而接口可以多实现;
    • (二) 成员属性方面

      • 抽象类中可以有普通属性,也可以有常量;
      • 接口中的成员变量全部默认是常量,使用public static final修饰,这个可以省略不写;
    • (三) 代码块方面

      • 抽象类可以含初始化块;接口不能含初始化块;
    • (四) 构造函数方面

      • 抽象类可以有构函数,但是这里的构造函数不是用来创建对象的,而且用来被实现类调用进行初始化操作的;
      • 接口不能有构造函数;
    • (五) 方法方面

      • 接口在JDK1.8之后可以定义抽象方法(无方法体)、default 修饰的默认方法(有方法体)、static修饰的静态方法(有方法体),JDK1.8以前是只能有抽象方法。

        public interface Test { 
            static void test() { 
            } 
            
            default void test2(){ 
            } 
            
            void test3();// 默认是abstract修饰 
        }
        
      • 抽象类中除了静态方法和抽象方法外还可以有普通方法。

    二者相同之处

    • 接口与抽象类都不能被实例化,需要被其他进行实现或继承。
    • 接口与抽象类里面都能包含抽象方法,实现接口或继承抽象类的子类都必须实现这些抽象方法。

    6、请判断当一个对象被当作参数传递给一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?

    是值传递。java 编程语言只有值传递参数。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的内容可以在被调用的方法中改变,但对象的引用是永远不会改变的。

    java中只有值传递,基本类型传递的是值的副本,引用类型传递的是引用的副本


    7、请你说一下JVM/JRE/JDK的区别?

    直接看一张图就可以理解他们的区别了:

    • JVM = Java虚拟机
    • JRE = JVM + 基础类库
    • JDK = JVM + 基础类库 + 编译工具


    8、请你说一下方法重载和方法重写的区别?

    • 重载:方法重载发生在同一个类中,重载的方法之间方法名必须相同,参数列表不同(参数的类型、参数的个数),方法的返回值和访问修饰符可以不同,发生在编译时期(方法重载实现了编译时多态)。
    • 重写:方法重写发生在子父类中,子类重写父类的方法,方法名称必须相同,参数列表也必须相同,方法的返回值小于等于父类方法的返回值,访问修饰符方位大于等于父类方法(如果父类方法修饰符为private,则子类就无法重写了)。

    9、请你说一下List接口和Set接口的区别?

    • List:有序、可重复集合。按照对象插入的顺寻保存数据,允许多个Null元素对象,可以使用iterator迭代器遍历,也可以使用get(int index)方法获取指定下标元素。
    • Set:无序、不可重复集合只允许有一个Null元素对象,取元素时,只能使用iterator迭代器逐一遍历。
    • Map: key-value 键值对形式的集合,添加或获取元素时,需要通过key来检索到value。

    Collecttion 集合体系结构简图:

    在这里插入图片描述
    参考文章:Java集合中List,Set以及Map等集合体系详解


    10、为什么重写了equals()方法还需要重写hashCode()方法?

    equals()只是判断对象属性是否相同,hashCode()要判断二者地址是否相同。java中如果要判断两个对象是否相等,需要同时满足地址 + 属性都相同!

    • 如果两个对象相同(即:用 equals() 比较返回true),那么它们的 hashCode 值一定要相同;
    • 如果两个对象的 hashCode 相同,它们并不一定相同;

    举例子:

    只重写 equals() 方法,不重写 hashcode() 方法:

    public class Student {
    	private String name;
    	private int age;
     
    	public Student(String name, int age) {
    		super();
    		this.name = name;
    		this.age = age;
    	}
    	@Override
    	public boolean equals(Object obj) {
    		if (this == obj)
    			return true;
    		if (obj == null)
    			return false;
    		if (getClass() != obj.getClass())
    			return false;
    		Student other = (Student) obj;
    		if (age != other.age)
    			return false;
    		if (name == null) {
    			if (other.name != null)
    				return false;
    		} else if (!name.equals(other.name))
    			return false;
    		return true;
    	}
    	// 省略 get,set方法...
    }
    

    执行下面的程序看看效果:

    public class hashTest {
    	@Test
    	public void test() {
    		Student stu1 = new Student("Jimmy",24);
    		Student stu2 = new Student("Jimmy",24);
    		
    		System.out.println("两位同学是同一个人吗?"+stu1.equals(stu2));
    		System.out.println("stu1.hashCode() = "+stu1.hashCode());
    		System.out.println("stu1.hashCode() = "+stu2.hashCode());
    	}
    }
    

    如果重写了 equals() 而未重写 hashcode() 方法,可能就会出现两个没有关系的对象 equals 相同(因为equals都是根据对象的特征进行重写的),但 hashcode 不相同的情况。因为此时 Student 类的 hashcode() 方法就是 Object 默认的 hashcode()方 法,由于默认的 hashcode()方法是根据对象的内存地址经哈希算法得来的,所以 stu1 != stu2,故两者的 hashcode 值不一定相等。

    根据 hashcode 的规则,两个对象相等其 hash 值一定要相等,矛盾就这样产生了。上面我们已经解释了为什么要使用 hashcode 算法,所以即使字面量相等,但是产生两个不同的 hashCode 值显然不是我们想要的结果。

    如果我们在重写 equals() 时,也重写了 hashCode() 方法:

    public class Student {
    	private String name;
    	private int age;
    	
    	public Student(String name, int age) {
    		super();
    		this.name = name;
    		this.age = age;
    	}
    	@Override
    	public int hashCode() {
    		final int prime = 31;
    		int result = 1;
    		result = prime * result + age;
    		result = prime * result + ((name == null) ? 0 : name.hashCode());
    		return result;
    	}
    	@Override
    	public boolean equals(Object obj) {
    		if (this == obj)
    			return true;
    		if (obj == null)
    			return false;
    		if (getClass() != obj.getClass())
    			return false;
    		Student other = (Student) obj;
    		if (age != other.age)
    			return false;
    		if (name == null) {
    			if (other.name != null)
    				return false;
    		} else if (!name.equals(other.name))
    			return false;
    		return true;
    	}
    	// 省略 get,set方法...
    }
    

    再来看执行结果:

    两位同学是同一个人吗?true
    stu1.hashCode() = 71578563
    stu1.hashCode() = 71578563
    

    Student 类重写后的 hashcode() 方法中可以看出,重写后返回的新的 hash 值与 Student 的两个属性是有关,这样就确保了对象和对象地址之间的关联性。


    如果文章帮助您复习巩固了知识点,三连支持一下,后续会亿点点的更新!

    在这里插入图片描述


    为了帮助更多小白从零进阶 Java 工程师,从CSDN官方那边搞来了一套 《Java 工程师学习成长知识图谱》,尺寸 870mm x 560mm,展开后有一张办公桌大小,也可以折叠成一本书的尺寸,有兴趣的小伙伴可以了解一下,当然,不管怎样博主的文章一直都是免费的~
    在这里插入图片描述

    展开全文
  • Java实习报告

    2016-11-30 10:06:41
    内含java实习报告,主要包含学习过程和体会,以及编写代码的过程
  • java实习经历

    千次阅读 2020-05-07 09:52:19
    记录一下java实习经历 2020.4.27 实习入职第一天,

    记录一下java实习经历

    2020.4.27

    实习入职第一天,上午填了一系列表格,领取资料分配工位。然后就是配置环境,下载idea,配置SDK这些。配好环境之后从gitlab上面clone项目,之后的几天一直看代码,熟悉项目。

    2020.5.9—2020.5.12

    项目的业务和逻辑基本熟悉之后开始安排我仿写一些简单的接口,并且使用postman测试接口。基本上就是一些最简单的增删改查,稍微进阶一点写了多表连接查询还有中间表的使用。期间还安排了一次联系客户,询问需求。

    2020.5.21

    练习项目部署,首先把在idea里面把项目打包,然后在服务器搭建项目运行环境,上传war包到webapps目录下面,运行tomcat即可。

    2020.6.1

    过去的一段时间都在做与毕业相关的事情,答辩、毕业手续…
    工作上主要是练习项目中所用到的技术,比如fdfs文件系统的使用,服务器运维相关的内容,还有就是在把现在这个项目具体业务相关的功能都删掉,最后只留下一个比较纯净的环境,总的来说就是对ssm框架的使用,另外就是学习springboot,后面会用到。

    2020.6.5

    终于下定决心二战考研了,因为实在是太不甘心了,已经提交了离职报告,全心投入考研复习~~~~

    展开全文
  • 临近秋招,备战暑期实习,祝大家每天进步亿点点! 本篇总结的是Java集合知识相关的面试题,后续会每日更新~ 1、 请问如何使ArrayList保证线程安全? 答案如下: // 方式一: // synchronizedList底层相当于把集合...
    • 临近秋招,备战暑期实习,祝大家每天进步亿点点!
    • 本篇总结的是Java集合知识相关的面试题,后续会每日更新~

    在这里插入图片描述


    1、 请问如何使ArrayList保证线程安全?

    答案如下:

    // 方式一:
    // synchronizedList底层相当于把集合的set add remove方法加上synchronized锁
    List<Object> list = Collections.synchronizedList(new ArrayList<>());
    
    // 方式二:
    // 使用线程安全的CopyOnWriteArrayList,其底层也是对增删改方法进行加锁:final ReentrantLock lock = this.lock;
    
    // 方式三:
    // 自己写一个包装类,继承ArrayList 根据业务,对add set remove方法进行加锁控制
    

    2、说一说Vector 和 ArrayList 的区别?

    • 二者初始容量均为 0,即在调用空参构造函数实例化时,二者容量为 0,并在第一次加入元素数据时附上初始容量值 10
    • Vector 扩容时,如果未指定扩容递增值capacityIncrement,或该值不大于 0 时,每次扩容为原来的 2 倍,否则扩容量为capacityIncrement的值。
    • ArrayList 扩容时,每次扩容为原来的 1.5 倍。
    • Vector是线程安全集合,通过对removeadd等方法加上synchronized关键字来实现。ArrayList 是非线程安全集合。

    参考文章:JDK集合源码之ArrayList解析(附带面试题举例)JDK集合源码之Vector解析


    3、请问CopyOnWriteArrayList添加新元素是否需要扩容?具体是如何做的?

    • CopyOnWriteArrayList 底层并非动态扩容数组,不能动态扩容,其线程安全是通过加可重入锁 ReentrantLock 来保证的。
    • 当向 CopyOnWriteArrayList 添加元素时,线程获取锁的执行权后,add 方法中会新建一个容量为(旧数组容量+1)的数组,将旧数组数据拷贝到该数组中,并将新加入的数据放入新数组尾部。

    代码如下:

    public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }
    

    CopyOnWriteArrayList 适用于,读多写少的情况下(读写分离)!因为每次调用修改数组结构的方法都需要重新新建数组,性能低

    文章参考:JDK集合源码之CopyOnWriteArrayList解析


    4、HashMap 与 HashTable 的区别?

    HashMap:

    • HashMap:底层是基于数组+链表 + 红黑树非线程安全的,默认容量是16允许有空的健和值。
    • 初始size为16,扩容:newsize = oldsize << 1size一定为2的n次幂
    • Map中元素总数超过Entry数组的75%,触发扩容操作,为了减少链表长度,元素分配更均匀计算index方法:index = hash & (tab.length – 1)。
    • 扩容针对整个Map,每次扩容时,原来数组中的元素依次重新计算存放位置,并重新插入。

    HashTable:

    • HashTable:底层数组+链表实现,无论key还是value都不能为null线程安全,实现线程安全的方式是在修改数据时锁(synchroized)住整个HashTable,效率低,ConcurrentHashMap做了相关优化。
    • 初始size为11,扩容:(tab.length << 1) + 1。
    • 计算index的方法:index = (hash & 0x7FFFFFFF) % tab.length

    二者区别:

    • HashMap不是线程安全的,HashTable是线程安全的(使用 synchronized 修饰)。

    • HashMap允许将null作为一个entrykey或者value,而Hashtable不允许。

    • HashMaphash 值重新计算过(如下面的代码),Hashtable 直接使用 hashCode

      // HashMap中重新计算hash值的算法
      static final int hash(Object key) {
          int h;
          return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
      }
      
    • Hashtable继承自Dictionary类,而HashMapMap 接口的一个实现类。

    HashMap与HashTable 求桶位index的寻址算法:

    • HashMapindex = hash & (tab.length – 1)
    • HashTableindex = (hash & 0x7FFFFFFF) % tab.length

    二者求桶位index的公式都是为了使每次计算得到的桶位index更分散,这样可以降低哈希冲突。

    HashTable中:

    • 0x7FFFFFFF0111 1111 1111 1111 1111 1111 1111 1111:除符号位外的所有 1
    • (hash & 0x7FFFFFFF)得到的结果将产生正整数。
    • (hash & 0x7FFFFFFF) % tab.length 计算得到的index结果始终为正整数,且确保index的值在 tab.length 范围内!
    • HashTable 的数组长度采用奇数导致的hash冲突会比较少,采用偶数会导致的冲突会增多!所以初始容量为 11,扩容为newsize = olesize << 1+1(即 2n + 1 倍),保证每次扩容结果均为奇数。

    HashMap中:

    • 初始容量为 16,当有效数据数量达到数组容量的 0.75 倍时,触发扩容。
    • 桶位计算公式:index = hash & (tab.length – 1),计算桶位index时,容量一定要为 2n 次幂(即偶数),这样是为了减少 hash 冲突,扩容:newsize = oldsize << 1(即 2n 倍),得到的结果也是偶数。
    • 此外桶中的链表长度大于 8 时且数组长度达到 64,链表进行树化,小于 6 时进行反树化。
    • JDK1.8前HashMap中的链表采用的是头插法,优点是:效率高于尾插法,因为不需要遍历一次链表再进行数据插入。
    • JDK1.8后使用尾插法,之所以采用尾插法是因为要去判段链表的长度是否大于了 8,这种情况要考虑树化。
    • HashMap解决哈希冲突的方法采用的是:链表法
    • HashMap是先插入数据再判断是否需要库容!

    文章参考:JDK集合源码之HashMap解析(上)JDK集合源码之HashMap解析(下)JDK集合源码之HashTable解析


    5、HashMap和TreeMap的区别?

    • HashMap 上面介绍过了,直接看TreeMap
    • TreeMap 底层是基于平衡二叉树(红黑树),可以自定义排序规则,要实现 Comparator 接口,能便捷的实现内部元素的各种排序 TreeMap(Comparetor c),但是性能比 HashMap 差。

    6、Set和Map的关系

    • 二者核心都是不保存重复的元素,存储一组唯一的对象。
    • Set 的每一种实现都是对应 Map里面的一种封装。
    • 例如HashSet 底层对应的就是封装了HashMapTreeSet底层就是封装了TreeMap

    7、HashMap底层为什么选择红黑树而不用其他树,比如二叉查找树,为什么不一开始就使用红黑树,而是链表长度到达8且数组容量大于64的时候才树化?

    • 二叉查找树在特殊情况下也会变成一条线性结构,和原先的长链表存在一样的深度遍历问题,查找性能慢,例如:
      在这里插入图片描述
    • 使用红黑树主要是为了提升查找数据的速度,红黑树是平衡二叉树的一种,插入新数据(新数据初始是红色结点插入)后会通过左旋,右旋,变色等操作来保持平衡,解决单链表查询深度的问题。
    • 之所以一开始不用红黑树是因为,当链表数据量少的时候,遍历线性链表比遍历红黑树消耗的资源少 (因为少量数据,红黑树本身自选、变色保持平衡也是需要消耗资源的),所以前期使用线性表。
    • 而之所以以 8 为树化门槛,是因为经过大量测试,8 这个值是最合适的。理想情况下,使用随机的哈希码,节点分布在 hash 桶中的频率遵循泊松分布,按照泊松分布的公式计算,链表中节点个数为 8 时的概率为 0.00000006,这个概率足够低了,并且到 8 个节点时,红黑树的性能优势也会开始展现出来,因此 8 是一个较合理的数字。

    既然提到了红黑树,也必然会问红黑树的5个性质:

    红黑树的性质
    性质1:每个节点要么是黑色,要么是红色
    性质2:根节点是黑色
    性质3:每个叶子节点(NIL)是黑色
    性质4:每个红色节点的两个子节点一定都是黑色。不能有两个红色节点相连。
    性质5:任意一节点到每个叶子节点的路径都包含数量相同黑结点。俗称:黑高

    红黑树实例图:

    在这里插入图片描述

    红黑树并不是一个完美平衡二叉查找树,从图上可以看到,根结点P的左子树显然比右子树高,

    但左子树和右子树的黑结点的层数是相等的,也就是说,任意一个结点到到每个叶子结点的路径都包含数量相同的黑结点(性质5)

    所以我们叫红黑树这种平衡为黑色完美平衡

    那么当某个桶位发生 hash 冲突时,不直接使用红黑树,而是先使用链表呢?

    • 首先,我们要知道,链表不是数组,它的存储地址并不是连续的,当检索数据时,需要通过指针逐一 next ,直到找到目标数据。
    • 如果桶中 hash 冲突次数较少,那么遍历链表耗费的时间并不多。但是,一旦 hash 冲突次数比较多,导致桶位中形成的链表长度很长,那么遍历一次长链表花费的时间就需要很多!
    • 而如果将长链表树化成一颗红黑树,红黑树是一种特殊的二叉树,二叉树是可以对半查找的,理想情况下可以直接将查询耗时折半!
    • 之所以一开始不直接使用红黑树,因为毕竟树结构占用的存储空间肯定是要比链表大很多的,因此当链表长度较短时,没必要树化!

    总之,链表和红黑树的取舍完全是出于对时间效率和空间大小的一种权衡把~

    参考文章:HashMap底层红黑树实现(自己实现一个简单的红黑树)


    8、为什么 HashMap 容量必须是 2 的 N 次幂?如果输入值不是 2 的幂比如 10 会怎么样?

    问题一:为什么 HashMap 容量必须是 2 的 N 次幂

    • 核心目的是为了使插入的结点均匀分布,减少 hash 冲突

    HashMap 构造方法可以指定集合的初始化容量大小,如:

    // 构造一个带指定初始容量和默认负载因子(0.75)的空 HashMap。
    HashMap(int initialCapacity)
    

    当向 HashMap 中添加一个元素的时候,需要根据 key 的 hash 值,去确定其在数组中的具体桶位(寻址算法)HashMap 为了存取高效,减少碰撞,就是要==尽量把数据分配均匀每个链表长度大致相同==,这个实现的关键就在把数据存到哪个链表中的算法。

    这个算法实际就是取模运算:hash % tab.length,而计算机中直接求余运算效率不如位移运算。所以源码中做了优化,使用 hash & (tab.length- 1)来寻找桶位。而实际上 hash % length 等于 hash & ( length - 1) 的前提是 length 必须为 2 的 n 次幂

    例如,数组长度 tab.length = 8 的时候,3 & (8 - 1) = 3,2 & (8 - 1) = 2,桶的位置是(数组索引) 32,不同位置上,不发生 hash 碰撞。

    再来看一个数组长度(桶位数)不是 2n 次幂的情况:

    从上图可以看出,当数组长度为 9 (非 2n 次幂)的时候,不同的哈希值 hashhash & (length - 1)所得到的数组下标相等(很容易出现哈希碰撞)。

    那么我们来总结一下 HashMap 数组容量使用 2n 次幂的原因:

    • ① 当根据 keyhash 值寻址计算确定桶位下标 index 时,如果HashMap 的数组长度 tab.length2n 次幂数,那么就可以保证新插入数组中的数据均匀分布,每个桶位都有可能分配到数据,而如果数组长度不是 2n 次幂数,那么就可能导致一些桶位上永远不会被插入到数据,反而有些桶位频繁发生 hash 冲突,导致数组空间浪费,冲hash 突概率增加。
    • ② 一般我们人的逻辑寻找数组桶位下标 index ,往往会采用取模运算的方式来确定 index,即 index = hash % length,然而计算机进行取模预算的效率远不如位运算,因此需要被改进成 hash & (length - 1)的方式寻址。本质上,两种方式计算得到的结果是相同的,即:hash & (length - 1) = hash % length

    因此,HashMap 数组容量使用 2n 次幂的原因,就是为了使新插入的数据在寻址算法确定桶位下标时,尽量保证新数据能均匀的分布在每个桶位上,尽量降低某个桶位上频繁发生 hash 冲突的概率。毕竟某个桶位中的 hash 冲突次数越多,桶内的链表长度越长,这样导致数据检索的时候效率大大降低 (因为数组线性查询肯定要比链表快很多)。

    问题二:如果创建HashMap对象时,输入的数组长度length是10,而不是2的n次幂会怎么样呢

    例如:

    HashMap<String, Integer> hashMap = new HashMap(10);
    

    这种情况下,HashMap双参构造函数会通过 tableSizeFor(initialCapacity)方法,得到一个最接近length且大于length的 2 的 n 次幂数(比如最接近 10 且大于 102n 次幂数是 16 )

    • 大于等于该容量的最小的 2N 次方数的计算方法如下:
    static final int tableSizeFor(int cap) {
        int n = cap - 1;
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }
    

    这个方法tableSizeFor(initialCapacity);就是用于获得大于等于 initialCapacity 的最小的 2n 次幂数。

    这里设计到位运算,由于是面试题,就不再一步步讲解如何运算递推了,直接附上一个案例图,详细地推请参考下面的源码分析文章:

    文章参考:JDK集合源码分析——HashMap(上)


    9、HashMap计算 key 的 hash 值,是怎么设计的?为什么要将 hashCode 的高16位参与运算?

    HashMap中重新计算 hash 值的方法如下:

    static final int hash(Object key) {
        int h;
        // 如果key为null,则hash值为0,
        // 否则调用key的hashCode()方法计算出key的哈希值然后赋值给h,
        // 然后与 h无符号右移16位后的二进制数进行按位异或 得到最终的hash值,
        // 这样做是为了使计算出的hash更分散,
        // 为什么要更分散呢?因为越分散,某个桶的链表长度就越短,之后生成的红黑树越少,检索效率越高!
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
    
    • 如上代码所示:将 keyhashCode 的高 16 位和 hashCode16 位 进行异或(XOR)运算,最终得到新的 hash 值。

    该方法分析如下:

    1. key.hashCode();返回散列值也就是 hashcode,假设随便生成的一个值。
    2. ^(按位异或运算)运算规则:相同的二进制数位上,数字相同,结果为 0,不同为 1
    3. h >>> 16 将 h 的值进行无符号右移 16 位。

    问题:为什么要这样操作呢?

    我们知道,HashMap 新插入的数据需要经过寻址算法 index = hash & (tab.length - 1)来确定桶位下标。tab.length就是数组长度,我们这里设其为 n

    如果当 n 即数组长度很小,假设是 n = 16 的话,那么 n - 115 ,其二进制数为 1111 ,这样的值和 hashCode 直接做按位与操作,实际上只使用了哈希值的后 4 位。如果当哈希值的高位变化很大,低位变化很小,这样就很容易造成哈希冲突了,所以这里把高低位都利用起来,从而解决了这个问题

    我们来看一个分析图:

    由上图,可以知道如果只使用 key.hashCode()方法计算得到的 hash 值,那么当 hash 值高位变化较大,而低位变化较小时,通过寻址算法 hash & (tab.length - 1) 得到的桶位下标 index 就更容易出现 hash 冲突了!


    10、说一说你对hash算法的理解?以及什么是hash 冲突?

    • hash 的基本概念就是把任意长度的输入,通过 hash 算法之后,映射成固定长度的输出
    • 在程序中可能会碰到两个 value 值经过 hash 算法计算之后,算出了同样的 hash 值,这种情况就叫做 hash 冲突。

    最后,再给大家推荐一个更硬核的HashMap分析点:为什么负载因子要设置为 0.75 ?


    补充题:说一说 resize 扩容时,旧数组元素向新数组中迁移的方式?

    HashMap 进行扩容时,会伴随着一次重新 hash 分配,并且会遍历旧数组中所有的元素,并将其迁移到扩容后的新数组中。旧数组中的数据迁移有三种情况,下面分别来分析一下:

    情况一、当前桶位中没有发生 hash 冲突,只有一个元素:

    这种情况下,HashMap 使用的 rehash 方式非常巧妙,因为每次扩容都是翻倍,与原来计算的 (n - 1) & hash 的结果相比,只是多了一个 bit 位,所以结点要么就在原来的位置,要么就被分配到 “原位置 + 旧容量” 这个位置。

    例如我们从 16 扩展为 32 时,具体的变化如下所示:

    由于元素在重新计算 hash 之后,数组长度 n 变为原来的 2 倍,那么 n - 1 的标记范围在高位多 1bit(红色标记),因此新的 index 就会发生这样的变化。

    说明

    上图中 5 是假设计算出来的原来的索引。这样就验证了上述所描述的:扩容之后所以结点要么就在原来的位置,要么就被分配到 “原位置 + 旧容量” 这个位置。

    因此,我们在扩充 HashMap 的时候,不需要重新计算 hash,只需要看看原来的 hash 值新增的那个 bit1 还是 0 就可以了,是 0 的话索引没变,是 1 的话索引变成 “原位置 + 旧容量” 。可以看看下图为 16 扩充为 32resize 示意图:

    正是因为这样巧妙的 rehash 方式,既省去了重新计算 hash 值的时间,而且同时,由于新增的 1bit0 还是 1 可以认为是随机的,在 resize 的扩容过程中保证了 rehash 之后每个桶上的结点数一定小于等于原来桶上的结点数,保证了 rehash 之后不会出现更严重的 hash 冲突,均匀的把之前的冲突的结点分散到新的桶中了。

    情况二、当前桶位中发生了 hash 冲突,并且形成链表,但不是红黑树:

    这时候,将桶中的链表拆分成 高位链低位链 两个链表依次放入扩容后的新数组中。文字描述不如直接上代码:

    	// 说明:hashMap本次扩容之前,table不为null
        if (oldTab != null) {
            // 把每个bucket桶的数据都移动到新的散列表中
            // 遍历旧的哈希表的每个桶,重新计算桶里元素的新位置
            for (int j = 0; j < oldCap; ++j) {
                // 当前node节点
                Node<K,V> e;
                // 说明:此时的当前桶位中有数据,但是数据具体是 
                // 1.单个数据 、 2.还是链表 、 3.还是红黑树 并不能确定
                if ((e = oldTab[j]) != null) {
                    // 原来的数据赋值为null 便于GC回收
                    oldTab[j] = null;
                    // 第一种情况:判断数组是否有下一个引用(是否是单个数据)
                    if (e.next == null)
                        // 没有下一个引用,说明不是链表,
                        // 当前桶上只有单个数据的键值对,
                        // 可以将数据直接放入新的散列表中
                        // e.hash & (newCap - 1) 寻址公式得到的索引结果有两种:
                        // 1.和原来旧散列表中的索引位置相同,
                        // 2.原来旧散列表中的索引位置i + 旧容量oldCap
                        newTab[e.hash & (newCap - 1)] = e;
                    // 第二种情况:桶位已经形成红黑树
                    else if (e instanceof TreeNode)
                        // 说明是红黑树来处理冲突的,则调用相关方法把树分开
                        // 红黑树这块,我会单独写一篇博客给大家详细分析一下
                        // 红黑树相关可以先跳过
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    // 第三种情况:桶位已经形成链表
                    else { // 采用链表处理冲突
                        // 低位链表:
                        // 扩容之后数组的下标位置,与当前数组的下标位置一致 时使用
                        Node<K,V> loHead = null, loTail = null;
                        // 高位链表:扩容之后数组的下标位置等于
                        // 当前数组下标位置 + 扩容之前数组的长度oldCap 时使用
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        
                        // 通过上述讲解的原理来计算结点的新位置
                        do {
                            // 原索引
                            next = e.next;
                         	// 这里来判断如果等于true 
                            // e这个结点在resize之后不需要移动位置
                            // 举例:
                            // 假如hash1     -> ...... 0 1111
                            // 假如oldCap=16 -> ...... 1 0000
                            // e.hash & oldCap 结果为0,则
                            // 扩容之后数组的下标位置j,与当前数组的下标位置一致
                            // 使用低位链表
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            // 举例:
                            // 假如hash2     -> ...... 1 1111
                            // 假如oldCap=16 -> ...... 1 0000
                            // e.hash & oldCap 结果不为0,则
                            // 扩容之后数组的下标位置为:
                            // 当前数组下标位置j + 扩容之前数组的长度oldCap
                            // 使用高位链表
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        
                        // 将低位链表放到bucket桶里
                        if (loTail != null) {
                            loTail.next = null;
                            // 索引位置=当前数组下标位置j
                            newTab[j] = loHead;
                        }
                        // 将高位链表放到bucket里
                        if (hiTail != null) {
                            hiTail.next = null;
                            // 索引位置=当前数组下标位置j + 扩容之前数组的长度oldCap
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
    

    情况三、桶位中形成了红黑树:

    如果面试官要问红黑树的迁移,是我的话,我选择直接放弃,块真的很复杂。

    如果想完全弄懂 HashMap源码,请参考这几篇文章:JDK集合源码之HashMap解析(上)JDK集合源码之HashMap解析(下)HashMap底层红黑树实现(自己实现一个简单的红黑树)


    总结的面试题也挺费时间的,文章会不定时更新,有时候一天多更新几篇,如果帮助您复习巩固了知识点,还请三连支持一下,后续会亿点点的更新!

    在这里插入图片描述


    为了帮助更多小白从零进阶 Java 工程师,从CSDN官方那边搞来了一套 《Java 工程师学习成长知识图谱》,尺寸 870mm x 560mm,展开后有一张办公桌大小,也可以折叠成一本书的尺寸,有兴趣的小伙伴可以了解一下,当然,不管怎样博主的文章一直都是免费的~
    在这里插入图片描述

    展开全文
  • Java实习生常规技术面试题每日十题Java基础(五)

    万次阅读 多人点赞 2021-06-09 17:12:15
    描述Java 锁机制。Comparable和Comparator接口是干什么的?列出它们的区别Java集合类框架的最佳实践有哪些?HashMap和Hashtable的区别。HashSet和TreeSet有什么区别?说出ArrayList,Vector, LinkedList的存储性能和...

    目录

    1.启动一个线程是用run()还是start()? . 

    2.线程的基本状态以及状态之间的关系。

    3.Set和List的区别,List和Map的区别?

    4.同步方法、同步代码块区别?

    5.描述Java 锁机制。

    6.Comparable和Comparator接口是干什么的?列出它们的区别

    7.Java集合类框架的最佳实践有哪些?

    8.HashMap和Hashtable的区别。

    9.HashSet和TreeSet有什么区别?

    10.说出ArrayList,Vector, LinkedList的存储性能和特性。


    1.启动一个线程是用run()还是start()? . 

    启动一个线程是调用start()方法,使线程就绪状态,以后可以被调度为运行状态,一个线程必须关联一些具体的执行代码,run()方法是该线程所关联的执行代码。 

    2.线程的基本状态以及状态之间的关系。

    1、新建状态(New):新创建了一个线程对象。 

    2、就绪状态(Runnable):也叫可运行状态。线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。 

    3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。 

    4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种: 

    ①等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。 

    ②同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。 

    ③其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。 

    5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

     

    3.Set和List的区别,List和Map的区别?

    1、Set是无序的,元素不可重复;List是有序的,元素可以重复;

    2、List存储的是单个对象的集合(有序的),Map存储的是键值对为对象的集合(无序的);

    4.同步方法、同步代码块区别?

    1.同步方法 

    即有synchronized关键字修饰的方法。  

        由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,  

    内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。

    2.同步代码块 

        即有synchronized关键字修饰的语句块。 

        被该关键字修饰的语句块会自动被加上内置锁,从而实现同步

    5.描述Java 锁机制。

    java中所说的锁就是指的内置锁,每个java对象都可以作为一个实现同步的锁,虽然说在java中一切皆对象, 但是锁必须是引用类型的,基本数据类型则不可以 。每一个引用类型的对象都可以隐式的扮演一个用于同步的锁的角色,执行线程进入synchronized块之前会自动获得锁,无论是通过正常语句退出还是执行过程中抛出了异常,线程都会在放弃对synchronized块的控制时自动释放锁。 获得锁的唯一途径就是进入这个内部锁保护的同步块或方法 。

    6.Comparable和Comparator接口是干什么的?列出它们的区别

    它们都是用于对类的对象进行比较和排序使用的接口。

    Comparable是排序接口,位于java.lang包下,若一个类实现了Comparable接口,且重写了compareTo方法,就意味着该类支持排序,常结合Collections.sort或Arrays.sort对集合或数组内的元素进行排序。

    Comparator是比较接口,位于java.util包下,我们如果需要控制某个类对象的次序,而该类本身不支持排序(即没有实现Comparable接口),那么我们就可以建立一个“该类的比较器”来进行排序,这个“比较器”只需要实现Comparator接口即可。

    Comparable相当于“内部比较器”,而Comparator相当于“外部比较器”。

    7.Java集合类框架的最佳实践有哪些?

    首先Java中的集合框架体系非常强大和完善,主要用于程序中的数据存储,从最顶层主要分为了Collection和Map接口,我们平时使用的集合类都是从这两个类别中扩展开来,正确选择要使用的集合的类型对性能非常重要。

    比如:

    1)元素的大小是固定的,而且能事先知道,我们就应该用Array而不是ArrayList。

    2)如果我们大概知道存储的数量,可以在使用集合时先给予一个初始容量大小,从而有效避免集合自动增长的算法而造成的空间浪费,如new ArrayList(30)。 

    3)为了类型安全,提高存取效率和可读性,我们优先使用泛型,并且还能有效避免ClassCastException类型转换异常。

    4)有时为了提高数据的快速定位查找,可优先使用Map键值对集合,因为Map集合在数据的查找上效率非常高,但是如果要保证数据的顺序,最好使用List

    5)使用JDK提供的不变类作为Map的键可以避免为我们自己的类实现hashCode()和equals()方法。

    6)底层的集合实际上是空的情况下,返回长度是0的集合或者是数组,不要返回null。

    8.HashMap和Hashtable的区别。

    1、HashMap和HashTable都是键值对数据结构,且都实现了Map接口,存储的元素无序;

    2、HashMap非线程安全的,而HashTable是线程安全的(HashTable里面的方法使用Synchronize关键字修饰),所以HashMap的效率高于HashTable。

    3、HashMap允许空键空值,HashTable则不允许

    9.HashSet和TreeSet有什么区别?

    相同点:1、单列存储   2、元素不可重复

    不同点:1、底层数据结构不同(HashSet===哈希表结构   TreeSet===二叉树结构)

            2、数据唯一性依据不同(HashSet通过重写hashcode和equals     TreeSet通过compareable接口)

            3、有序性不同,HashSet无序,TreeSet有序

    10.说出ArrayList,Vector, LinkedList的存储性能和特性。

    1、ArrayList和LinkedList、Vector都实现了List接口;

    2、ArrayList和Vector底层是用数组实现的,而LinkedList使用双向链表实现的,在集合插入、删除元素时,ArrayList需要移动数组元素性能较差;但是在查询时,因为是连续的数组,所以查询速度快;LinkedList正好相反。

    3、在容量增长上,ArrayList增长原来50%,Vector集合增加容量原来的一倍。

    4、安全性方面Vector能够保证线程安全,但是效率比ArrayList要低。

     

    其他面试题:

    Java实习生常规技术面试题每日十题Java基础(八)

    Java实习生常规技术面试题每日十题Java基础(七)

    Java实习生常规技术面试题每日十题Java基础(六)

    Java实习生常规技术面试题每日十题Java基础(五)

    Java实习生常规技术面试题每日十题Java基础(四)

    Java实习生常规技术面试题每日十题Java基础(三)

    Java实习生常规技术面试题每日十题Java基础(二)

    Java实习生常规技术面试题每日十题Java基础(一)

    展开全文
  • Java实习生常规技术面试题每日十题Java基础(七)

    万次阅读 多人点赞 2021-06-11 15:18:26
    Java设计模式有哪些?GC是什么?为什么要有GC?Java中是如何支持正则表达式。比较一下Java和JavaSciprt。Math.round(11.5) 等于多少?Math.round(-11.5)等于多少?JDBC连接数据库的步骤?Class.forName()方法有什么...
  • 广州三本找Java实习经历

    千次阅读 多人点赞 2018-06-21 18:58:20
    这阵子跑去面试Java实习生啦~~~我来简单介绍一下背景吧。 广州三本大三在读,在广州找实习。大学开始接触编程,一个非常平庸的人。 在学习编程时,跟我类似的人应该会有一个疑问:究竟学到什么程度才能找到一份...
  • java实习面试经历

    千次阅读 2016-07-05 16:58:35
    分享面试java实习生的经历吧,找的是恒生的java实习岗位,希望对找实习的同学有所帮助。 因为前面面试的几次都失败了,都是不大的公司,虽然招的也是实习生岗位,但是他们应该要求比较高,最好直接就能拿来干活那种...
  • 临近秋招,备战暑期实习,祝大家每天进步亿点点! 本篇总结的是Java基础知识相关的面试题,后续会每日更新~ 1、请你说一下Java中的IO流?以及他们的分类和作用? IO 流的分类: 按照数据流的方向的不同,可以...
  • java实习生面试题

    万次阅读 多人点赞 2019-03-17 22:04:43
    1.Java容器框架有哪些? Java容器框架中有两个名称分别为Collection和Set的接口 2.list,map,set,array,它们有什么区别 List接口主要有三个实现类:LinkedList,ArrayList,Vector. LinkedList:底层基于链表实现,...
  • Java实习生笔试

    千次阅读 2019-02-23 23:43:11
    为了方便理解,可以将上述语句的执行 分解成两个过程 ,第一个过程是新建对象 的过程,即new String("abc");第二个过程是赋值的过程,即String s=。由于第二个过程只是定义了一个名为s的String类型的变量...
  • java实习面试题

    千次阅读 2018-08-09 14:00:53
    1.Java有那些基本数据类型,String是不是基本数据类型,他们有何区别。 Java语言提供了八种基本类型: 六种数字类型(四个整数型,两个浮点型) 字节型byte 8位 短整型short 16位 整型int 32位 长整型long 64位 ...
  • java实习课堂笔记一

    2020-07-20 17:07:33
    java实习课堂笔记一工作环境配置JDKIDEAMySqlMySql图形界面管理工具项目入门练习MySql练习实验 工作环境配置 在开始java项目工作前,我们需要配置好我们的工作环境。需要的配置环境包括JDK、IDEA、MySql以及MySql...
  • Java实习生面试题整理

    万次阅读 多人点赞 2018-09-14 10:16:34
    例如在作为网络连接参数的情况下如果 String 是可变的,那么在网络连接过程中,String 被改变,改变 String 对象的那一方以为现在连接的是其它主机,而实际情况却不一定是。 4. 线程安全 String 不可变性天生...
  • JAVA 实习面试题大全必看

    千次阅读 多人点赞 2020-04-02 09:05:31
    JAVA 实习面试题大全必看 JavaSE 88 基础语法 9 Q1:简单说说Java有哪些数据类型 答:①分为基本数据类型和引用数据类型。②基本数据类型包括:数值型(byte、short、int、long、float、double),字符型(char)...
  • JAVA实习报告内容详细

    2011-03-22 12:44:17
    有关JAVA的实际报告,过程,日志,等等
  • 我的java实习报告

    2011-02-21 17:10:13
    进一步了解java开发的相关知识,掌握java开发的基本技术,丰富java开发的实战经验。学习SQL的基础知识及正确的运用方法,和有用的相关技术,提高自己的工作效率。通过实习,巩固大学所学专业的基本知识,提高分析、...
  • 2019中原银行java实习面试

    千次阅读 2019-07-05 21:09:21
    大二结束了,暑假如果能找一个IT的实习职位就再好不过了。 笔者坐标郑州,中原银行总行所在地,该银行在暑期有招收暑期实习生的计划,在其官网上发布后,又去郑州大学总部进行了宣讲,总行招人共分三类职位,信息...
  • Java实习生】每日10道面试题打卡!

    千次阅读 多人点赞 2021-06-29 17:24:52
    A类地址:以0开头 B类地址:以10开头 C类地址:以110开头 D类地址:以1110开头 E类地址:以1111开头,保留地址 3、三次握手过程中可以携带数据吗? 第三次握手时可以携带数据,但是第一、二次不行。 原因:设想这样...
  • Java实习生常规技术面试题每日十题Java基础(一)

    万次阅读 多人点赞 2021-06-05 12:43:06
    1.Java 的 “一次编写,处处运行”如何实现? 2.描述JVM运行原理。 3.为什么Java没有全局变量? 4.说明一下public static void main(String args[])这段声明里每个关键字的作用。 5.Java 是否存在内存泄漏? 6.==与...
  • Java实习面试经验一

    千次阅读 2019-05-29 20:05:49
    Java大三实习面试经验 由于在春招大厂面试时不给力,只能靠现在在BOSS直聘、拉钩等网址投简历,正好学习有个毕业照招聘会,我也过去面试了以下Java的后台开发,以下是面试的一些问题,分享出来,大家可以借鉴下。 ...
  • Java实习日记(day1)

    千次阅读 2018-07-17 12:40:00
    Java实习报道上班的第一天,因为是初次实习,第一天的任务较为轻松,但不清闲,主要内容概述如下:  1、搭配java编译环境。  2、配置tomcat。  3、初步了解maven的含义和用处  4、配置maven  5、读现有...
  • 校招java实习生基础知识面试题

    千次阅读 多人点赞 2019-04-01 20:54:01
    前言 校招生可能没什么项目经验,更多的看潜力。这个潜力怎么体现?我理解可以从下面几个方面来看: a、学习成绩——可以反映是否勤奋:学习...Java有那些基本数据类型,String是不是基本数据类型,他们有何区别...
  • Java实习的记录1

    千次阅读 2019-04-04 16:05:44
    转眼就快步入社会却不知道何去何从,开始重新认真学习Java。 一开始跟着网上的视频一边看,一边敲。从JaveSE到EE,主流框架SSM、SSH、前端jsp、html、jq都基本学习过。前前后后算是学习有大半年时间,但却一直找不到...
  • Java实习生)每日10道面试题打卡——JVM篇

    千次阅读 多人点赞 2021-05-27 08:37:23
    临近秋招,备战暑期实习,祝大家每天进步亿点点!打卡 Day06! 有粉丝大佬要求更新有难度的,所以本篇总结的是 JVM 相关的面试题,后续会每日更新~ 注:JVM 比较枯燥,直接刷题前,最好先去串一遍 JVM 课程,这里推荐...
  • JAVA实习生面试问题

    万次阅读 多人点赞 2018-03-20 16:01:24
    1.Java有那些基本数据类型,String是不是基本数据类型,他们有何区别。Java语言提供了八种基本类型:六种数字类型(四个整数型,两个浮点型)字节型byte 8位 短整型short 16位 整型int 32位长整型long 64位 单精度...
  • Java实习生面试题分享

    万次阅读 多人点赞 2019-03-05 14:48:16
    1.Java有那些基本数据类型,String是不是基本数据类型,他们有何区别。 Java语言提供了八种基本类型: 六种数字类型(四个整数型,两个浮点型) 字节型byte 8位 短整型short 16位 整型int 32位 长整型long 64位 ...
  • 商汤java实习生面试题

    千次阅读 2018-09-09 19:29:42
    前两天面深圳商汤,java实习生。记录一下。 技术一面。 前台传递数据给后台的详细过程。画图 https协议,运用了加密。过程是什么? servlet实现了什么接口。没答上来。退而求次,回答了servlet里面有什么方法。...
  • Java实习生常规技术面试题每日十题Java基础(三)

    万次阅读 多人点赞 2021-06-07 11:04:26
    Overload和Override的区别。参数列表相同,返回值不同的方法,...如何理解Java中的Serialization和Deserialization。String是最基本的数据类型吗?如何实现字符串的反转及替换。String("xyz");创建了几个字符串对象。
  • java实习面试题总结

    2018-08-15 09:54:19
    基础篇 String、StringBuffer和StringBuilder的区别 Array、ArrayList和LinkedList区别 HashMap和Hashtable的区别 HashMap和ConcurrentHashMap的区别 ... 面向对象与面向过程的区别 重载和重...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 30,415
精华内容 12,166
关键字:

java实习过程

java 订阅