Java基础知识_模拟面试_1

  1. JDK JRE JVM 三者的介绍以及三者之间关系。
    JDK:
    JDK是Java开发工具包。
    JDK中包含JRE,在JDK的安装目录下有一个名为jre的目录,里面有两个文件夹bin和lib,在这里可以认为bin里的就是JVM,lib中则是JVM工作所需要的类库,而JVM和 lib和起来就称为JRE。
    JDK是整个JAVA的核心,包括了Java运行环境JRE(Java Runtime Envirnment), 一堆Java工具和Java基础的类库。
    JRE:
    JRE是Java运行环境(Java Runtime Envirnment),是运行基于Java语言编写的程序所不可缺少的运行环境。
    通过JRE,Java的开发者才得以将自己开发的程序发布到用户手中,让用户使用。
    JRE中包含了Java virtual machine(JVM),runtime class libraries和Java application launcher,这些是运行Java程序的必要组件。
    JRE是Java运行环境,并不是一个开发环境,所以没有包含任何开发工具(如编译器和调试器),只是针对于使用Java程序的用户。
    JVM:
    JVM是Java虚拟机(Java Virtual Machine),整个java实现跨平台的最核心的部分,所有的java程序会首先被编译为.class的类文件,这种类文件可以在虚拟机上执行。
    三者关系:
    在这里插入图片描述

  2. 运算符
    & 和 && |和||
    && 如果两个操作数都非零,则条件为真;
    || 如果两个操作数中有任意一个非零,则条件为真。
    & 按位与操作,按二进制位进行"与"运算
    & 和 && 在判断语句中都可以实现“和”这个功能,
    不过区别在于 & 两边都运算,而 && 先算 && 左侧,
    若左侧为 false 那么右侧就不运算了。
    因此从效率上来说,判断语句中推荐使用 &&
    而 | 和 || 的比较与上类似。

  3. euqals()和“==”的区别
    equals():比较的是两个字符串的内容,属于内容比较。
    equals ()方法用来比较两个独立对象的内容是否相等。
    字符串基本都是使用equals()方法。
    如果一个类没有自己定义equals()方法,那么将继承Object类的equals()方法。
    如果一个类没有自己定义equals方法,equals方法默认使用操作符“==”。

    也就是比较两个变量指向的对象是否是同一对象,这时候使用equals跟==是一样的结果

    ==:比较的是两个字符串内存地址的数值是否相等,属于数值比较。
    == : 就是比较变量所对应的内存中所存储的数值是否相同。

    要比较两个基本类型的数据或两个引用变量是否相等,只能用 “==” 操作符。

    重写 euqals()时 为什么重写hashcode方法
    因为在比较两个对象相等的时候,可以通过先hashCode()方法比较。
    如果hashCode()方法比较后,如果不相等,则可以判断这两个对象不相等;
    如果hashCode()方法比较后相等,则进入equals()方法进行比较;
    如果结果是相等,两个对象相等,否则不相等。
    因此:
    hashCode相等,两个对象不一定相等;
    两个对象equals后相等,则两个对象的hashCode一定相等。

    hashCode方法
    在这里插入图片描述
    在Java中,hashCode方法的主要作用是为了配合基于散列的集合一起正常运行,这样的散列集合包括HashSet、HashMap以及HashTable。
    当向集合中插入对象时,判别在集合中是否已经存在该对象的时候,如果采用equals方法去逐一比较,效率必然是一个问题。
    此时hashCode方法的作用就体现出来了,当集合要添加新的对象时,先调用这个对象的hashCode方法,得到对应的hashcode值,实际上在HashMap的具体实现中会用一个table保存已经存进去的对象的hashcode值,如果table中没有该hashcode值,它就可以直接存进去,不用再进行任何比较了;如果存在该hashcode值, 就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址,所以这里存在一个冲突解决的问题,这样一来实际调用equals方法的次数就大大降低了,说通俗一点:Java中的hashCode方法就是根据一定的规则将与对象相关的信息(比如对象的存储地址,对象的字段等)映射成一个数值,这个数值称作为散列值。
    java.util.HashMap的中put方法的具体实现,先计算key的hash值,从table数组中取出对应节点,如果节点不存在则添加一个节点;如果存在则更新value,返回旧value。
    equals方法
    在这里插入图片描述
    如果在一个类重写了equals方法,hashCode方法没有重写,两个对象的数值相同,equals方法为true,认为是同一个人。但是两个对象的hashCode返回值不同。

  4. 子类是否可以调用父类私有的属性与方法
    1.子类对象不能再自己的方法内部,直接访问父类的私有属性或私有方法。
    2.子类对象可以通过父类的公有方法间接访问到私有属性和私有方法。

  5. 子类重写的方法和返回类型,访问权限修饰符是否可以修改
    子类重写父类方法:
    名称及参数必须和所覆盖的方法相同。修饰符权限要大于等于父类,返回类型可以是父类方法返回类型的子类,如果方法有异常,抛出的异常类型要小于等于父类的异常。

