java笔试+面试总结——java基础篇

时间:2023-02-16 12:29:58

1、给一段java程序写出程序的输出结果(考点:类的执行顺序,赋值顺序,继承)
执行顺序:
1. 父类静态块
2. 自身静态块
3. 父类块
4. 父类构造器
5. 自身块
6. 自身构造器
赋值顺序为(与执行顺序相关联):
1. 父类的静态变量赋值
2. 自身的静态变量赋值
3. 父类成员变量赋值
4. 父类块赋值
5. 父类构造器赋值
6. 自身成员变量赋值
7. 自身块赋值
8. 自身构造器赋值

2、Java运行过程
Java源文件(.java)——>Java编译器——>字节码文件(.class)——>类装载器——>字节码校检器—— >解释器——>操作系统(Windows、Linux等)整个文件Load到内存区,一系列动作之后形成操作系统认识的代码,操作系统找到 main方法开始实行。
局部变量:方法或是语句块内部定义的变量(local variable)
成员变量:方法外部、类内部定义的变量,也叫做全局变量(glbal valiable)

3、final/static关键字
final
 根据程序上下文环境,Java关键字final有“这是无法改变的”或者“终态的”含义,它可以修饰非抽象类、非抽象类成员方法和变量。你可能出于两种理解而需要阻止改变:设计或效率。
final类不能被继承,没有子类,final类中的方法默认是final的。
final方法不能被子类的方法覆盖,但可以被继承。
final成员变量表示常量,只能被赋值一次,赋值后值不再改变。
final不能用于修饰构造方法。
注意:父类的private成员方法是不能被子类方法覆盖的,因此private类型的方法默认是final类型的。
使用final方法的原因有二:
第一、把方法锁定,防止任何继承类修改它的意义和实现。
第二、高效。编译器在遇到调用final方法时候会转入内嵌机制,大大提高执行效率。
1、final类
 final类不能被继承,因此final类的成员方法没有机会被覆盖,默认都是final的。在设计类时候,如果这个类不需要有子类,类的实现细节不允许改变,并且确信这个类不会载被扩展,那么就设计为final类。
2、final方法
如果一个类不允许其子类覆盖某个方法,则可以把这个方法声明为final方法。
3、final变量(常量)
 用final修饰的成员变量表示常量,值一旦给定就无法改变!
 final修饰的变量有三种:静态变量、实例变量和局部变量,分别表示三种类型的常量。
 另外,final变量定义的时候,可以先声明,而不给初值,这中变量也称为final空白,无论什么情况,编译器都确保空白final在使用之前必须被初始化(构造方法里赋值)。但是,final空白在final的使用上提供了更大的灵活性,为此,一个类中的final数据成员就可以实现依对象而有所不同,却有保持其恒定不变的特征。
4、final参数
当函数参数为final类型时,你可以读取使用该参数,但是无法改变该参数的值。
static
http://zhangjunhd.blog.51cto.com/113473/20280/

