Java实习面试:访问修饰符

  1. default:在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法
  2. private:在同一类内可见。使用对象:变量,方法
  3. public:对所有类可见。使用对象:类,接口,变量,方法
  4. protected:对同一包内的类和所有子类可见。使用对象:变量,方法

默认访问修饰符-不使用任何关键字

使用默认访问修饰符声明的变量和方法,对同一个包内的类是可见的。接口里的变量都隐式声明为

public static final

而接口里的方法默认情况下访问权限为

public
同包访问
不同包访问

私有访问修饰符-private

私有访问修饰符是最严格的访问权限,被声明为private的方法,变量和构造方法只能被所属类访问,并且类和接口不能声明为private。

声明为私有访问类型的变量只能通过类中公共的getter方法被外部类访问。private访问修饰符的使用主要用来隐藏类的实现细节和保护类的数据。

公有访问修饰符-public

被声明为public的类,方法,构造方法和接口能够被任何其他类访问。

如果几个相互访问的public类分布在不同的包中,则需要导入相应public类所在的包。由于类的继承性,类所有的公有方法和变量都能被其子类继承。

受保护的访问修饰符-protected

  • 子类与基类在同一包中:被声明为protected的变量,方法和构造器都能被同一个保重的任何其他类访问。
  • 子类与基类不在同一包中:那么在子类中,子类实例可以访问其从基类继承而来的protected方法,而其他类不能访问基类实例的protected方法。
不同包中无法访问
同包中可以访问

参考

Java实习面试:简单谈谈AOP

AOP与OOP

AOP面向切面编程,是OOP的补充和完善。

OOP引入封装,继承,多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。OOP只允许开发者定义纵向的关系,不适合定义横向的关系,如日志,事务,安全等。而AOP技术利用一种称为“横切”的技术,剖解开封装的对象内部,并将影响多个类的公共行为封装到一个可重用模块Aspect,即切面。

AOP核心概念

  1. 横切关注点:对哪些方法进行拦截,拦截后怎么处理
  2. 切面:类是对物体特征的抽象,切面就是对横切关注点的抽象
  3. 连接点:被拦截到的点,因为Spring只支持方法类型的连接点,所以Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器
  4. 切入点:对连接点进行拦截的定义
  5. 通知:所谓通知指的是拦截到连接点之后要执行的代码,通知分为:
    1. 前置
    2. 后置
    3. 异常
    4. 最终
    5. 环绕通知
  6. 目标对象:代理的目标对象
  7. 织入:将切面应用到目标对象并导致代理对象创建的过程
  8. 引入:在不修改代码的前提下,引入可以在运行期为类动态的添加一些方法或字段

切面

“切面“简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。使用横切技术,AOP把软件系统分为两个部分:核心关注点和横切关注点,业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。

Spring中的AOP

Spring中AOP代理由Spring的IOC容器负责生成,管理。它的依赖关系也由IOC容器负责,因此,AOP代理可以直接使用容器中的其他bean示例作为目标,这种关系可由IOC容器的依赖注入提供。

Spring创建代理的规则为:

  1. 默认使用JDK动态代理来创建AOP代理,这样可以为任何接口示例创建代理
  2. 当需要代理的类不是代理接口时,Spring会切换为使用CGLIB代理,也可以强制使用CGLIB代理

参考

Java实习面试笔记:线程池的参数

corePoolSize

线程池核心线程数量,核心线程不会被回收,即使没有任务执行,也会保持空闲状态。如果线程池中的线程少于此数,则在执行任务时创建。

线程池刚创建时,线程数量为0,当每次执行execute添加新的任务时,会在线程池创建一个新的线程,知道线程数量达到corePoolSize为止。

maximumPoolSize

池允许最大的线程数,当线程数量达到corePoolSize,且workQueue队列塞满任务之后,继续创建线程。

线程数量大于corePoolSize但不会超过maximumPoolSize,如果超过maximumPoolSize,就会抛出异常。

keepAliveTime

超过corePoolSize之后的“临时线程”的存活时间。

