记录java相关不清楚的知识点

不断更新中。

20200530更新;

  • “java”=="java"结果是什么

他们两个在常量池里面是一个东西,所有地址相等,结果是true

  • short s1 = 1; s1 = s1 + 1;有什么错?short s1 = 1; s1 += 1;有什么错

第一个有问题,第二个没有问题

  • java里面的参数传递

java里面都是按值传递的,对象传递其实是把对象的地址的值传递了

  • abstract 的method 是否可同时是static,是否可同时是native,是否可同时是synchronized?

不能

  • sysnchronized方法继承之后是怎么样

1、子类继承父类时,如果没有重写父类中的同步方法,子类同一对象,在不同线程并发调用该方法时,具有同步效果。

2、子类继承父类,并且重写父类中的同步方法,但没有添加关键字synchronized,子类同一对象,在不同线程并发调用该方法时,不再具有同步效果,这种情形即是"关键字synchronized不能被继承"的转述。

  • 代码块、静态代码块、构造函数的加载顺序

父类--静态变量
父类--静态初始化块

(上面两个顺序其实不是固定的,如果变量写在了静态块下面,那么静态块是最先执行的)
子类--静态变量
子类--静态初始化块
父类--变量
父类--初始化块
父类--构造器
子类--变量
子类--初始化块
子类--构造器

  • http1.0 与http1.1的区别

HTTP1.0 :

无状态、无连接,没次请求都需要重新建立了tcp连接


HTTP1.1  :  
持久连接
请求管道化
增加缓存处理(新的字段如cache-control)
增加Host字段、支持断点传输等(把文件分成几部分)


HTTP2.0  :
二进制分帧
多路复用(或连接共享)
头部压缩
服务器推送

  • http请求中的内容

1.请求行(描述客户端的请求方式请求的资源名称,以及使用的HTTP协议版本号)

2.消息头(描述客户端请求哪台主机,以及客户端的一些环境信息等)

3.一个空行

4.(不一定有的内容)具体发送的内容

  • http响应中的内容

1.状态响应行

2.消息头

3.一个空行

4.响应内容

  • servlet生命周期
  1. 加载Servlet。当Tomcat第一次访问Servlet的时候,Tomcat会负责创建Servlet的实例
  2. 初始化。当Servlet被实例化后,Tomcat会调用init()方法初始化这个对象
  3. 处理服务。当浏览器访问Servlet的时候,Servlet 会调用service()方法处理请求
  4. 销毁。当Tomcat关闭时或者检测到Servlet要从Tomcat删除的时候会自动调用destroy()方法,让该实例释放掉所占的资源。一个Servlet如果长时间不被使用的话,也会被Tomcat自动销毁
  5. 卸载。当Servlet调用完destroy()方法后,等待垃圾回收。如果有需要再次使用这个Servlet,会重新调用init()方法进行初始化操作
  6. 简单总结:**只要访问Servlet,service()就会被调用。init()只有第一次访问Servlet的时候才会被调用。destroy()只有在Tomcat关闭的时候才会被调用
  • httpServletResponser里面的print()和writer()

前者参数是字符,后者支持byte

  • forward和redirect

转发: 访问 Servlet 处理业务逻辑,然后 forward 到 jsp 显示处理结果,浏览器里 URL 不变

重定向: 提交表单,处理成功后 redirect 到另一个 jsp,防止表单重复提交,浏览器里 URL 变了

  • 数据库三范式

第一范式、第二范式、第三范式

第一范式:每列要是属性不可分割,不能出现某列里面存了姓名和性别的数据。

第二范式:每条数据要有唯一ID,也就是主键。

第三范式:消除冗余,冗余数据使用外键进行查询。

实际开发中,不一定完全按照范式来设计数据库,按照具体的业务来具体分析,破坏的比较多的就是第三范式,有时候加入冗余字段可以避免很多不必要的多表查询,实现以空间换时间。

  • 数据库的乐观锁和悲观锁

悲观锁:使用数据库的锁机制进行操作,在查询完数据的时候就把事务锁起来,直到提交事务。

乐观锁:在修改数据的时候把事务锁起来,通过version的方式来进行锁定

  • foreach与正常for循环效率对比

需要循环数组结构的数据时,建议使用普通for循环,因为for循环采用下标访问,对于数组结构的数据来说,采用下标访问比较好。

需要循环链表结构的数据时,一定不要使用普通for循环,这种做法很糟糕,数据量大的时候有可能会导致系统崩溃。

 

  • i++和++i

i=i++,此时i还是等于原值,++操作没用。i =1;i=i++;i=++i+i++*++i;此时要从操作数栈搞起。

  • java方法的传递

上图中右边是jvm内存模型。都是传值的,对象传的是地址。经过函数处理之后的内存图如下:

 

  • 重写约束

重写规则之一:重写方法不能比被重写方法限制有更严格的访问级别

重写规则之二:参数列表必须与被重写方法的相同

重写规则之三:返回类型必须与被重写方法的返回类型相同

重写规则之四:重写方法不能抛出新的异常或者比被重写方法声明的检查异常更广的检查异常。但是可以抛出更少,更有限或者不抛出异常。

