【面试八股】

1、请你说一下HTTP的报文段是什么样的?

请求行:包含请求方式(get、post、put等)、URL,Http协议版本; 请求头:包含主机host,请求时间,客户端类型、指定客户端接收的数据格式,客户端支持的语言等; 请求体:包含post方法提交的数据,也可以为空;
响应行:包含状态码、响应信息、Http协议版本; 响应头:包含响应时间,响应数据的长度、类型、字符编码等; 响应体:包含页面源码;

请求头/响应头与 “body” 中间有一个空行(CRLF)作为区分两者的边界,记住是第一个空行(CRLF)

GET /books/java.html HTTP/1.1 —请求行(如果有带参数,会拼接在URL)
----------------------------------------------------------------------------------------------------->
Accept: / —header
Accept-Language: en-us Connection: Keep-Alive
Host: localhost Referer: http://localhost/links.asp
User-Agent: Mozilla/4.0 Accept-Encoding: gzip, deflate
----------------------------------------------------------------------------------------------------->
—空白行
–body(这里没有内容)

2、从浏览器中输入URL到返回页面的过程,都发生了什么

从浏览器中输入URL到返回页面的过程,都发生了什么?

3、ServletResponse重定向和请求转发的区别

同:都可以实现页面跳转
不同:请求转发,url不改变;重定向,url会发生改变

4、JAVA中try、catch、finally带return的执行顺序总结

public class Test {
    public Student st;
    public int k = 5;

    public  Test  test() {
        Test t1 = new Test();
        int i=0;
        try {
            Student s1 = new Student("TOM", 10);
            t1.st = s1;
            t1.k=6;
            System.out.println("try{ "+"st:"+t1.st.name+","+t1.st.age+";"+"k:"+t1.k+"}");
            return t1;

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            Student s2 = new Student("Jack", 20);
            t1.st=s2;
            t1.k=7;
            System.out.println("finally{ "+"st:"+t1.st.name+","+t1.st.age+";"+"k:"+t1.k+"}");
        }
        
        Student s3 = new Student("Jac", 30);
        t1.st=s3;
        t1.k=8;
        return  new Test();
    }
    private int testReturn1() {
        int i = 1;
        try {
            i++;
            System.out.println("try:i=" + i);
            return i;
        } catch (Exception e) {
            i++;
            System.out.println("catch:i=" + i);
        } finally {
            i++;
            System.out.println("finally:i=" + i);
        }
        return i;
    }

    public static void main(String[] args) {
        Test t2 = new Test().test();
        System.out.println("return{ "+"st:"+t2.st.name+","+t2.st.age+";"+"k:"+t2.k+"}");
        int i=new Test().testReturn1();
        System.out.println("return i="+i);
    }
}

输出:

try{ st:TOM,10;k:6}i:1
finally{ st:Jack,20;k:7}i:2
return{ st:Jack,20;k:7}
try:i=2
finally:i=3
return i=2

当try中带有return时,会先执行return前的代码,然后暂时保存需要return的信息,再执行finally中的代码,最后再通过return返回之前保存的信息,但是,如果返回的是引用类型变量,finally可以通过地址改变了变量,还是会影响方法返回值的。

catch中return与try中一样,会先执行return前的代码,然后暂时保存需要return的信息,再执行finally中的代码,最后再通过return返回之前保存的信息

当finally中有return的时候,try中的return会失效,在执行完finally的return之后,就不会再执行try中的return。

总结:

1、finally中的代码总会被执行。

2、当try、catch中有return时,也会执行finally。return的时候,要注意返回值的类型,是否受到finally中代码的影响。

3、finally中有return时,会直接在finally中退出,导致try、catch中的return失效。

5、Java中final、finally、finalize的区别与用法

final:java中的关键字,修饰符。
A).如果一个类被声明为final,不能作为父类被继承。因此,一个类不能同时被声明为abstract抽象类的和final的类。
B).如果将变量或者方法声明为final,可以保证它们在使用中不被改变.
  1)被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。
  2)被声明final的方法只能使用,不能重载。
注意:final修饰的引用类型数据不变只是代表了被存储的数据不可更改性,即引用地址不可变,并不代表了数组内容本身不可变
  
finally:java的一种异常处理机制。
  finally是对Java异常处理模型的最佳补充。finally结构使代码总会执行,而不管无异常发生。使用finally可以维护对象的内部状态,并可以清理非内存资源。特别是在关闭数据库连接这方面,如果程序员把数据库连接的close()方法放到finally中,就会大大降低程序出错的几率。
  
finalize:Java中的一个方法名。
Java技术使用finalize()方法在垃圾收集器将对象从内存中清除出去前。这个方法是由垃圾收集器在确定这个对象没被引用时对这个对象调用的。它是在Object类中定义的,因此所的类都继承了它。子类覆盖finalize()方法以整理系统资源或者执行其他清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。

6、String s=“abc“与String s=new String(“abc“)的区别?分别创建了几个对象?

  • String s1 = new String(“abc”)的创建过程:
  • 我们知道声明的对象引用名都存在栈中,而真正new的对象在堆中,而又因为"abc"是一个常量值,所以存在方法区的常量池。如果在常量池中没有"abc"的话,那么就会在常量池中开辟一块新的地址空间存放"abc",常量池新开辟的空间会有地址引用(这里是0x001),接着堆空间会开辟一块空间接收"abc"常量池地址引用值(0x001),而堆空间本身也是new的,所以也会有一个地址引用(这里是0x0001),最后堆把0x0001引用值赋给栈。此处会有两种情况:
    1.如果在常量池中存在"abc"的时候,常量池就不会开辟一块新的地址空间,所以这里就不会new对象,所以就只有堆中会new对象,所以此时只会创建1个对象
    2.如果在常量池中不存在"abc"的时候,常量池就会开辟一块新的地址空间,所以这里就会new对象,然后堆中也会new对象,所以此时只会创建2个对象
  • String s2 = “abc"的创建过程:String s2是直接赋值,所以不会在堆里面有什么操作,所以先直接在常量池中找有没有"abc”,没有"abc"的话直接在常量池中开辟一块空间将"abc"存放起来,并产生地址引用值0x001,接着直接把0x001复制给了s2。此处会有两种情况:
    1.如果在常量池中存在"abc"的时候,常量池就不会开辟一块新的地址空间,所以这里就不会new对象,所以此时不会创建对象
    2.如果在常量池中不存在"abc"的时候,常量池就会开辟一块新的地址空间,所以这里就会new对象,所以此时会创建1个对象