unit

keepAliveTime的单位

workQueue

当前线程数超过corePoolSize时,新的任务会处在等待状态,并存在workQueue中,BlockingQueue是一个先进先出的阻塞式队列实现。

ThreadFactory

创建线程的工厂类,通常我们会自定一个threadFactory设置线程的名称,这样我们就可以知道线程是由哪个工厂类创建的,可以快速定位。

handler

线程池执行拒绝策略,当线程数量达到maximumPoolSize大小,并且workQueue也已经塞满了任务的情况下,线程池会调用handler拒绝策略来处理请求。

系统默认的拒绝策略有以下集中:

  1. AbortPolicy:线程池默认的拒绝策略,该策略直接抛出异常
  2. DiscardPolicy:直接抛弃不处理
  3. DiscardOldestPolicy:丢弃队列中最老的任务
  4. CallerRunsPolicy:将任务分配给当前执行execute方法线程来处理。

还可以自定义拒绝策略,只需要实现RejectedExecutionHandler接口即可

线程池添加任务的整个流程

  1. 线程池刚刚创建时,线程数量为0
  2. 执行execute添加新的任务时会在线程池创建一个新的线程
  3. 当线程数量达到corePoolSize时,再添加新任务则会将任务放到workQueue队列
  4. 当队列已满放不下新的任务,再添加新任务则会继续创建新线程,但线程数量不超过maximumPoolSize
  5. 当线程数量达到maximumPoolSize时,再添加新任务则会抛出异常。

参考

Java面试:List, Set, Map异同

List

List元素以线性方式存储,可以存放重复对象

  • ArrayList:长度可变的数组,可以对元素进行随机的访问,向ArrayList中插入与删除元素的速度慢。JDK8中ArrayList扩容的实现是通过grow()方法实现的,该方法使用语句newCapacity = oldCapacity + (oldCapacity >> 1)计算容量,然后调用Arrays.copyof()方法对原数组进行复制。
  • LinkedList:采用链表数据结构,插入和删除速度快,但是访问速度慢。

Set

Set中的对象不按特定的方式排序,并且没有重复对象

  • HashSet:按照哈希算法来存储集合中的对象,存取速度比较快,当HashSet中的元素个数超过数组大小*loadFactor时,就会进行近似两倍扩容(newCapacity = (oldCapacity << 1) + 1)。
  • TreeSet:实现了SortedSet接口,能够对集合中的对象进行排序。

Map

Map是一种把键对象和值对象映射的集合,它的每一个元素都包含一个键对象和值对象。

  • HashMap:基于散列表实现,其插入和查询<K, V>的开销是固定的,可以通过构造器设置容量和负载因子来调整容器的性能。
  • LinkedHashMap:类似于HashMap,但是迭代遍历它时,取得<K, V>的顺序是其插入次序,或者是最近最少使用的次序。
  • TreeMap:基于红黑树实现。查看<K, V>时,它们会被排序。TreeMap是唯一的带有subMap()方法的Map,该方法可以返回一个子树。

HashMap的实现,原理,机制

底层实现

HashMap底层整体结构是一个数组,数组中的每个元素又是一个链表。每次添加一个对象时会产生一个链表对象(Object类型),Map中的每个Entry就是数组中的一个元素(Map.Entry就是一个键值对),它具有由当前元素指向下一个元素的饮用,这就构成了链表。

存储原理

当向HashMap中添加元素的时候,先根据HashCode计算Key的Hash值,得到数组下标,如果数组该位置已经存在其他元素,那么这个位置的元素将会以链表的形式存放,新假如的放在链表的头部,如果数组该位置元素不存在,那么就直接将该元素放到此数组中的该位置。

去重原理

不同的Key计算到数组下标相同的几率很小,新建一个键值对放入到HashMap的时候,首先会计算Key的数组下标,如果数组该位置已经存在其他元素,则比较两个Key,若相同则覆盖写入,若不同则形成链表。

读取原理

