Java面试题3.1

本文详细介绍了Java面试中常见的核心知识点,包括==与equals()、hashCode()的区别,int与Integer的区别,多态的理解,String、StringBuffer、StringBuilder的对比,内部类与抽象类、接口的异同,泛型中的extends和super的区别,以及进程和线程、锁的概念,反射获取类对象的方法,HTTP与HTTPS的区别,AOP和IoC的应用,Spring Bean的线程安全性,HashMap的工作原理和优化,以及CMS垃圾回收机制和线程间的同步策略。内容详实,涵盖了Java编程的多个重要方面。
摘要由CSDN通过智能技术生成

1、java中==和equals和hashCode的区别
     1、equals():用来检测两个对象是否相等,即两个对象的内容是否相等。
     2、基本数据类型的==比较的值相等.
          引用类型==比较的是地址,
     3、hashCode也是Object类的一个方法。返回一个离散的int型整数。在集合类操作中使用,为了提高查询速度。(HashMap,HashSet等比较是否为同一个)
          如果两个对象equals,Java运行时环境会认为他们的hashcode一定相等。
          如果两个对象不equals,他们的hashcode有可能相等。
          如果两个对象hashcode相等,他们不一定equals。
          如果两个对象hashcode不相等,他们一定不equals。
=========================================================================
2、int与integer的区别
     1.Integer是int的包装类,int则是java的一种基本的数据类型;
     2.Integer变量必须实例化之后才能使用,而int变量不需要实例化;
     3.Integer实际是对象的引用,当new一个Integer时,实际上生成一个指针指向对象,而int则直接存储数值
     4.Integer的默认值是null,而int的默认值是0。
 而超出-128〜127的范围,进行==比较时是进行地址及数值比较。
=========================================================================
3、谈谈对java 多态的理解
概念
    同一个行为具有多个不同表现形式或形态的能力,指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。
理解
    多态是同一个行为具有多个不同表现形式或形态的能力。
    多态就是同一个接口,使用不同的实例而执行不同操作。
条件
    继承(在多态中必须存在有继承关系的子类和父类。)
    重写(子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。)
    父类引用指向子类对象(检查父类中是否有该方法,没有则编译错误;有则调用子类的同名方法)
=========================================================================
4、String、StringBuffer、StringBuilder区别
String是一个长度不可变的字符序列,底层是一个被final修饰的char[]数组,所以说,任何对 String 类型进行改变的操作实际上都是重新生成了一个新的String对象,
然后将指针指向新的 String 对象,这样不仅效率低下,而且大量浪费有限的内存空间,所以经常改变内容的字符串最好不要用 String 。
StringBuilder类 和 StringBuffer类:
StringBuilder: 可变的字符序列 , 线程不安全的,效率高,底层是 char[]数组 存储
StringBuffer : 可变的字符序列 , 线程安全的,效率低 ,底层是 char[]数组 存储
区别一:线程安全:
       StringBuffer:线程安全,StringBuilder:线程不安全。因为 StringBuffer 的所有公开方法都是 synchronized 修饰的,而 StringBuilder 并没有 synchronized 修饰。
区别二:运行速度:
       因为StringBulider是线程不安全的,所有公开方法都是没有加锁的,但是StringBuffer是线程安全的,有公开方法都是同步的,所以毫无疑问,
StringBuilder的速度要大于StringBuffer。写一个简单的代码测试一下,都循环100000次添加字符串"abc",然后删除添加的字符串。可以发现StringBuffer消耗的时间近乎StringBuilder的1.5倍。
String:适用于少量的字符串操作的情况
StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况
=========================================================================
5、什么是内部类?内部类的作用
将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。
成员内部类
成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)。
当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员。
成员内部类可以看成是外部类的一个成员,在成员内部类中无法声明静态成员,但static final(两个一起使用)字段是个例外。
我们知道加载类时,会先初始化静态成员,如果成员内部类有静态成员,那么内部类就会在外部类之前生成,而内部类是为外部类服务的,内部类在外部类之前就生成可能会脱离掌控。
在实例化成员内部类时,成员内部类会持有一个外部类当前对象的引用,这样在成员内部类中就可以直接访问外部类的成员,即使是private修饰的。
编译后Outer.Inner被重命名为Outer$Inner。
局部内部类
    局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。