7、Java中的String类为什么用final修饰?

 1.为了实现字符串池
 2.为了线程安全
 3.为了实现String可以创建HashCode不可变性
  • 因为String类的不可变性,才能使得JVM可以实现字符串常量池;字符串常量池可以在程序运行时节约很多内存空间,因为不同的字符串变量指向相同的字符串常量时,都是指向字符串常量池中的同一个对象。这样一方面能够节约内存,另一方面也提升了性能。

  • 因为字符串是不可变的,所以是多线程安全的,同一个字符串实例可以被多个线程共享。这样便不用因为线程安全问题而使用同步。字符串自己便是线程安全的。

  • 因为字符串是不可变的,所以在它创建的时候HashCode就被缓存了,不需要重新计算。这就使得字符串很适合作为Map中的 键,字符串的处理速度要快过其它的键对象。这就是HashMap中的键往往都使用字符串。

  • 如果字符串是可变的,那么会引起很严重的安全问题。譬如,数据库的用户名、密码都是以字符串的形式传入来获得数据库的连接,或者在socket编程中,主机名和端口都是以字符串的形式传入。因为字符串是不可变的,所以它的值是不可改变的,否则黑客们可以钻到空子,改变字符串指向的对象的值,造成安全漏洞。

8、Java中基本类型和包装类的各类比较(==),以及包装类的对象缓存池

对于“=="操作符来说,如果比较的数据是基本类型,则比较它们的值,如果比较的是引用类型的对象,则会比较对象的内存地址。另外,如果一个是基本类型、一个是包装类型,在比较前会先把包装类型拆箱(“intValue()")成基本类型,自动装箱(“valueOf()")。
“Integer a = n"这句话背后编译器实际上调用了valueOf()方法进行了自动装箱,由于常量池中的缓存,基本类型的包装类值在[-128,127] 期间,jdk会将包装类对象做一个cache,可以用 “==”进行值比较(Double、Float是没有缓存的),超出了这个范围,就会new一个新的对象,所以在用“=="比较,肯定是不同的对象地址,返回false;不在该范围内的需要使用equals进行值比较;(对于包装类,无论何时强烈推荐都使用equals进行值比较)

Integer i1=10;
Integer i2=10;

Integer i3=200;
Integer i4=200;
	i1==i2:true
	i3==i4:false

9、反射和动态代理

参考:反射和动态代理
“反射(Reflection)"是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序获取自身的信息,并且可以操作类或对象的内部属性。通过反射机制,可以在运行时访问 Java 对象的属性,方法,构造方法等。
反射的缺点:

  • 性能开销:由于反射涉及动态解析的类型,因此无法执行某些 Java 虚拟机优化。因此反射操作的性能要比非反射操作的性能要差,应该在性能敏感的应用程序中频繁调用的代码段中避免。
  • 破坏封装性: -反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。
  • 内部曝光 :由于反射允许代码执行在非反射代码中非法的操作,例如访问私有字段和方法,这可能会导致代码功能失常并可能破坏可移植性。反射代码打破了抽象,因此可能会随着平台的升级而改变行为。

类加载的完整过程如下:

  1. 在编译时,Java 编译器编译好 .java 文件之后,在磁盘中产生 .class 文件。.class 文件是二进制字节码文件。
  2. JVM 中的类加载器读取字节码文件,取出二进制数据,加载到内存中,解析.class文件内的信息。类加载器会根据类的全限定名来获取此类的二进制字节流;然后,将字节流所代表的静态存储结构转化为方法区的运行时数据结构;接着,在内存中生成代表这个类的“java.lang.Class 对象"
  3. 加载结束后,JVM 开始进行连接阶段(包含验证、准备、初始化)。经过这一系列操作,类的变量会被初始化。

要想使用反射,首先需要获得待操作的类所对应的 Class 对象。Java 中,无论生成某个类的多少个对象,这些对象都会对应于同一个 Class 对象。这个 Class 对象是由 JVM 生成的,通过它能够获悉整个类的结构。所以,java.lang.Class 可以视为所有反射 API 的入口点。
“反射的本质就是:在运行时,把 Java 类中的各种成分映射成一个个的 Java 对象。"

“静态代理"

“意义:"真实对象专注于做自己的事情,代理可以对其拓展一些公共业务,也方便集中管理这些公共业务。
缺点:大量使用这种静态代理,会使我们系统内的类的规模增大,并且不易维护;并且由于 Proxy 和 A 的功能本质上是相同的,Proxy 只是起到了中介的作用,这种代理在系统中的存在,导致系统结构比较臃肿和松散。

“动态代理":在运行状态中,需要代理的地方,根据A和 I,动态地创建一个 P,用完之后,就会销毁,这样就可以避免了 Proxy 角色的 class 在系统中冗杂的问题了。
动态代理是Java 反射的一种使用场景,只要有一个接口,就能在运行时动态生成类型安全的字节码文件,其目的就是为真实对象提供一个代理对象以控制对真实对象的访问。为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值