重载的方法是否可以修改访问修饰符以及返回值
重载方法:名称及参数必须相同,方法的返回类型和方法的修饰符可以不相同。

  1. HashMap
    HashMap的底层结构
    HashMap是由数组+链表+红黑树所实现。
    大方向上,HashMap 里面是一个数组,然后数组中每个元素是一个单向链表。
    当链表中的元素超过了 8 个以后,HashMap会将链表转换为红黑树,在这些位置进行查找的时候可以降低时间复杂度为 O(logN)。
    为什么Key 大多数是String类型
    String类中缓存有个hash变量,它可以缓存hashCode,避免重复计算hashCode。
    在使用 String 类型的对象做 key 时,可以只根据传入的字符串内容就能获得对应存在 map 中的 value 值。
    而非 String 类型的对象在获得对应的 value 时需要的条件太过苛刻,首先要保证散列码相同,并且经过 equals() 方法判断为 true 时才可以获得对应的 value。
    HashMap为什么使用红黑树结构
    红黑树
    红黑树其实是一种自平衡二叉查找树。
    它的左右子树高度可能大于1,严格意义上来讲,红黑树并不是完全平衡的二叉树。
    在这里插入图片描述
    1.每个节点要么是红色,要么是黑色。
    2.根节点必须为黑色。
    3.红色节点不可以连续 (红色节点的孩子不能为红色)。
    4.对于每个节点,从该节点到 null (树尾端)的任何路径,都含有相同个数的黑色节点。

Java中使用红黑树来管理HashMap,而是在hash值相同的情况下(且重复数量大于8),用红黑树来管理数据。 红黑树相当于排序数据,可以自动的使用二分法进行定位,性能较高。一般情况下,hash值做的比较好的话基本上用不到红黑树。
红黑树牺牲了一些查找性能 但其本身并不是完全平衡的二叉树。因此插入删除操作效率略高于AVL树。
AVL树用于自平衡的计算牺牲了插入删除性能,但是因为最多只有一层的高度差,查询效率会高一些。

  1. 哈希冲突
    HashMap 采用一种所谓的“Hash 算法”来决定每个元素的存储位置。
    当程序执行 map.put(String,Obect)方法 时,系统将调用String的 hashCode() 方法得到其 hashCode 值:每个 Java 对象都有 hashCode() 方法,都可通过该方法获得它的 hashCode 值。
    得到这个对象的 hashCode 值之后,系统会根据该 hashCode 值来决定该元素的存储位置。
    当、通过put(key, value)向HashMap中添加元素时,需要通过散列函数确定元素究竟应该放置在数组中的哪个位置,当不同的元素被放置在了数据的同一个位置时,后放入的元素会以链表的形式,插在前一个元素的尾部,这个时候我们称发生了hash冲突。
    哈希冲突解决方法
    常用两种方法:链表法和开放寻址法
    1.链表法(chaining)
    在哈希表中,每一个桶(bucket)或者槽(slot)都会对应一条链表,所有哈希值相同的元素放到相同槽位对应的链表中。
    在这里插入图片描述
    在插入的时候,可以通过散列函数计算出对应的散列槽位,将元素插入到对应的链表即可,时间复杂度为O(1);在查找或删除元素时,我们同样通过散列函数计算出对应的散列槽位,然后再通过遍历链表进行查找或删除,时间复杂度为O(k),k为链表长度。
    2.开放寻址法
    核心思想:如果出现散列冲突,就重新探测一个空闲位置,再将元素插入。
    一种比较简单的探测方法:线性探测法(Linear Probing)
    但我们往散列表中插入元素时,如果某个数据经过散列函数散列之后,存储位置已经被占用了,那么就从当前位置开始,依次往后遍历,直到找到空余的位置插入为止(插入第一个空余的位置方便查找)。,
    在查找元素时,先将要查找元素键值通过散列函数变成散列值,然后与下标为散列值的元素比较,若相等,则说明这是我们要找的元素;若不相等,则顺序往后遍历查找,如果遍历到数组中的空余位置还是没有找到,说明要查找的元素不在散列表中。

