1月29日 说说你对static关键字的理解
如果与其它技术人员交流关于static关键字的理解的话,思路要清晰,从字段到类,再说说static的功能性作用。
(1)static 修饰字段
static修饰的字段是属于类的,这个类的所有对象均共享此一个字段的值。可以直接使用类名调用。
(2)static修饰方法
static修饰的方法也是可以直接使用类名调用。一般在单例模式中,可以将构造函数私有化,然后将方法直接设置为静态的,那么调用的时候,就不用需要new这个类的对象。
(3)static修饰内部类
static修饰的内部类我见过的情况就是单例模式,由某个单例类的内部嵌套类来生成单例对象。
(4)static静态代码块
静态代码块一般用于执行一些初始化的动作。
(5)静态导包
static关键字还可以用于静态导包。使用 import static 包名.方法名;
也能够在其它类中导入静态常量。
其它:
其实main函数也是一种特殊的静态函数,加载某个类由触发这个类的静态函数触发。
1月30日 交流一下final关键字的用途
(1)修饰变量。被final修饰的变量不能被修改。
(2)修饰方法。被final修饰的方法不能被子类修改。一般在模版设计模式中,父类可以将模版方法使用final修饰。再提供一些抽象方法给子类实现。
(3)修饰类。被final修饰的类不能被继承。
(4)还有一些特殊的用法,比如某个字段被final修饰,那么必须在编译阶段就指明它的值。可以直接为final修饰的成员变量设置初始值,也可以为构造函数中为final修饰的变量设置初始值。
(5)特殊用法就是,在并发编程中。因为JVM可能会出现重排序优化,在构造函数执行结束的时候, final域的变量一定会被初始化,而普通变量可能没有被初始化。
(6)还有就是,一般为匿名内部类提供参数,这个参数需要使用final修饰。因为匿名类的生命周期有可能超过局部变量的声明周期,所以使用final来延长这个局部变量的生命周期。
2月11日 说说对关键字protected与default的作用范围理解
如果某个字段或者方法被protected修饰,可以在本包中创建这个对象,可以调用这个对象的这个protected修饰字段或者方法。如果在另外的包中创建此对象,那么调用对象的protected修饰的字段或者方法是不行的。
另外一个就是在子类中可以直接访问父类的protected修饰的方法或者字段。
权限修饰符,那么这就是包访问权限。在同一个包中,可以创建直接调用某对象的protected字段或者方法。
2月11日 交流一下序列化 Serializable [ sɪərɪrlaɪ'zəbl C瑞来紫波 ]相关的东西,transient[ˈtrænziənt trainZi 恩特]。
[ 什么是序列化,序列化版本号,transient关键字,与static关键字,属性是对象 ]
对象序列化就是把对象转换成字节数组,便于进行网络传输。反序列化也就是把字节数组重新恢复成对象。
实现序列化接口Serializable接口会产生一个序列化版本ID。JVM会先把从网络环境中获取到的字节数组恢复成一个对象,然后把这个对象的序列化版本ID与本地实体对象中的序列化版本ID比较。如果比较一致,则可以成功的反序列化,将熟悉赋值到本地的实体对对象中。反之,则反序列化失败。
用transient修饰的成员变量的值不会被反序列化。
静态区中。
如果某个成员变量也是一个对象,那么这个成员变量也要实现序列化接口。
2月12日 谈谈对volatile关键字的理解
volatile关键字的用途就是让变量在多个线程间可见,强制线程从公共堆栈中取得变量的值。
volatile关键字还有一个作用就是禁止指令重排序优化。
缺点就是但是它不保证原子性。
volatile关键字原理:
(1)将最先的变量的值写入到主内存中
(2)使其它线程中缓存了该变量的内存地址失效
JMM内存模型要求会画。线程 《===》 线程私有内存(共享变量副本) 《===》主存
此处牵扯到单例模式的双重检验锁的懒汉式。
下面重排序过程务必能够手写出来。
instance = new Singleton(); 这一行代码可以拆分成三句。
memory = allocate(); //1.分配对象的内存空间
initInstance(memory); //2.初始化对象
instance = memroy; //3.设置instance引用指向这块内存
上面的代码中,2和3可能会被重排序。
另外一个并发线程B可能拿到的是没有被A线程初始化的对象。
解决办法:禁止指令重排序。所以使用 volatile 关键字修饰instance对象
2月13日 谈谈对Java异常体系的理解
常见的RuntimeException :
NullPointerException ArrayIndexOutOfBoundExceptions ClassCastException NumberFormatException IllegalArgumentException
空指角标内裤转换
数字解析非法参数
找不到类解析打断SQL
SQLException ParseException InterruptException ClassNotFoundException
2月14日 谈谈对synchronized关键字的理解
一般如果交流这些略有复杂性的关键字的时候,可以先从使用角度(作用)来阐述,然后是阐述原理。
synchronized有两个使用场景。synchronized关键字的基本用法就是修饰方法。修饰的方法就是一个同步方法。同一时刻,只能有一个线程访问这个方法,同步方法的同步锁是当前对象的字节码文件。另外一个使用场景就是修饰同步代码块,同一时刻使用有一个线程进入这个临界区,同步锁是任意指定一个对象。
监视器进入指令插入到同步代码的开始位置,将监视器退出指令加入到同步代码的结束或者异常位置。
synchroized可重入性:当一个线程在得到对象锁,再次请求该对象锁是可以的。
synchronized在内存中的作用。它能保证变量的可见性与排他性。
可见性分析:在释放锁的时候,将线程私有变量刷新到主存。获取锁的时候,将私有内存中变量的值废弃,重新从主存中获取最新变量的值。排他性分析:在进入同步代码之前,使用监视器进入指令。在方法退出或者异常出,使用监视器退出指令。保证临界区内中有一个线程。
2月18日 面向对象的基本特征是什么?
JAVA是一门面向对象的语言,它的基本特征是封装、继承、多态。
封装也就是把客观事物封装成抽象的类,并且隐藏类的实现细节,仅仅对外提供公共的访问方式。生活中封装也处处都能体现出来,就像开车一样,只需要学习如何操作防线盘,刹车,油门,不需要知道汽车的内部构造,这样的话,可以降低我们学习的难度。编程也一样。
继承。子类是从父类派生出来的类,子类拥有父类所有的属性与方法。并且子类还能够扩展。继承的好处之一就是增加代码的复用性。就像男人继承了人,也就有了人的一切性质。
多态。我的理解就是同一种事物有不同的形态。比如我们的老师,他在生活中可能扮演着父亲一样的角色,在学习中扮演着老师的角色。从代码方面来实现的话,那么将老师这个人视为一个对象,他就实现了父亲接口与教学接口。在不同环境下,是扮演的角色不一样。
2月18日 聊聊多态
多态的前提:类与类之间要有继承或者实现的关系。父类引用指向子类对象,子类重写父类的方法。
多态的原理:动态绑定。绑定,在Thinking in Java 这本书里面的解释是:对象引用找到正确的方法体的过程。动态绑定也就是指:程序在运行阶段,动态的判断对象引用所指向的实际类型。这个时候,或许对象引用是一个父类型,但是执行的确是子类的方法体。
多态的优势:对象复用性与灵活性。比如我们在某个方法上定义一个接口类型的参数,那么传递进来的实际对象可以是任何一个实现类。所以说,这么方法不用担心实际传递对象的问题,这体现了这个方法的服用性与灵活性。
2月18日 自动装箱与自动拆箱
自动装箱与自动拆箱。自动装箱是把基本数据类型转换为包装类。自动拆箱是把包装类对象转为基本数据类型。
Integer a = 10 ; 相当于调用了 Integer a = new Integer(10);
Integer b = 10 ; sout(a == b) 的结果是 true。 因为包装类常量 在 -128 到 127 之间。
sout( a == 10) ,自动拆箱,所以结果是true。
2月18日 String、StringBuilder、StringBuffer
String是字符串,它是一种不可变的对象。任何对String变量的修改实则是重新创建了一个新的String对象。
StringBuffer是一个可变的字符序列,它线程安全。
StringBuilder也是一个可变的字符序列,但是它线程不安全。
一般来说,从执行效率上来看,StringBuilder的效率最高。因为它用于单线程环境并且是一个可变对象,操作的是StringBuilder对象本身,并没有创建新的对象。StringBuffer的效率其次,它的也是可变对象,但是因为多线程的原因,需要判断同步锁,所以效率略低与StringBuilder。性能相对最差的是字符串,它是不可变对象,在操作字符串的过程中,有可能会产生很多新的对象,所以性能相对最差。
2月18日 字符串存放的位置
String是存放在字符串常量池,在编译期已经被确定了。
如果是通过构造函数创建的字符串,那么会存放在堆内存当中。
对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象
INTERN方法
直接使用双引号声明出来的String对象会直接存储在常量池中。
如果是用new 声明的 String对象,可以使用String提供的intern方法。intern 方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中,并返回池中的引用。
字符串倒序代码手写
字符串设计为不可变类的优势
线程安全
因为字符串是不可变的,所以是多线程安全的,同一个字符串实例可以被多个线程共享。这样便不用因为线程安全问题而使用同步。字符串自己便是线程安全的。
加快字符串处理速度
因为字符串是不可变的,所以在它创建的时候hashcode就被缓存了,不需要重新计算。这就使得字符串很适合作为Map中的键,字符串的处理速度要快过其它的键对象。这就是HashMap中的键往往都使用字符串。
2月20日 为什么要重写hashCode与equals方法
首先要明白的是hashCode与equals方法的基本作用。
hashCode返回的是对象的内存地址,equals方法比较的是两个对象是否相等,在Object类中默认是比较的两个对象的内存地址。它们共同用于比较两个对象是否相等。
我个人理解为什么要重写hashCode与equals方法。为了提升程序的效率才重写hashCode与equals方法。在比较对象的时候,先比较hashCode,如果hash值不一样,那么没必要在进行比较了。如果hash值一样,那么再比较equals方法。这样的好处就是可以大量减少比较equals方法的次数。常见的要重写hashCode与equals方法的例子就是在集合框架中。
在集合框架中,Set集合是无序的,不可以重复的。为了保证元素的唯一性,新插入一个元素的时候,先计算它的hash值,然后通过这个哈希值定义到一个地址上。然后看看这个地址上有没有元素。如果没有的话,那么就直接插入。如果有的话,那么再比较equals方法。这样设计的好处就是可以减少equals方法的比较次数,从而提升Set集合的插入效率。
提升比较两个对象是否相同的效率。
2月20日 JVM的内存分布
堆内存、JAVA栈内存、方法区、本地方法栈、程序计数器。
堆内存使用NEW关键字创建的对象,都保存在堆内存中。这块内存不需要我们关心,会由垃圾回收器进行控制。堆内存被所有线程共享,并且在JVM中只有一个。另外,数组也存放在堆内存中。
Java栈用于保存方法的临时变量,对象引用。
方法区。用于存储已被虚拟机加载的类信息、常量、静态变量等数据。在方法区中有一个非常重要的部分就是常量池,比如字符串常量池。
本地方法栈。 本地方法栈则是为虚拟机使用到的Native方法服务。而JAVA的栈内存是为了保存我们编写的临时变量与对象引用。
程序计数器。JAVA其实说到底也就是一条一条的指令。程序计数器个人理解也就是用来指示执行哪条指令的。每个线程都需要有自己独立的程序计数器,并且不能互相被干扰,否则就会影响到程序的正常执行次序。因此,可以这么说,程序计数器是每个线程所私有的。
2月26日 谈谈对ThreadLocal这个对象的理解
定义:ThreadLocal可以视为线程局部变量。
结构:一个Thread对象中持有一个的ThreadLocalMap。键为具体的ThreadLocal对象,值使用的是嵌套类Entry对象进行封装。
Thread = ThreadLocalMap = Entry[] , Entry = ThreadLocal : value。
作用:在线程的任务代码块中。调用ThreadLocal的set方法设值,调用ThreadLocal对象的get方法取值。其实内部使用的是嵌套类Entry对象进行封装键值对。键为ThreadLocal,值为我们存放的值。让后把这个Entry对象存放到当前线程的ThreadLocalMap中。
因为Entry的key是一个弱引用的ThreadLocal对象,所以在 垃圾回收 之前,将会清除此Entry对象的key。那么, ThreadLocalMap 中就会出现 key 为 null 的 Entry,就没有办法访问这些 key 为 null 的 Entry 的 value。所以value所占内存不会被释放。若在指定的线程任务里面,调用ThreadLocal对象的get()、set()、remove()方法,可以避免出现内存泄露。
内存泄露:为什么要将ThreadLocal设计为弱引用?
答:因为弱引用的对象的生命周期直到下一次垃圾回收之前被回收。弱引用的对象将会被置为null。我们可以通过判断弱引用对象是否已经为null,来进行相关的操作。在ThreadLocalMap中,如果键ThreadLocal已经被回收,说明ThreadLocal对象已经为null,所以其对应的值已经无法被访问到。这个时候,需要及时手动编写代码清理掉这个键值对,防止内存泄露导致的内存溢出。
补充:ThreadLocal 的经典使用场景是数据库连接和 session 管理等。
ThreadLocal与Synchronized的区别
synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。
一句话,ThreadLocal用于数据隔离,而Synchronized用于数据共享。