匿名内部类
    匿名内部类就是没有名字的内部类
    匿名内部类不能有构造器,匿名内部类没有类名,肯定无法声明构造器。
    匿名内部类必须继承或实现一个接口,指定给new的类型为匿名类的超类型,匿名类不能有显示的extends或implements子句,也不能有任何修饰符。
    匿名内部类和成员内部类、局部内部类一样,也不能声明静态成员。
静态内部类
       静态内部类,有的书上也称为嵌套类,声明它时需要用static修饰符,静态内部类不同于前三种内部类,静态内部类不会持有外部类当前对象的引用,
    所以在静态内部类中无法访问外部类的非静态成员,可以这么说,静态内部类不依赖于外部类。
       指被声明为static的内部类,他可以不依赖内部类而实例,而通常的内部类需要实例化外部类,从而实例化。静态内部类不可以有与外部类有相同的类名。
    不能访问外部类的普通成员变量,但是可以访问静态成员变量和静态方法(包括私有类型)一个 静态内部类去掉static 就是成员内部类,
    他可以自由的引用外部类的属性和方法,无论是静态还是非静态。但是不可以有静态属性和方法。
=========================================================================
6、抽象类和接口区别
    1、抽象类和接口都不能直接实例化。如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
    2、抽象类要被子类继承,接口要被类实现。
    3、接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现。
    4、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
    5、抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,实现接口的时候,
         如不能全部实现接口方法,那么该类也只能为抽象类。
    6、抽象方法只能申明,不能实现。
    7、抽象类里可以没有抽象方法
    8、如果—个类里有抽象方法,那么这个类只能是抽象类
    9、抽象方法要被实现,所以不能是静态的,也不能是私有的。
    10、接口可以继承接口,并且可多继承接口,但类只能单—继承。
    11.接口可以通过匿名内部类实例化。接口是对动作的抽象,抽象类是对根源的抽象。抽象类表示的是,这个对象是什么。而接口表示的是,这个对象能做什么。

7、接口的意义和用途
    1.含义
       Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,
       而这些实现可以具有不同的行为(功能)。
    2.接口的特点
       接口中的每个方法都是隐式抽象的,接口中的方法会被隐式的指定为public abstract
       接口中可以含有变量,但是接口中的变量会被隐式的指定为public static final变量
       接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法
=========================================================================
8、泛型中extends和super的区别
泛型的定义
      泛型的定义:泛型是JDK 1.5的一项新特性,它的本质是参数化类型(Parameterized Type)的应用,也就是说所操作的数据类型被指定为一个参数,
      在用到的时候在指定具体的类型。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口和泛型方法。
泛型通配符extends与super的区别
      <? extends T>限定参数类型的上界:参数类型必须是T或T的子类型
      <? super T> 限定参数类型的下界:参数类型必须是T或T的超类型
总结为:
      <? extends T> 只能用于方法返回,告诉编译器此返参的类型的最小继承边界为T,T和T的父类都能接收,但是入参类型无法确定,只能接受null的传入
      <? super T>只能用于限定方法入参,告诉编译器入参只能是T或其子类型,而返参只能用Object类接收
      <? extends Fruit>代表的是上界通配符,也就是说这个List中存放的对象都是Fruit以及其子类的对象,这样我们就不用因为输入的List中类型的不同而改变代码了。
    上界通配符有一个特点,就是程序只知道List<? extends Fruit>中的对象是Fruit的子类的对象,但是如果Fruit的子类有很多个,那个在使用add方法的时候,
    就可能出现本来是List,然后在其中添加了banana对象,从而失败。
      super与extends是完全相反的,其定义的是下界通配符。 List<? super Fruit>也就是说List中存放的都是Fruit和它的父类的对象,比如food,Object。
    而且如果要在这个List中取出数据,那就不能够确定具体是Fruit的哪个父类的对象,可能是Food,可能是Object。为了保证一定能够取出来,
    就必须把其转型成Object对象,但是这个时候就会失去原有对象的类型信息。所以List<? super Fruit>不能够提取数据。