删除元素时,删除操作不能简单地把元素设置为空,而是要特殊标记为deleted,因为如果简单设置为空,在查找元素的过程中遇到这个被删除元素的位置就会停下,而不是继续往后遍历,会使查找算法失效;但是如果特色标记为deleted,当线性探测查找时,遇到标记为deleted的位置就会往下探测。

线性探测法的缺点:当插入的数据越来越多时,散列冲突发生的可能性会越来越大,空余位置会越来越少,线性探测的时间会越来越长,最坏时间复杂度为O(n)。

另外的两种探测方法是二次探测法(Quadratic probing)和双重散列法(Double hashing)。

  1. 多线程和线程安全
    线程安全
    当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在调用代码中不需要任何额外的同步或者协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。
    volatile关键字的作用
    用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最的值。
    volatile 变量具备两种特性,volatile 变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取 volatile 类型的变量时总会返回最新写入的值。
    1.变量可见性
    volatile保证该变量对所有线程可见,这里的可见性指的是当一个线程修改了变量的值,那么新的值对于其他线程是可以立即获取的。
    2.禁止重排序
    volatile 禁止了指令重排。
    当对非 volatile 变量进行读写的时候,每个线程先从内存拷贝变量到 CPU 缓存中。如果计算机有多个 CPU,每个线程可能在不同的 CPU 上被处理,这意味着每个线程可以拷贝到不同的 CPUcache 中。而声明变量是 volatile 的,JVM 保证了每次读变量都从内存中读,跳过 CPU cache这一步。
    synchronized关键字的作用
    synchronized 可以把任意一个非 NULL 的对象当作锁。
    synchronized 作用范围
    1.synchronized 作用于方法时,锁住的是对象的实例(this);
    2.当synchronized 作用于静态方法时,锁住的是 Class 实例,又因为 Class 的相关数据存储在永久带PermGen ,永久带是全局共享的,因此静态方法锁相当于类的一个全局锁,会锁所有调用该方法的线程;
    3.synchronized 作用于一个对象实例时,锁住的是所有以该对象为锁的代码块。它有多个队列,当多个线程一起访问某个对象监视器的时候,对象监视器会将这些线程存储在不同的容器中。
    synchronized 底层实现原理
    1.JVM 每次从队列的尾部取出一个数据用于锁竞争候选者(OnDeck),但是并发情况下, ContentionList 会被大量的并发线程进行 CAS 访问,为了降低对尾部元素的竞争,JVM 会将一部分线程移动到 EntryList 中作为候选竞争线程。
    2.Owner 线程会在 unlock 时,将 ContentionList 中的部分线程迁移到 EntryList 中,并指定EntryList 中的某个线程为 OnDeck 线程(一般是最先进去的那个线程)。
    3.Owner 线程并不直接把锁传递给 OnDeck 线程,而是把锁竞争的权利交给 OnDeck, OnDeck 需要重新竞争锁。这样虽然牺牲了一些公平性,但是能极大的提升系统的吞吐量,在JVM 中,也把这种选择行为称之为“竞争切换”。
    4.OnDeck 线程获取到锁资源后会变为 Owner 线程,而没有得到锁资源的仍然停留在 EntryList中。如果Owner 线程被 wait 方法阻塞,则转移到WaitSet 队列中,直到某个时刻通过 notify或者 notifyAll 唤醒,会重新进去 EntryList 中。
    5.处于 ContentionList、EntryList、WaitSet 中的线程都处于阻塞状态,该阻塞是由操作系统来完成的(Linux 内核下采用 pthread_mutex_lock 内核函数实现的)。
    6.synchronized 是非公平锁。 Synchronized 在线程进入 ContentionList 时,等待的线程会先尝试自旋获取锁,如果获取不到就进入 ContentionList,这明显对于已经进入队列的线程是不公平的,还有一个不公平的事情就是自旋获取锁的线程还可能直接抢占 OnDeck 线程的锁资源。
    ReentrantLock 与 synchronized 区别
    1.ReentrantLock 通过方法 lock()与 unlock()来进行加锁与解锁操作,与 synchronized 会被 JVM 自动解锁机制不同,ReentrantLock 加锁后需要手动进行解锁。为了避免程序出现异常而无法正常解锁的情况,使用 ReentrantLock 必须在 finally 控制块中进行解锁操作。
    2.ReentrantLock 相比 synchronized 的优势是可中断、公平锁、多个锁。这种情况下需要使用 ReentrantLock。
    线程死锁
    死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
    Java中死锁最简单的情况是,一个线程T1持有锁L1并且申请获得锁L2,而另一个线程T2持有锁L2并且申请获得锁L1,因为默认的锁申请操作都是阻塞的,所以线程T1和T2永远被阻塞了。导致了死锁。这是最容易理解也是最简单的死锁的形式。
    在这里插入图片描述
    死锁解决方法
    三种用于避免死锁的技术:
    1.加锁顺序(线程按照一定的顺序加锁)
    2.加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
    3.死锁检测

  2. TCP
    TCP 三次握手建立连接
    1.第一次握手:
    客户端将TCP报文标志位SYN置为1,随机产生一个序号值seq=J,保存在TCP首部的序列号(Sequence Number)字段里,指明客户端打算连接的服务器的端口,并将该数据包发送给服务器端,发送完毕后,客户端进入SYN_SENT状态,等待服务器端确认。
    2.第二次握手:
    服务器端收到数据包后由标志位SYN=1知道客户端请求建立连接,服务器端将TCP报文标志位SYN和ACK都置为1,ack=J+1,随机产生一个序号值seq=K,并将该数据包发送给客户端以确认连接请求,服务器端进入SYN_RCVD状态。
    3.第三次握手:
    客户端收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给服务器端,服务器端检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,客户端和服务器端进入ESTABLISHED状态,完成三次握手,随后客户端与服务器端之间可以开始传输数据了。