从HashMap中读取元素时,首先计算Key的HashCode,找到数组下标,然后在对应位置的链表中找到需要的元素。

扩容机制

当HashMap中的元素个数超过数组大小*loadFactor时,就会进行2倍扩容。

比较

List

  • 可以允许重复的对象
  • 可以插入多个null元素
  • 是一个有序容器,保持了每个元素的插入顺序,输出的顺序就是插入的顺序
  • 常用的实现类有ArrayList、LinkedList和Vector。ArrayList最为流行,它提供了使用索引的随意访问,而LinkedList则对于经常需要从List中添加或删除元素的场合更为合适

Set

  • 不允许重复对象
  • 无序容器,无法保证每个元素的存储顺序,TreeSet通过Comparator或者Comparable维护了一个排序顺序
  • 只允许一个null元素
  • Set接口最流行的几个实现类是HashSet、LinkedHashSet以及TreeSet。最流行的是基于HashMap实现的HashSet。TreeSet还实现了SortedSet接口,因此TreeSet是一个根据其compare()和compareTo()的定义进行排序的有序容器

Map

  • Map不是Collection的子接口或者实现类,Map是一个接口
  • Map的每个Entry都持有两个对象,也就是一个键一个值,Map可能会持有相同的值对象,但键对象必须是唯一的
  • TreeMap也通过Comparator或者Comparable维护了一个排序顺序
  • Map里可以拥有随意个null值,但最多只能有一个null键
  • Map接口最流行的几个实现类是HashMap、LinkedHashMap、HashTable和TreeMap
    • HashMap:非线程同步,允许null,增长:2的指数倍
    • HashTable:线程安全,不允许null,增长:n*2+1
    • TreeMap:按键排序(默认升序)
    • ConcurrentHashMap:线程同步
    • LinkedHashMap:链表形式

使用场景

  • 使用索引来对容器中的元素进行访问,List是最好的选择。ArrayList提供更快速的访问,而如果需要经常添加删除元素的,选择LinkedList。
  • 若需要容器中的元素能够按照它们插入的次序有序存储,选择List
  • 需要保证插入元素的唯一性,可以选择Set的实现类。
  • 需要键值对的形式存储数据,Map是你正确的选择。

参考

  1. Java中 List、Set、Map 之间的区别
  2. List、Set、Map的区别

Java实习面试笔记:String, StringBuilder, StringBuffer的异同

一、String

String是不可变类

String str = "Hello, ";
str = str + "world!";

上述代码需要三次开辟内存空间,对内存来说,是一个极大的浪费。

二、StringBuilder和StringBuffer

StringBuffer和StringBuilder则是为了应对经常性的字符串操作而产生的。

StringBuffer和StringBuilder的对象都能被多次修改,期间不产生新的未使用的对象。而StringBuilder和StringBuffer的区别在于StringBuilder不是线程安全的,在访问上StringBuilder相较于StringBuffer有速度上的优势。

StringBuffer有一定缓冲区容量,当字符串大小没有超过缓冲区容量时不会分配新的容量,当字符串大小超过容量时会自动增加容量。

三、主要的区别

  • String是不可变字符串,初始化可null
  • StringBuffer是可变字符串,效率低,线程安全,初始化可null
  • StringBuilder是可变字符串,效率高,线程不安全,初始化不能null

参考

图析:String,StringBuffer与StringBuilder的区别

如有侵权烦请联系,我会将内容删除!

Java中为什么需要空的构造函数

package com.haloasis.Abstract;

public class Circle extends Shape{

    private Integer r;

    public Circle(Integer r) {
        this.r = r;
    }

    public Circle() {

    }

    public Integer getR() {
        return r;
    }

    public void setR(Integer r) {
        this.r = r;
    }

    @Override
    public Double area() {
        return Math.PI * Math.pow(r, 2);
    }
}

这个类中有了带参的构造函数还有一个空的构造函数。

因为如果写了一个带参的构造函数,Java就会将该构造函数替换掉默认的空构造函数,使得以下代码报错。

Shape circle = new Circle();