=========================================================================
9、父类的静态方法能否被子类重写?静态属性和静态方法是否可以被继承
      首先静态属性和静态方法是可以被子类继承的,但静态属性不能被子类重写。
      重写的本质是动态绑定,即根据运行时对象的类型来决定调用哪个方法,而不是根据编译时的类型。静态方法属于类的,在编译阶段已经静态绑定到类上,
   表现出来就是通过类名.方法名进行访问;所以静态方法无法被子类重写。
      子类重写父类静态方法时,加注解@Override则会进行检查,不通过IDE的校验;表明静态方法不能被子类重写。另外不加注解@Override,
   通过引用son动态绑定method1(),得到的结果也出人意料,再次表明静态方法不能被子类重写。
=========================================================================
10、进程和线程的区别
    1.根本区别:进程是操作系统进行资源分配的最小单元,线程是操作系统进行运算调度的最小单元。
    2.从属关系不同:进程中包含了线程,线程属于进程。
    3.开销不同:进程的创建、销毁和切换的开销都远大于线程。
    4.拥有资源不同:每个进程有自己的内存和资源,一个进程中的线程会共享这些内存和资源。
    5.控制和影响能力不同:子进程无法影响父进程,而子线程可以影响父线程,如果主线程发生异常会影响其所在进程和子线程。
    6.CPU利用率不同:进程的CPU利用率较低,因为上下文切换开销较大,而线程的CPU的利用率较高,上下文的切换速度快。
    7.操纵者不同:进程的操纵者一般是操作系统,线程的操纵者一般是编程人员。

=========================================================================

11、final,finally,finalize的区别
   final:java中的关键字,修饰符。
      1.如果一个类被声明为final,就意味着它不能再派生出新的子类,不能作为父类被继承。因此,一个类不能同时被声明为absrtact抽象类的和final的类。
      2.如果将变量或者方法声明为final,可以保证它们在使用中不被改变.
    2.1 被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。 
    2.2被声明final的方法只能使用,不能重载。
   finally:java的一种异常处理机制。
       finally是对Java 异常处理模型的最佳补充。finally 结构使代码总会执行,而不管有无异常发生。使用 finally 可以维护对象的内部状态,并可以清理非内存资源。
       特别是在关闭数据库连接这方面,如果程序员把数据库连接的close()方法放到finally中,就会大大降低程序出错的几率。
   finalize:Java中的一个方法名。
       Java技术使用finalize()方法在垃圾收集器将对象从内存中清除出去前,做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。
       它是在Object类中定义的,因此所有的类都继承了它。子类覆盖finalize()方法以整理系统资源或者执行其他清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。
=========================================================================
12、锁可以锁在哪里
    Java 为程序加锁的方式主要有两种:synchronized 与 Lock。,
    1. synchronized 可以修饰的作用域如下:
    -非静态方法(加的锁为对象锁);
    -静态方法(加的锁为类锁);
    -代码块(对象锁与类锁均可);
    2. Lock 采用 lock()对代码加锁,unlock()进行解锁
    Java中synchronized同步锁用法及作用范围
    java中的synchronize关键字可以在多线程环境下用来作为线程安全的同步锁,本文主要对synchronize的作用,已经其有效范围进行讨论。
    Java中的对象锁和类锁:java的对象锁和类锁在概念上基本上和内置锁是一致的,但是,俩个锁实际是有很大的区别的,对象锁是用于对象的实例方法,
    或者一个对象实例上的,类锁是用于类的静态方法或者一个类的class对象上的,我们知道,累的对象实例可以有很多个,但是每个类只有一个class对象,
    所以不同对象实例的对象锁是互补干扰的,但是每个类只有一个类锁,但是有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真是存在的,
    它只是用来帮助我们理解锁定实例方法和静态方法的区别。