在这里插入图片描述
为什么需要三次握手
假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。但server却以为新的运输连接已经建立,并一直等待client发来数据。这样,server的很多资源就白白浪费掉了。
所以,采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接。
TCP 四次挥手关闭连接
1.第一次挥手: Client端发起挥手请求,向Server端发送标志位是FIN报文段,设置序列号seq,此时,Client端进入FIN_WAIT_1状态,这表示Client端没有数据要发送给Server端了。
2.第二次分手:Server端收到了Client端发送的FIN报文段,向Client端返回一个标志位是ACK的报文段,ack设为seq加1,Client端进入FIN_WAIT_2状态,Server端告诉Client端,我确认并同意你的关闭请求。
3.第三次分手: Server端向Client端发送标志位是FIN的报文段,请求关闭连接,同时Client端进入LAST_ACK状态。
4.第四次分手 : Client端收到Server端发送的FIN报文段,向Server端发送标志位是ACK的报文段,然后Client端进入TIME_WAIT状态。Server端收到Client端的ACK报文段以后,就关闭连接。此时,Client端等待2MSL的时间后依然没有收到回复,则证明Server端已正常关闭,那好,Client端也可以关闭连接了。

在这里插入图片描述
为什么需要四次挥手
建立连接时因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。所以建立连接只需要三次握手。

由于TCP协议是一种面向连接的、可靠的、基于字节流的运输层通信协议,TCP是全双工模式。
这就意味着,关闭连接时,当Client端发出FIN报文段时,只是表示Client端告诉Server端数据已经发送完毕了。当Server端收到FIN报文并返回ACK报文段,表示它已经知道Client端没有数据发送了,但是Server端还是可以发送数据到Client端的,所以Server很可能并不会立即关闭SOCKET,直到Server端把数据也发送完毕。
当Server端也发送了FIN报文段时,这个时候就表示Server端也没有数据要发送了,就会告诉Client端,我也没有数据要发送了,之后彼此就会愉快的中断这次TCP连接。
TCP和UDP区别
在这里插入图片描述
HTTP和HTTPS的区别
HTTP:是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),用于从WWW服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少。

HTTPS:是以安全为目标的HTTP通道,简单讲是HTTP的安全版,即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。

HTTPS协议的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种就是确认网站的真实性。

1.HTTP 明文传输,数据都是未加密的,安全性较差,HTTPS(SSL+HTTP) 数据传输过程是加密的,安全性较好。
2.使用 HTTPS 协议需要到 CA(Certificate Authority,数字证书认证机构) 申请证书,一般免费证书较少,因而需要一定费用。证书颁发机构如:Symantec、Comodo、GoDaddy 和 GlobalSign 等。
3.HTTP 页面响应速度比 HTTPS 快,主要是因为 HTTP 使用 TCP 三次握手建立连接,客户端和服务器需要交换 3 个包,而 HTTPS除了 TCP 的三个包,还要加上 ssl 握手需要的 9 个包,所以一共是 12 个包。
4.HTTP 和 HTTPS 使用的是完全不同的连接方式,用的端口也不一样,前者是 80,后者是 443。
5.HTTPS 其实就是建构在 SSL/TLS 之上的 HTTP 协议,所以,要比较 HTTPS 比 HTTP 要更耗费服务器资源。

HTTP状态码
请添加图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值