重写规则之五:不能重写被标识为final的方法。
重写规则之六:如果一个方法不能被继承,则不能重写它。

  • 重载约束
  1. 必须具有不同的参数列表;
  2. 可以有不同的返回类型,只要参数列表不同就可以了;
  3. 可以有不同的访问修饰符;
  4. 可以抛出不同的异常;

也就说参数列表必须不一样,有这样的前提才能讨论是否重载,然后返回类型、访问修饰符和异常可以不同,但是不同不能用于判断重载。

  • hashCode问题

String a = "ab";String b = new String("ab");这个时候a.hashCode() == b.hashCode()

  • TCP连接的三次握手和四次挥手

三次握手指的是建立连接,四次挥手指的是断开连接。

三次握手通俗理解:

client:我要和你建立连接

server:我收到了你的请求,我同意

client:我收到了你的答复,我要准备发数据了

为什么需要三次而不是两次呢?client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段,但是server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求,于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了,由于client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据,但server却以为新的运输连接已经建立,并一直等待client发来数据。

所以三次握手是为了:为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误

四次挥手:

为什么需要四次挥手呢?TCP是全双工模式,当client发出FIN报文段时,只是表示client已经没有数据要发送了,client告诉server,它的数据已经全部发送完毕了;但是,这个时候client还是可以接受来server的数据;当server返回ACK报文段时,表示它已经知道client没有数据发送了,但是server还是可以发送数据到client的;当server也发送了FIN报文段时,这个时候就表示server也没有数据要发送了,就会告诉client,我也没有数据要发送了,如果收到client确认报文段,之后彼此就会愉快的中断这次TCP连接。

总结:所有的步骤都是为了安全可靠的连接建立和关闭,如果少了其中一步,就会出现问题。

  • 在new一个对象A时,A继承了B,此时是否也新建了对象B

不是,只会在堆上面产生A,不会新建B对象。此时如果B的构造函数里面调用的方法被A重写了,那么这个时候B的构造函数是调用的A重写的方法。

  • 为什么重写了equals(),一定要重写hashCode()

为了满足java规范:如果两个对象equals相等,那么这两个对象的hashCode也相等。加入我们重新了equals,里面的逻辑通过来一个人的姓名来判断相等,这个时候如果是两个对象它对应的姓名是一样的,这个时候equals是返回true的,hashCode如果不重写,那么就返回false,这个时候不满足规范。

  • hashcode会重复吗,怎么产生的

hashcode默认取的是对象的内存地址,它可能出现重复,比如String类,它重写了hashcode()方法,

String s ="Ea"; 

String d ="FB"; 

System.out.println(s.hashCode());

System.out.println(d.hashCode());

他们两个的值相等。这也就解释了为什么String判断相等的时候,想判断hashcode,在判断值的具体内容。

 

  • i=i++ 与 i=++i

理解:int i = 5;i=i++;这个时候结果是i=5;

原理:jvm在处理i= i++时, 会建立一个临时变量来接收i++的的值,然后返回这个临时变量的值,此时该临时变量计算出来等于5,临时变量计算完成之后,此时i = 6,但是,最后一步,临时变量的值有重新赋值给了i,这样就是说i虽然自增了但是又被赋值了5,这样输出的结果自然就是5了。

i=++i。此时临时变量的值等于6,临时变量计算完成之后,此时i =6,最后一步等号右边的值赋值给i,结果还是6

  • retrun与finally

先了解一下比较系统的原理:https://blog.csdn.net/sinat_22594643/article/details/80509266

//情况一
 public int test1() {
        int a = 0;
        try {
            a = 100;
            return a;//1.代码执行完这里面的时候,会在局表变量表里面新增一个数据,为了方便理解,暂定为:returnValue = a = 100,局部变量表存同时也存在a = 100。
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
//2.执行a=200的时候,确实是改了a的值,但是return返回的值是returnValue,这个值并没有被修改,所以最后返回的还是100
            a = 200;
        }
        return 0;
    }


情况二:
public StringBuilder test2() {
        StringBuilder stringBuilder = new StringBuilder();
        try {
            stringBuilder.append("abcde");
//1.同上,执行完return后面的表达式之后,局表变量表returnValue = 对象的引用(真实对象在堆里面)
            return stringBuilder;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
//2.这个操作把堆里面的那个对象的内容修改了,但是并没有修改returnValue对象的引用,所以最后返回的StringBuilder对象引用没有任何修改,对象的内容修改成功了,最后打印出来就是:abcdefinally
            stringBuilder.append("finally");
        }
        return null;
    }


//情况三
 public String test3() {
        StringBuilder stringBuilder = new StringBuilder();
        try {
            stringBuilder.append("abcde");
//1.同上,局部变量表里面returnvalue = 指向abced的对象引用
            return stringBuilder.toString();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
//2.修改了stringbuilder之后,returnvalue里面的对象引用没有被修改
           stringBuilder.append("finally");
        }
        return null;
    }


所以,总结如下:
finally执行完成之后,不能修改return 返回的任何数据,注意,返回对象的时候,是返回的对象的引用,对象的引用也不能被修改,除非,在finally里面写在一个return。如果finally里面还有return,那么这个return就可以把局部变量表的returnValue改掉,但是,这样写代码是不正常的

 

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