=========================================================================
13、怎么利用反射获取类中的对象
Java 反射机制是在运行状态中,对于任意一个类,都能够获得这个类的所有属性和方法,对于任意一个对象都能够调用它的任意一个属性和方法。
这种在运行时动态的获取信息以及动态调用对象的方法的功能称为 Java 的反射机制。
    先建立一个Student类
    1.通过class属性直接获取:类名.class
    2.通过getClass()方法获取:对象名.getClass();     
    3.通过Class.forName("类名");
=========================================================================
14、HTTP和https区别
     HTTP协议以明文方式发送内容,不提供任何方式的数据加密。HTTP协议不适合传输一些敏感信息,比如:信用卡号、密码等支付信息。
https则是具有安全性的ssl加密传输协议。http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
并且https协议需要到ca申请证书。HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。
     HTTPS协议的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种就是确认网站的真实性。
HTTPS在HTTP的基础上加入了SSL协议,SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密
=========================================================================
15、说说AOP和IoC的应用
     IOC: 控制反转,是一种设计模式.一层含义是控制权的转移:由传统的在程序中控制依赖转移到由容器来控制:
第二层是依赖注入:将相互依赖的对象分离,在spring配置文件中描述他们的依赖关系.他们的依赖关系只在使用的时候才建立.简单来说就是不需要NEW一个对象了.
     AOP: 面向切面,是一种编程思想,OOP的延续.将系统中非核心的业务提取出来,进行单独处理.比如事务.日志和安全等.
这个简单来说就是可以在一段程序之前或者之后做一些事. Spring 的AOP和IOC都是为了解决系统代码耦合度过高的问题.
=========================================================================
16、Spring中bean是线程安全的吗
       Spring容器中的Bean是否线程安全,容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,
     但是具体还是要结合具体scope的Bean去研究。
       Spring 的 bean 作用域(scope)类型
          1、singleton:单例,默认作用域。
          2、prototype:原型,每次创建一个新对象。
          3、request:请求,每次Http请求创建一个新对象,适用于WebApplicationContext环境下。
          4、session:会话,同一个会话共享一个实例,不同会话使用不用的实例。
          5、global-session:全局会话,所有会话共享一个实例。
       线程安全这个问题,要从单例与原型Bean分别进行说明。
       原型Bean
     对于原型Bean,每次创建一个新对象,也就是线程之间并不存在Bean共享,自然是不会有线程安全的问题。
       单例Bean
     对于单例Bean,所有线程都共享一个单例实例Bean,因此是存在资源的竞争。
     如果单例Bean,是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例Bean是线程安全的。
     比如Spring mvc 的 Controller、Service、Dao等,这些Bean大多是无状态的,只关注于方法本身。
     对于有状态的bean,Spring官方提供的bean,一般提供了通过ThreadLocal去解决线程安全的方法,比如RequestContextHolder、
     TransactionSynchronizationManager、LocaleContextHolder等。
       使用ThreadLocal的好处
     使得多线程场景下,多个线程对这个单例Bean的成员变量并不存在资源的竞争,因为ThreadLocal为每个线程保存线程私有的数据。这是一种以空间换时间的方式。
     当然也可以通过加锁的方法来解决线程安全,这种以时间换空间的场景在高并发场景下显然是不实际的。