4、java synchronized实现机制
原理:
synchronized,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。
所有对象都自动含有单一的锁,JVM负责跟踪对象被加锁的次数。
如果一个对象被解锁,其计数变为0。
在任务(线程)第一次给对象加锁的时候, 计数变为1。
每当这个相同的任务(线程)在此对象上获得锁时,计数会递增。
只有首先获得锁的任务(线程)才能继续获取该对象上的多个锁。
每当任务离开时,计数递减,当计数为0的时候,锁被完全释放。
synchronized就是基于这个原理,同时synchronized靠某个对象的单一锁技术的次数来判断是否被锁,所以无需(也不能)人工干预锁的获取和释放。
原理!!!!!!!!!! 如果结合方法调用时的栈和框架(如果对方法的调用过程不熟悉建议看看http://wupuyuan.iteye.com/blog/1157548
小结:
一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
五、以上规则对其它对象锁同样适用.
理解对象和锁:
打个比方:一个object就像一个大房子,大门永远打开。房子里有 很多房间(也就是方法)。
这些房间有上锁的(synchronized方法), 和不上锁之分(普通方法)。房门口放着一把钥匙(key),这把钥匙可以打开所有上锁的房间。
另外我把所有想调用该对象方法的线程比喻成想进入这房子某个 房间的人。所有的东西就这么多了,下面我们看看这些东西之间如何作用的。
在此我们先来明确一下我们的前提条件。该对象至少有一个synchronized方法,否则这个key还有啥意义。当然也就不会有我们的这个主题了。
一个人想进入某间上了锁的房间,他来到房子门口,看见钥匙在那儿(说明暂时还没有其他人要使用上锁的 房间)。于是他走上去拿到了钥匙,并且按照自己 的计划使用那些房间。注意一点,他每次使用完一次上锁的房间后会马上把钥匙还回去。即使他要连续使用两间上锁的房间,中间他也要把钥匙还回去,再取回来。因此,普通情况下钥匙的使用原则是:“随用随借,用完即还。”
这时其他人可以不受限制的使用那些不上锁的房间,一个人用一间可以,两个人用一间也可以,没限制。但是如果当某个人想要进入上锁的房间,他就要跑到大门口去看看了。有钥匙当然拿了就走,没有的话,就只能等了。要是很多人在等这把钥匙,等钥匙还回来以后,谁会优先得到钥匙?Not guaranteed。象前面例子里那个想连续使用两个上锁房间的家伙,他中间还钥匙的时候如果还有其他人在等钥匙,那么没有任何保证这家伙能再次拿到。

5、String和StringBuffer的区别
JAVA平台提供了两个类:String和StringBuffer,它们可以储存和操作字符串,即包含多个字符的字符数据。这个String类提供了数值不可改变的字符串。而这个StringBuffer类提供的字符串进行修改。当你知道字符数据要改变的时候你就可以使用StringBuffer。典型地,你可以使用StringBuffers来动态构造字符数据。另外,String实现了equals方法,new String(“abc”).equals(newString(“abc”)的结果为true,而StringBuffer没有实现equals方法,所以,new StringBuffer(“abc”).equals(newStringBuffer(“abc”)的结果为false。
接着要举一个具体的例子来说明,我们要把1到100的所有数字拼起来,组成一个串。

StringBuffer sbf = new StringBuffer(); 
for(int i=0;i<100;i++)
{
sbf.append(i);
}

上面的代码效率很高,因为只创建了一个StringBuffer对象,而下面的代码效率很低,因为创建了101个对象。

String str = new String(); 
for(int i=0;i<100;i++)
{
str = str + i;
}

6、java并发
并发解析:http://www.ibeifeng.com/tech-69782.html
a、根据题目场景写代码,考虑并发情况
1.数据库设计要合理,设计时考虑分库和同步问题;
2.尽量分析出时效性不强的操作,这些操作可以放在后台慢慢操作;
3.程序结构设计合理,对象之间的关系合理,尽量减少大对象的使用;
4.改善算法,这个不用多说了;
4.加强缓存机制(这里面考虑的东西很多的,文件的、内存的、数据库的等等,都要结合使用);
5.如果压力过大,可以采用分布式(WebService、Remoting、csla、WCF等等你都可以参考);
6.最重要的一点,需求一定要合理,一些蛮横的需求会让你永远提高不了系统性能。如果出现这样的需求,需要给出建议,适当弱化这些需求,让需求和性能达到平衡;
7.做一些适当的压力测试,不过说实话,我自己也是偶尔做做的;

b、一些解决并发问题技巧
几点需要注意:
1. 尽量使用缓存,包括用户缓存,信息缓存等,多花点内存来做缓存,可以大量减少与数据库的交互,提高性能。
2. 用jprofiler等工具找出性能瓶颈,减少额外的开销。
3. 优化数据库查询语句,减少直接使用hibernate等工具的直接生成语句(仅耗时较长的查询做优化)。
4. 优化数据库结构,多做索引,提高查询效率。
5. 统计的功能尽量做缓存,或按每天一统计或定时统计相关报表,避免需要时进行统计的功能。
6. 能使用静态页面的地方尽量使用,减少容器的解析(尽量将动态内容生成静态html来显示)。
7. 解决以上问题后,使用服务器集群来解决单台的瓶颈问题。
cas:http://blog.csdn.net/hsuxu/article/details/9467651

c、多线程的实现方式
1、继承Thread
2、实现Runnable

7、java数据结构(实现、原理、应用)
a、hashmap详细的实现过程。
①数据存储是基于数组和链表实现的,默认是构建一个初始容量为16,负载因子为0.75 的HashMap。
②添加数据(put)时,首先先根据key的hashCode计算hash值,根据hash值得到这个元素在数组中的位置(即下标),如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。
③和ArrayList一样,HashMap由于也使用了数组,也存在对数组进行扩容的情况。当HashMap中的元素个数超过数组大小*loadFactor时,就需要进行扩容。扩容需要新建一个数组,将所有数据重新计算Hash值之后拷贝到新的数组。HashMap数组扩容的操作代价很高,我们应该尽量减少这种操作。
④获取数据时,首先根据key的hashCode计算数组位置,然后从链表里进行查找。
⑤删除数据时,也需要先根据key的hashCode计算数组位置,然后从链表里进行查找,找到之后,需要修改链表节点之间的引用即可。
http://blog.csdn.net/guoweimelon/article/details/50802694

b、Vector,ArrayList, LinkedList的区别是什么?
1、Vector、ArrayList都是以类似数组的形式存储在内存中,LinkedList则以链表的形式进行存储。
2、List中的元素有序、允许有重复的元素,Set中的元素无序、不允许有重复元素。
3、Vector线程同步,ArrayList、LinkedList线程不同步。
4、LinkedList适合指定位置插入、删除操作,不适合查找;ArrayList、Vector适合查找,不适合指定位置的插入、删除操作。
5、ArrayList在元素填满容器时会自动扩充容器大小的50%,而Vector则是100%,因此ArrayList更节省空间。
详见:http://www.cnblogs.com/mgod/archive/2007/08/05/844011.html

c、list和map的区别
http://blog.csdn.net/speedme/article/details/22398395

d、HashMap与ConcurrentHashMap的区别(搜狐笔试)
最大的区别就是ConcurrentHashMap是线程安全的,hashMap不是线程安全的。
为什么线程安全呢:
ConcurrentHashMap代码中可以看出,它引入了一个“分段锁”的概念,具体可以理解为把一个大的Map拆分成N个小的HashTable,根据key.hashCode()来决定把key放到哪个HashTable中。
在ConcurrentHashMap中,就是把Map分成了N个Segment,put和get的时候,都是现根据key.hashCode()算出放到哪个Segment中:

e、tree
http://www.cnblogs.com/KeenLeung/archive/2012/11/03/2750545.html

8、缓存
Redis
http://www.iigrowing.cn/redis-huan-cun-ji-shu-xue-xi.html
Ehcache,Memcached
http://xuezhongfeicn.blog.163.com/blog/static/2246014120106144143737/