=========================================================================
17、概述HashMap工作原理,HashMap1.7与1.8的区别,说明1.8做了哪些优化,如何优化的
      HashMap是我们开发中经常使用到的集合,jdk1.8相对于1.7底层实现发生了一些改变。1.8主要优化减少了Hash冲突 ,提高哈希表的存、取效率。
 1.  底层数据结构不一样,1.7是数组+链表,1.8则是数组+链表+红黑树结构(当链表长度大于8,转为红黑树)。
 2.  JDK1.8中resize()方法在表为空时,创建表;在表不为空时,扩容;而JDK1.7中resize()方法负责扩容,inflateTable()负责创建表。
 3.  1.8中没有区分键为null的情况,而1.7版本中对于键为null的情况调用putForNullKey()方法。但是两个版本中如果键为null,那么调用hash()方法得到的都将是0,所以键为null的元素都始终位于哈希表table【0】中。
 4.  当1.8中的桶中元素处于链表的情况,遍历的同时最后如果没有匹配的,直接将节点添加到链表尾部;而1.7在遍历的同时没有添加数据,而是另外调用了addEntry()方法,将节点添加到链表头部。
 5.  1.7中新增节点采用头插法,1.8中新增节点采用尾插法。这也是为什么1.8不容易出现环型链表的原因。
 6.  1.7中是通过更改hashSeed值修改节点的hash值从而达到rehash时的链表分散,而1.8中键的hash值不会改变,rehash时根据(hash&oldCap)==0将链表分散。
 7.  1.8rehash时保证原链表的顺序,而1.7中rehash时有可能改变链表的顺序(头插法导致)。
 8.  在扩容的时候:1.7在插入数据之前扩容,而1.8插入数据成功之后扩容。
=========================================================================
18、谈谈Currenthashmap的扩容机制
> 1.7 
>
> 1. 1.7版本的ConcurrentHashMap是基于Segment分段实现的
> 2. 每个Segment相对于⼀个⼩型的HashMap
> 3. 每个Segment内部会进⾏扩容,和HashMap的扩容逻辑类似
> 4. 先⽣成新的数组,然后转移元素到新数组中
> 5. 扩容的判断也是每个Segment内部单独判断的,判断是否超过阈值
>
> 1.8
>
> **扩容相关的属性:**
>
> - `nextTable`: 扩容期间,将元素迁移到 nextTable 新Map, nextTable是共享变量。
>
> - `sizeCtl`: 多线程之间,sizeCtl来判断ConcurrentHashMap当前所处的状态。
>
> - - 通过CAS设置sizeCtl属性,告知其他线程ConcurrentHashMap的状态变更
>
> - - `transferIndex`: 扩容索引,表示已经完成数据分配的table数组索引位置。
>
>   - 数据转移已经到了哪个位置? 其他线程根据这个值帮助扩容从这个索引位置继续转移数据
>
>   - `ForwardingNode`节点: 标记作用,表示此节点已经扩容完毕
>
>   - - 数组位置的数据已经被转移到新Map中,此位置就会被设置为这个属性
>     - 这个属性包装了新Map,可以用find方法取扩容转移后的值
=========================================================================
19、cms垃圾回收机制
   1.概念:
      CMS英文全称是“Concurrent Mark-Sweep”,是一款低延时的Java垃圾收集器。通常使用在对Java堆中老年代的对象收集中。
      CMS得优点是低延时,缺点是长时间运行的话会产生内存碎片,当碎片达到阀值,会引发一起Serial Old的Full GC.对应用造成严重的停顿、卡死现象。
   2、使⽤场景:GC过程短暂停,适合对时延要求较⾼的服务,⽤户线程不允许⻓时间的停顿。
   3、缺点:
       1、服务⻓时间运⾏,造成严重的内存碎⽚化。
       2、算法实现⽐较复杂(如果也算缺点的话)。
   4、实现机制:根据GC的触发机制分为:
       1、周期性Old GC(被动):2s执⾏⼀次;
       2、主动Old GC:触发条件:
          i. YGC过程发⽣Promotion Failed,进⽽对⽼年代进⾏回收
          ii. ⽐如执⾏了System.gc(),前提是没有参数ExplicitGCInvokesConcurrent
=========================================================================
20、线程a,b,c,d运⾏任务,怎么保证当a,b,c线程执⾏完再执d线程?
     1、使用join()方法,将线程B加⼊到线程A的尾部,当A执⾏完后B才执⾏
     2、CountDownLatch类:⼀个同步辅助类,常⽤于某个条件发⽣后才能执⾏后续进程。给定计数初始化CountDownLatch,调⽤countDown()方法,在计数到达零之前,
await⽅法⼀直受阻塞。
     3、notify、wait⽅法,Java中的唤醒与等待⽅法,关键为synchronized代码块,参数线程间应相同,也常⽤Object作为参数
=========================================================================

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值