Java专项练习(牛客网)
第一题
- What will happen when you attempt to compile and run the following code?
1 | public class Test { |
解析:
- 静态代码块中的
x
是局部变量,这里不会有什么影响,不用管 - 静态成员变量
x, y
的初始值都为0
- 然后执行
x--
之后x
的值为-1
,调用myMethod()
方法,执行x++
之后x
的值
变为0
,x++
的值为-1
,然后接着执行++x
,这时x
的值为1
,++x
的值也为1
,所以这时y
的值为0
,所以x + y + ++x
的值为1 + 0 + 2 = 3
- 静态成员常量必须被初始化,静态变量和普通成员变量不用初始化
第二题
- 以下程序的运行结果是?
1 | public class TestThread { |
解析:
这里需要注意的是,新建了一个线程之后,并没有调用 start()
方法,所以这里并不会体现多线程,就只是和调用普通的类的 run()
方法是一样的,
所以这时候的执行结果是 foobar
,如果此时新建的线程调用了 start()
方法,那么这时候,该线程会被加入到
等待队列当中,并不会马上开始执行该线程,等待CPU调用它,才会开始执行,所以这时候就可能有两个答案 foobar
或 barfoo
。
第三题
下面哪个行为被打断不会导致InterruptedException:
Thread.join
Thread.sleep
Object.wait
CyclicBarrier.await
Thread.suspend
解析:
抛InterruptedException的代表方法有:
java.lang.Object 类的 wait 方法
java.lang.Thread 类的 sleep 方法
java.lang.Thread 类的 join 方法
CyclicBarrier是一个屏障类,它的await方法可以简单的理解为:等待多个线程同时到达之后才能继续进行,在此之前它就是这些线程的屏障,线程不能继续进行,而对于失败的同步尝试,CyclicBarrier 使用了一种要么全部要么全不 (all-or-none) 的破坏模式:如果因为中断、失败或者超时等原因,导致线程过早地离开了屏障点,那么在该屏障点等待的其他所有线程也将通过 BrokenBarrierException(如果它们几乎同时被中断,则用 interruptedException)以反常的方式离开。因此它被中断也是可以抛出interruptedException的,如果还是不清楚,查看一下JavaAPI,对于这个类介绍的清清楚楚。
第四题
List,Set,Queue 都继承于 Collection,SortedMap 继承于 Map
Collection
—–List
—–LinkedList 非同步
—-ArrayList 非同步,实现了可变大小的元素数组
—-Vector 同步
——Stack
—–Set 不允许有相同的元素
Map
—–HashTable 同步,实现一个key–value映射的哈希表
—–HashMap 非同步,
—–WeakHashMap 改进的HashMap,实现了“弱引用”,如果一个key不被引用,则被GC回收
第五题
启动一个线程的方法是: thread.start()
结束一个线程的方式通常用interrupt()方法
让线程等待另一个线程的方法是 thread.wait()
将一个线程标记成daemon线程,意味着当主线程结束,并且没有其它正在运行的非daemon线程时,该daemon线程也会自动结束。
第六题
GenericServlet类的实现接口中包括了ServletConfig接口,但是它自身的init(ServletConfig config)方法又需要外界给它传递一个实现ServletConfig的对象,就是说GenericServlet和ServletConfig的依赖关系既是继承关系,也是一种关联关系。
第七题
在GoF设计模式中,结构型模式有:
1.适配器模式 Adapter
适配器模式是将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
两个成熟的类需要通信,但是接口不同,由于开闭原则,我们不能去修改这两个类的接口,所以就需要一个适配器来完成衔接过程。
2.桥接模式 Bridge
桥接模式将抽象部分与它的实现部分分离,是它们都可以独立地变化。它很好的支持了开闭原则和组合锯和复用原则。实现系统可能有多角度分类,每一种分类都有可能变化,那么就把这些多角度分离出来让他们独立变化,减少他们之间的耦合。
3.组合模式 Composite
组合模式将对象组合成树形结构以表示部分-整体的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。
4.装饰模式 Decorator
装饰模式动态地给一个对象添加一些额外的职责,就增加功能来说,它比生成子类更灵活。也可以这样说,装饰模式把复杂类中的核心职责和装饰功能区分开了,这样既简化了复杂类,有去除了相关类中重复的装饰逻辑。 装饰模式没有通过继承原有类来扩展功能,但却达到了一样的目的,而且比继承更加灵活,所以可以说装饰模式是继承关系的一种替代方案。
5.外观模式 Facade
外观模式为子系统中的一组接口提供了同意的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式中,客户对各个具体的子系统是不了解的,所以对这些子系统进行了封装,对外只提供了用户所明白的单一而简单的接口,用户直接使用这个接口就可以完成操作,而不用去理睬具体的过程,而且子系统的变化不会影响到用户,这样就做到了信息隐蔽。
6.享元模式 Flyweight
享元模式为运用共享技术有效的支持大量细粒度的对象。因为它可以通过共享大幅度地减少单个实例的数目,避免了大量非常相似类的开销。.
享元模式是一个类别的多个对象共享这个类别的一个对象,而不是各自再实例化各自的对象。这样就达到了节省内存的目的。
7.代理模式 Proxy
为其他对象提供一种代理,并由代理对象控制对原对象的引用,以间接控制对原对象的访问。
第八题
java.lang.OutOfMemoryError: PermGen space
查了一下为”永久代”内存大小不足,“永久代”的解释应该为JVM中的方法区,主要用于存储类信息,常量,静态变量,即时编译器编译后代码等。本错误仅限于Hotspot虚拟机,本区进行垃圾回收很少,不够直接加大简单粗暴。
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
直接翻译报错信息:数组过长导致堆内存溢出,加大堆内存或者减少数组长度。
java.lang.OutOfMemoryError: Java heap space
堆内存不足,直接增大堆内存。
java.lang.OutOfMemoryError: nativeGetNewTLA
参考这里
第九题
需要注意的是,null从技术上讲是一个直接量,而不是关键字
Java关键字
第十题
下列程序的输出结果是什么?
1 | public class Test1{ |
解析:
重新说下String的equals方法,不是说所有类的equals方法都只判断值。
例如Object的equals方法的作用和==是相同的,都是判断引用。
只不过String类重写了Object的equals方法而已,代码如下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String) anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
先判断地址是否相等,如果相等直接返回true.
否则先判断是否是String类型的,如果是在判断每个字符是否相等,如果都相等返回true,其余情况返回false
第十一题
静态变量只能在类中定义,不能在类的方法中定义,静态变量属于类而不属于方法
参考资料: Java修饰符大汇总
第十二题
持久带中主要存放用于存放静态类型数据,如 Java Class, Method 等, 与垃圾收集器要收集的Java对象关系不大。
而heapspace分为年轻带和年老带
年轻代的垃圾回收叫 Young GC, 年老代的垃圾回收叫 Full GC。
在年轻代中经历了N次(可配置)垃圾回收后仍然存活的对象,就会被复制到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象
年老代溢出原因有 循环上万次的字符串处理、创建上千万个对象、在一段代码内申请上百M甚至上G的内存,既A B D选项
持久代溢出原因 动态加载了大量Java类而导致溢出
参考资料:JVM 堆内存设置原理
第十三题
- 环境变量可在编译source code时指定
- 在编译程序时,所能指定的环境变量可以包括class path
- javac一次可同时编译数个Java源文件
- javac.exe能指定编译结果要置于哪个目录(directory)
第十四题
在《java虚拟机》一书中明确讲了,释放掉占据的内存空间是由 gc
完成,但是程序员 无法明确强制其运行
,该空间在不被引用的时候不一定会立即被释放,这取决于GC本身,无法由程序员通过代码控制
。
Java 把内存划分成两种:一种是 栈内存
,另一种是 堆内存
。
在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时,Java 就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java 会自动释放掉为该变量分配的内存空间,该内存空间可以立即被另作它用。数组和对象
在没有引用变量指向它的时候,才变为垃圾,不能再被使用,但 仍然占据内存空间不放
,在随后的一个不确定的时间被垃圾回收器收走(释放掉)。这也是 Java 比较占内存的原因。
第十五题
1 | package Wangyi; |
这个程序编译会不通过,因为向上转型,父类的引用无法访问子类独有的方法
第十六题
抽象类中可以有抽象方法,一般是非抽象子类在实例化时调用
第十七题
垃圾回收包含的内容不少,但顺着下面的顺序捋清知识也并不难。首先要
搞清垃圾回收的范围(栈需要GC去回收吗?),然后就是回收的前提条件
如何判断一个对象已经可以被回收(这里只重点学习根搜索算法就行了),
之后便是建立在根搜索基础上的三种回收策略,最后便是JVM中对这三种
策略的具体实现。
1.范围:要回收哪些区域?
Java方法栈、本地方法栈以及PC计数器随方法或线程的结束而自然被回收,
所以这些区域不需要考虑回收问题。Java堆和方法区是GC回收的重点区域,
因为一个接口的多个实现类需要的内存不一样,一个方法的多个分支需要
的内存可能也不一样,而这两个区域又对立于栈可能随时都会有对象不再
被引用,因此这部分内存的分配和回收都是动态的。
2.前提:如何判断对象已死?
(1)引用计数法
引用计数法就是通过一个计数器记录该对象被引用的次数,方法简单高效,
但是解决不了循环引用的问题。比如对象A包含指向对象B的引用,对象B
也包含指向对象A的引用,但没有引用指向A和B,这时当前回收如果采用的
是引用计数法,那么对象A和B的被引用次数都为1,都不会被回收。
下面是循环引用的例子,在Hotspot JVM下可以被正常回收,可以证实JVM
采用的不是简单的引用计数法。通过-XX:+PrintGCDetails输出GC日志。
1 | package com.cdai.jvm.gc; |
[Full GC (System) [Tenured: 2048K->366K(10944K), 0.0046272 secs] 4604K->366K(15872K), [Perm : 154K->154K(12288K)], 0.0046751 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
(2)根搜索
通过选取一些根对象作为起始点,开始向下搜索,如果一个对象到根对象
不可达时,则说明此对象已经没有被引用,是可以被回收的。可以作为根的
对象有:栈中变量引用的对象,类静态属性引用的对象,常量引用的对象等。
因为每个线程都有一个栈,所以我们需要选取多个根对象。
附:对象复活
在根搜索中得到的不可达对象并不是立即就被标记成可回收的,而是先进行一次
标记放入F-Queue等待执行对象的finalize()方法,执行后GC将进行二次标记,复活
的对象之后将不会被回收。因此,使对象复活的唯一办法就是重写finalize()方法,
并使对象重新被引用。
1 | package com.cdai.jvm.gc; |
要注意的两点是:
第一,finalize()方法只会被执行一次,所以对象只有一次复活的机会。
第二,执行GC后,要停顿半秒等待优先级很低的finalize()执行完毕。
3.策略:垃圾回收的算法
(1)标记-清除
没错,这里的标记指的就是之前我们介绍过的两次标记过程。标记完成后就可以
对标记为垃圾的对象进行回收了。怎么样,简单吧。但是这种策略的缺点很明显,
回收后内存碎片很多,如果之后程序运行时申请大内存,可能会又导致一次GC。
虽然缺点明显,这种策略却是后两种策略的基础。正因为它的缺点,所以促成了
后两种策略的产生。
(2)标记-复制
将内存分为两块,标记完成开始回收时,将一块内存中保留的对象全部复制到另
一块空闲内存中。实现起来也很简单,当大部分对象都被回收时这种策略也很高效。
但这种策略也有缺点,可用内存变为一半了!
怎样解决呢?聪明的程序员们总是办法多过问题的。可以将堆不按1:1的比例分离,
而是按8:1:1分成一块Eden和两小块Survivor区,每次将Eden和Survivor中存活的对象
复制到另一块空闲的Survivor中。这三块区域并不是堆的全部,而是构成了新生代。
从下图可以看到这三块区域如何配合完成GC的,具体的对象空间分配以及晋升请
参加后面第6条补充。
为什么不是全部呢?如果回收时,空闲的那一小块Survivor不够用了怎么办?这就是
老年代的用处。当不够用时,这些对象将直接通过分配担保机制进入老年代。那么
老年代也使用标记-复制策略吧?当然不行!老年代中的对象可不像新生代中的,
每次回收都会清除掉大部分。如果贸然采用复制的策略,老年代的回收效率可想而知。
(3)标记-整理
根据老年代的特点,采用回收掉垃圾对象后对内存进行整理的策略再合适不过,将
所有存活下来的对象都向一端移动。
4.实现:虚拟机中的收集器
(1)新生代上的GC实现
Serial:单线程的收集器,只使用一个线程进行收集,并且收集时会暂停其他所有
工作线程(Stop the world)。它是Client模式下的默认新生代收集器。
ParNew:Serial收集器的多线程版本。在单CPU甚至两个CPU的环境下,由于线程
交互的开销,无法保证性能超越Serial收集器。
Parallel Scavenge:也是多线程收集器,与ParNew的区别是,它是吞吐量优先
收集器。吞吐量=运行用户代码时间/(运行用户代码+垃圾收集时间)。另一点区别
是配置-XX:+UseAdaptiveSizePolicy后,虚拟机会自动调整Eden/Survivor等参数来
提供用户所需的吞吐量。我们需要配置的就是内存大小-Xmx和吞吐量GCTimeRatio。
(2)老年代上的GC实现
Serial Old:Serial收集器的老年代版本。
Parallel Old:Parallel Scavenge的老年代版本。此前,如果新生代采用PS GC的话,
老年代只有Serial Old能与之配合。现在有了Parallel Old与之配合,可以在注重吞吐量
及CPU资源敏感的场合使用了。
CMS:采用的是标记-清除而非标记-整理,是一款并发低停顿的收集器。但是由于
采用标记-清除,内存碎片问题不可避免。可以使用-XX:CMSFullGCsBeforeCompaction
设置执行几次CMS回收后,跟着来一次内存碎片整理。
5.触发:何时开始GC?
Minor GC(新生代回收)的触发条件比较简单,Eden空间不足就开始进行Minor GC
回收新生代。而Full GC(老年代回收,一般伴随一次Minor GC)则有几种触发条件:
(1)老年代空间不足
(2)PermSpace空间不足
(3)统计得到的Minor GC晋升到老年代的平均大小大于老年代的剩余空间
这里注意一点:PermSpace并不等同于方法区,只不过是Hotspot JVM用PermSpace来
实现方法区而已,有些虚拟机没有PermSpace而用其他机制来实现方法区。
6.补充:对象的空间分配和晋升
(1)对象优先在Eden上分配
(2)大对象直接进入老年代
虚拟机提供了-XX:PretenureSizeThreshold参数,大于这个参数值的对象将直接分配到
老年代中。因为新生代采用的是标记-复制策略,在Eden中分配大对象将会导致Eden区
和两个Survivor区之间大量的内存拷贝。
(3)长期存活的对象将进入老年代
对象在Survivor区中每熬过一次Minor GC,年龄就增加1岁,当它的年龄增加到一定程度
(默认为15岁)时,就会晋升到老年代中。
第十八题
- 功能性注释嵌在源程序中,用于说明程序段或语句的功能以及数据的状态。
- 可使用空行或缩进,以便很容易区分注释和程序。
- 修改程序也应修改注释。
第十九题
线程运行速度与线程的优先级无关。
第二十题
超文本传输协议(HTTP)的统一资源定位符将从因特网获取信息的五个基本元素包括在一个简单的地址中:
传送协议。
服务器。
端口号。(以数字方式表示,若为HTTP的默认值“:80”可省略)
路径。(以“/”字符区别路径中的每一个目录名称)
查询。(GET模式的窗体参数,以“?”字符为起点,每个参数以“&”隔开,再以“=”分开参数名称与数据,通常以UTF8的URL编码,避开字符冲突的问题)
典型的统一资源定位符看上去是这样的:
(带方括号[]的为可选项):
protocol :// hostname[:port] / path / [;parameters][?query]#fragment
第二十一题
java中的字符串存储在 字符串常量区
,不会改变,发生改变
是会 新创建一个对象
StringBuffer是 线程安全的StringBuilder
StringBuilder跟StringBuffer功能相同,区别是StringBuilder 不是
线程安全的
StringBuilder和StringBuffer底层都是以 字符数组
存放的,可以 修改
内容
第二十二题
一般关系数据模型和对象数据模型之间有以下对应关系:表对应类
,记录对应对象
,表的字段对应类的属性
第二十三题
sleep和wait的区别有:
- 这两个方法来自不同的类分别是Thread和Object
- 最主要是sleep方法没有释放锁,而wait方法释放了锁,使得敏感词线程可以使用同步控制块或者方法。
- wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在
任何地方使用
1 | synchronized(x){ |
- sleep必须捕获异常,而
和 notifyAll``` 不需要捕获异常 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
### 第二十四题
编译不会编译注释中的内容。javac编译后的字节码文件中也不会出现自己手打的注释。
### 第二十五题
Java 提供的事件处理模型是一种人机交互模型。它有三个基本要素:
1) 事件源(Event Source):即事件发生的场所,就是指各个组件,如按钮等,点击按钮其实就是组件上发生的一个事件;
2) 事件(Event):事件封装了组件上发生的事情,比如按钮单击、按钮松开等等;
3) 事件监听器(Event Listener):负责监听事件源上发生的特定类型的事件,当事件到来时还必须负责处理相应的事件;
### 第二十六题
对象的初始化方式:
1. new时初始化;
2. 静态工厂 newInstance;
3. 反射Class.forName();
4. clone方式;
5. 反序列化;
### 第二十七题
1. 类方法是指类中被 `static` 修饰的方法,`无` this指针。
2. 类方法是可以调用其他类的static方法的。
3. 可以在类方法中 `生成实例对象` 再调用实例方法。
### 第二十八题
在Java中,函数代码小,频繁调用的情况下适合采用内联函数
在说内联函数之前,先说说函数的调用过程。
调用某个函数实际上将程序执行顺序转移到该函数所存放在内存中某个地址,将函数的程序内容执行完后,再返回到
转去执行该函数前的地方。这种转移操作要求在转去前要保护现场并记忆执行的地址,转回后先要恢复现场,并按原来保
存地址继续执行。也就是通常说的压栈和出栈。因此,函数调用要有一定的时间和空间方面的开销。那么对于那些函数体
代码不是很大,又频繁调用的函数来说,这个时间和空间的消耗会很大。
那怎么解决这个性能消耗问题呢,这个时候需要引入内联函数了。内联函数就是在程序编译时,编译器将程序中出现
的内联函数的调用表达式用内联函数的函数体来直接进行替换。显然,这样就不会产生转去转回的问题,但是由于在编译
时将函数体中的代码被替代到程序中,因此会增加目标程序代码量,进而增加空间开销,而在时间代销上不象函数调用时
那么大,可见它是以目标代码的增加为代价来换取时间的节省。
在大学里学习写C代码时,我们都学到将一些简短的逻辑定义在宏里。这样做的好处是,在编译器编译的时候会将用
到该宏的地方直接用宏的代码替换。这样就不再需要象调用方法那样的压栈、出栈,传参了。性能上提升了。内联函数的
处理方式与宏类似,但与宏又有所不同,内联函数拥有函数的本身特性(类型、作用域等等)
写过C++代码的应该都知道,在C++里有个内联函数,使用inline关键字修饰。另外,写在Class定义内的函数也会被
编译器视为内联函数。
那么,在java中的内联函数长什么模样呢?在java中使用final关键字来指示一个函数为内联函数,例如:
```java
public final void method1() {
//TODO something
}
这个指示并不是必需的。final关键字只是告诉编译器,在编译的时候考虑性能的提升,可以将final函数视为内联函数。
但最后编译器会怎么处理,编译器会分析将final函数处理为内联和不处理为内联的性能比较了。
第二十九题
double m = 3; // 正确
double m = 3.0; // 正确
Double m = 3; // 错误
Double m = 3.0; // 正确
int m = 0.0 // 错误
float m = 3; // 正确
float m = 3.0; // 错误,需要强制类型转换
第三十题
算法包括0个或多个输入,1个或多个输出,中间有穷个处理过程。
存储结构不属于算法结构
下面是维基百科对算法的定义输入
:一个算法必须有零个或以上输入量。输出
:一个算法应有一个或以上输出量,输出量是算法计算的结果。明确性
:算法的描述必须无歧义,以保证算法的实际执行结果是精确地匹配要求或期望,通常要求实际运行结果是确定的。有限性
:依据图灵的定义,一个算法是能够被任何图灵完备系统模拟的一串运算,而图灵机只有有限个状态、有限个输入符号和有限个转移函数(指令)。而一些定义更规定算法必须在有限个步骤内完成任务。有效性
:又称可行性。能够实现,算法中描述的操作都是可以通过已经实现的基本运算执行有限次来实现。
第三十一题
下列程序输出结果是:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class Example {
String str = new String("good");
char[] ch = { 'a', 'b', 'c' };
public static void main(String args[]) {
Example ex = new Example();
ex.change(ex.str, ex.ch);
System.out.print(ex.str + " and ");
System.out.print(ex.ch);
}
public static void change(String str, char ch[])
{
str = "test ok";
ch[0] = 'g';
}
}
最后的输出结果是:
good and gbc
分析:
这里需要注意的是,在java中只有按值传递,不管是基本类型还是对象,并没有按引用传递(详情见119页Java核心技术卷I),
change函数被调用时,第一个形参str接收了类的成员变量str的值(虽然名称都是str,但是却是两个独立的String类型的引用变量),注意这两个str自身都是变量且都指向了堆内存中的String对象”good”,当我们在change函数内部将str指向了另一个String对象”test ok”后,类的成员变量str仍然保持指向”good”,所以最终打印出来就是”good”;对于第二个形参ch,它也是接收了类的成员变量ch的值拷贝,这一点和str没有差别,即两个ch都指向了字符数组{ ‘a’, ‘b’, ‘c’ }的首地址,但是ch[0]表示的是字符数组中’a’的地址,修改了它也就修改了字符数组的第一个元素,这个改变在change函数返回之后也会存在。所以本题中两个形参传参的本质区别在于,修改str只是将形参指向了新的对象,对外部的实参没有任何影响,而修改ch[0]是实实在在的修改了字符数组的首元素。
扩展:
- 可以试验一下,在Example中再定义一个字符数组char[] ch2={‘d’};然后在change函数中把ch[0] = ‘g’;这句改成ch=ch2;,那么就会和str传参一样的,change函数返回后不会对类的成员ch有任何影响。
- 本题和“String类是一个final类,不能被继承”以及“String底层的字符数组被声明为private final char value[];所以其值不能被修改”这些String的特性无关。
- 我们平时交换数组中的两个元素时,一般定义swap方法为 void swap(int[] a, int i, int j),想想看为什么能达到目的?如果不使用数组,能实现交换吗?数组中存放的不是基本类型变量而是引用类型变量呢?
第三十二题
A、Semaphore:类,控制某个资源可被同时访问的个数;
B、ReentrantLock:类,具有与使用synchronized方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大;
C、 Future:接口,表示异步计算的结果;
D、 CountDownLatch: 类,可以用来在一个线程中等待多个线程完成任务的类。
第三十三题
在单行文本输入区(Textfield)构件上可能发生的事件包括 FocusEvent
焦点事件,所对应的事件监听器是 FocusListener
; ActionEvent
动作事件,所对应的事件监听器是 ActionListener
; MouseEvent
鼠标事件,所对应的事件监听器是 MouseMotionListener
。
第三十四题
HttpServlet容器响应Web客户请求流程如下:
- Web客户向Servlet容器发出Http请求;
- Servlet容器解析Web客户的Http请求;
- Servlet容器创建一个HttpRequest对象,在这个对象中封装Http请求信息;
- Servlet容器创建一个HttpResponse对象;
- Servlet容器调用HttpServlet的service方法,这个方法中会根据request的Method来判断具体是执行doGet还是doPost,把HttpRequest和HttpResponse对象作为service方法的参数传给HttpServlet对象;
- HttpServlet调用HttpRequest的有关方法,获取HTTP请求信息;
- HttpServlet调用HttpResponse的有关方法,生成响应数据;
- Servlet容器把HttpServlet的响应结果传给Web客户。
第三十五题
- ConcurrentHashMap实际上时 HashTable的升级版,使用segment来分段和管理锁,并不是synchronized;
- HashMap实现的接口有:Serializable, Cloneable, Map<K,V> ,没有实现Cllectio
- Arrays.asList()方法返回的列表是Arrays.ArrayList类型的,并不是java.util.ArrayList;
- SimpleDateFormat是线程不安全的
第三十六题
事务属性的种类:传播行为
、隔离级别
、只读
和事务超时
传播行为定义了被调用方法的事务边界。
传播行为 | 意义 |
---|---|
PROPERGATION_MANDATORY | 表示方法必须运行在一个事务中,如果当前事务不存在,就抛出异常 |
PROPAGATION_NESTED | 表示如果当前事务存在,则方法应该运行在一个嵌套事务中。否则,它看起来和 PROPAGATION_REQUIRED看起来没什么俩样 |
PROPAGATION_NEVER | 表示方法不能运行在一个事务中,否则抛出异常 |
PROPAGATION_NOT_SUPPORTED | 表示方法不能运行在一个事务中,如果当前存在一个事务,则该方法将被挂起 |
PROPAGATION_REQUIRED | 表示当前方法必须运行在一个事务中,如果当前存在一个事务,那么该方法运行在这个事务中,否则,将创建一个新的事务 |
PROPAGATION_REQUIRES_NEW | 表示当前方法必须运行在自己的事务中,如果当前存在一个事务,那么这个事务将在该方法运行期间被挂起 |
PROPAGATION_SUPPORTS | 表示当前方法不需要运行在一个是事务中,但如果有一个事务已经存在,该方法也可以运行在这个事务中 |
隔离级别
在操作数据时可能带来 3 个副作用,分别是脏读、不可重复读、幻读。为了避免这 3 中副作用的发生,在标准的 SQL 语句中定义了 4 种隔离级别,分别是未提交读、已提交读、可重复读、可序列化。而在 spring 事务中提供了 5 种隔离级别来对应在 SQL 中定义的 4 种隔离级别,如下:
隔离级别 | 意义 |
---|---|
ISOLATION_DEFAULT | 使用后端数据库默认的隔离级别 |
ISOLATION_READ_UNCOMMITTED | 允许读取未提交的数据(对应未提交读),可能导致脏读、不可重复读、幻读 |
ISOLATION_READ_COMMITTED | 允许在一个事务中读取另一个已经提交的事务中的数据(对应已提交读)。可以避免脏读,但是无法避免不可重复读和幻读 |
ISOLATION_REPEATABLE_READ | 一个事务不可能更新由另一个事务修改但尚未提交(回滚)的数据(对应可重复读)。可以避免脏读和不可重复读,但无法避免幻读 |
ISOLATION_SERIALIZABLE | 这种隔离级别是所有的事务都在一个执行队列中,依次顺序执行,而不是并行(对应可序列化)。可以避免脏读、不可重复读、幻读。但是这种隔离级别效率很低,因此,除非必须,否则不建议使用。 |
只读
如果在一个事务中所有关于数据库的操作都是只读的,也就是说,这些操作只读取数据库中的数据,而并不更新数据,那么应将事务设为只读模式( READ_ONLY_MARKER ) , 这样更有利于数据库进行优化 。
因为只读的优化措施是事务启动后由数据库实施的,因此,只有将那些具有可能启动新事务的传播行为 (PROPAGATION_NESTED 、 PROPAGATION_REQUIRED 、 PROPAGATION_REQUIRED_NEW) 的方法的事务标记成只读才有意义。
如果使用 Hibernate 作为持久化机制,那么将事务标记为只读后,会将 Hibernate 的 flush 模式设置为 FULSH_NEVER, 以告诉 Hibernate 避免和数据库之间进行不必要的同步,并将所有更新延迟到事务结束。
事务超时
如果一个事务长时间运行,这时为了尽量避免浪费系统资源,应为这个事务设置一个有效时间,使其等待数秒后自动回滚。与设
置“只读”属性一样,事务有效属性也需要给那些具有可能启动新事物的传播行为的方法的事务标记成只读才有意义。
第三十六题
ArrayList的构造函数总共有三个:
- ArrayList()构造一个初始容量为
10
的空列表。 - ArrayList(Collection<? extends E> c)构造一个包含指定
collection
的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的。 ArrayList(int initialCapacity)
构造一个具有指定初始容量
的空列表。
调用的是第三个构造函数,直接初始化为大小为20的list,没有扩容,所以选择A
参考资料:
第三十七题
>>
为带符号右移,右移后左边的空位被填充为 符号位
>>>
为不带符号右移,右移后左边的空位被填充为 0
没有 <<<
因为 <<
后右边 总是补0
第三十八题
1 | package NowCoder; |
该代码能正常运行
解析:
类方法(就是静态方法)不依附于对象,所以当然可以正常运行
引用不同于指针,引用中既包含指向对象的指针、又包含指向类的指针,test中指向对象的指针确实为空,但指向Test的指针可不为空
第三十九题
Web容器在启动时为每个Web应用创建一个ServletContext对象,ServletConfig对象中维护了ServletContext的引用,开发人员在编写servlet时,可以通过ServletConfig.getServletContext方法获得ServletContext对象。由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。ServletContext对象通常也被称之为context域对象。
- 多个Servlet通过ServletContext对象实现数据共享。 在InitServlet的Service方法中利用ServletContext对象存入需要共享的数据
ServletContext context = this.getServletContext();
context.setAttribute(“name”, “haha”);
在其它的Servlet中利用ServletContext对象获取共享的数据
ServletContext context = this.getServletContext();
String name = context.getAttribute(“name”);
- 获取WEB应用的初始化参数。 在DemoServlet的doPost方法中测试获取初始化参数的步骤如下:
ServletContext context = this.getServletContext();
String url = context.getInitParameter(“url”);
第四十题
jvm虚拟机的功能:
- 通过 ClassLoader 寻找和装载 class 文件
- 解释字节码成为指令并执行,提供 class 文件的运行环境
- 进行运行期间垃圾回收
- 提供与硬件交互的平台
第四十一题
在Java中,对于不再使用的内存资源,如调用完成的方法,不是
由 垃圾回收器
自动将其释放,而是方法调用时,会创建 栈帧
在栈中,调用完是 程序自动出栈释放
第四十二题
Spring并没有提供一个AOP方式的日志系统,而是我们需要使用AOP(面向方面编程)的方式,借助Spring与日志系统log4j实现我们自己的日志系统。
Spring是一系列轻量级Java EE框架的集合,比如:
spring context ,spring aop ,springMVC ,spring ORm ,spring web ,spring dao,core context
Spring中包含一个“依赖注入”模式的实现
使用Spring可以实现声明式事务
第四十三题
HashTable和HashMap区别
- 继承不同。
1 | public class Hashtable extends Dictionary implements Map |
Hashtable 中的方法是同步的,而HashMap中的方法在缺省情况下是非同步的。在多线程并发的环境下,可以直接使用Hashtable,但是要使用HashMap的话就要自己增加同步处理了。
Hashtable中,key和value都不允许出现null值。
在HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,即可以表示 HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。两个遍历方式的内部实现上不同。
Hashtable、HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。哈希值的使用不同,HashTable直接使用对象的hashCode。而HashMap重新计算hash值。
Hashtable和HashMap它们两个内部实现方式的数组的初始大小和扩容的方式。HashTable中hash数组默认大小是11,增加的方式是 old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数。
第四十四题
在Java中,关于接口实现类的方法这里还有几点需要注意:
- 接口中的方法如果抛出了异常,那么实现类中的方法跑出的异常必须 <= 接口中的方法抛出的异常
- 接口的中方法的返回值类型如果是Person,它的一个子类是Student,父类是Animal,那么实现类中的方法的返回值类型必须是 Student 或 Person,不能是Animal
- 实现类中的方法名、参数类型个数顺序都必须和接口中的方法相同
- 接口中的方法的默认权限是
public
,那么实现类重写后的方法的权限只能是public
第四十五题
String a = “llo”;
String str1 = “hello”;
String str2 = “he” + new String(“llo”);
String str3 = “he” + “llo”;
String str4 = “he” + a;
System.out.println(str1 == str2); // false
System.out.println(str1 == str3); // true
System.out.println(str1 == str4); // false
解析:
String str1= “hello”, String str2=”he”+”llo”;之所以str1\=\=str2返回true是因为两者都是在字符串常量池中(由于初始化就会在此区域分布内存)而常量池中的有个与栈区类似的特性,就是当str2指向的常量在常量区已存在时,他不会创建新的内存空间来存此常量,而是指向已有常量的内存(应该是以此节约空间),此时str1与str2这两个引用变量的值都是存”hello”的内存空间地址,但是String str3= “he”+a;String a=”llo”;时str1\=\=str3返回的为false,是因为:str1指向的hello在编译期一如既往的还是分配在常量区内,a指向的llo也在常量区,虽然str3也是初始化但是编译器无法判断a这货到底是什么个情况,进而不会将str3的等号右侧声明在常量区内,而是在通过构造时在堆区中的非常量池外的内存中声明,至此str3与str1不止是分配内存的时期不同(一个在编译期,一个在运行期)而且在内存空间的区域也不同,上面最高票答案只区分了时间没区分空间。
第四十六题
java语言的下面几种数组复制方法中,哪个效率最高?
复制的效率 System.arraycopy > clone > Arrays.copyOf > for
循环,这个有兴趣自己测试一下就知道了。这里面在System类源码中给出了arraycopy的方法,是native方法,也就是本地方法,肯定是最快的。而Arrays.copyOf(注意是Arrays类,不是Array)的实现,在源码中是调用System.arraycopy的,多了一个步骤,肯定就不是最快的
第四十七题
1 | package algorithms.com.guan.javajicu; |
最终的运行结果是:
0
第四十八题
在java中线程是有分优先等级的所以优先级不能相同
Thread实现了Runnable接口是一个类不是接口
实现多线程的三种方式,一种是继承Thread类使用此方式就不能继承其他的类了。还有两种是实现Runnable接口或者实现Callable接口
第四十九题
Servlet生命周期
Servlet的生命周期一般可以用三个方法来表示:
init()
:仅执行一次,负责在装载Servlet时初始化Servlet对象
service()
:核心方法,一般HttpServlet中会有get,post两种处理方式。在调用doGet和doPost方法时会构造servletRequest和servletResponse请求和响应对象作为参数。
destroy()
:在停止并且卸载Servlet时执行,负责释放资源
初始化阶段:Servlet启动,会读取配置文件中的信息,构造指定的Servlet对象,创建ServletConfig对象,将ServletConfig作为参数来调用init()方法。
第五十题
- 从地址栏显示来说
forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器. 浏览器根本不知道服务器发送的内容从哪里来的,所以它的地址栏还是原来的地址. redirect是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是新的URL.
- 从数据共享来说
forward:转发页面和转发到的页面可以共享request里面的数据. redirect:不能共享数据.
- 从运用地方来说
forward:一般用于用户登陆的时候,根据角色转发到相应的模块. redirect:一般用于用户注销登陆时返回主页面和跳转到其它的网站等.
- 从效率来说
forward:高. redirect:低.
第五十一题
- 有两个类 A 和 B,B 继承 A,且 A 和 B 中均有静态代码块和非静态代码块,那么获取 B 的一个实例时,相应的执行顺序如下。
先执行 A 类中的静态代码块,再执行 B 类中的代码块
接着执行 A 类中的非静态代码块和构造函数
最后执行 B 类中的非静态代码块和构造和函数。
第五十二题
- java 中访问权限
作用域 | 当前类 | 同一package | 子孙类 | 其他package |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
default | √ | √ | × | × |
private | √ | × | × | × |
第五十三题
中间件运行于操作系统之上,应用软件之下,而不是操作系统内核之中。
中间件是一种独立的系统软件或服务程序
,分布式应用软件借助这种软件在不同的技术之间共享资源。中间件位于客户机/ 服务器的操作系统之上,管理计算机资源和网络通讯。是连接两个独立应用程序或独立系统的软件。相连接的系统,即使它们具有不同的接口,但通过中间件相互之间仍能交换信息。执行中间件的一个关键途径是信息传递。通过中间件,应用程序可以工作于多平台或OS环境。
(简单来说,中间件并不能提高内核的效率
,一般只是负责网络信息的分发处理
)
第五十四题
1 | A:设置HTTP头标 |
第五十五题
关于PreparedStatement 和 Statement 描述
- PreparedStatement 比 Statement 执行效率更高
- PreparedStatement 会预编译 SQL 语句
- Statement 每次都会解析/编译 SQL 语句确立并优化数据获取路径
第五十六题
关于运算符优先级
单目>运算>移位>比较>按位>逻辑>三目>赋值
第五十七题
当程序执行到try{}语句中的return方法时,它会干这么一件事,将要返回的结果存储到一个临时栈中,然后程序不会立即返回,而是去执行finally{}中的程序。并不是说return后面的语句不执行,只是要等到finally{}中的语句(不存在return语句)执行完后,再返回。如果finally{}里也有一个return,那么在执行这个return时,就会更新临时栈中的值。
第五十八题
一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
禁止进行指令重排序。
volatile只提供了保证访问该变量时,每次都是从内存中读取最新值,并不会使用寄存器缓存该值——每次都会从内存中读取。
而对该变量的修改,volatile并不提供原子性的保证。
由于及时更新,很可能导致另一线程访问最新变量值,无法跳出循环的情况
多线程下计数器必须使用锁保护
第五十九题
int x = 1, float y = 2, 则表达式 x / y 的值为:0.5
解析:
- 当多个数字进行运算的时候,最终结果以
最高精度
的为准
参考:①float x = 1;与float x = 1.0f,这两种对于float类型的变量来说定义的方式都是正确的,也是比较常见的笔试题里面考察类型转换的例子,当第一种情况时,是将低精度int向上转型到float,是由于java的特性导致而不需要进行强制转换,而第二种情况则是比较正式的对于float变量的定义,由于这种类型本身在工作项目中并不常见,常用的带小数的数字我们一般都直接使用double类型,而double类型直接定义是没有问题的:double x = 1.0。而由于float的精度没有double类型高,因此必须对其进行显示的格式书写,如果没有这个f,就默认是double类型了。当然double x = 1.0d也是正确的命名,不信你可以尝试,虽然这是一个令人窒息的操作。②当多个精度的数字同时进行运算时,最终结果以最高精度为准。在多数情况下,整数和小数的各级混合运算中,一般结果都是double类型的。但就本题而言,结果是float类型的,因为x,y两个数字精度最高的就是float,所以最终结果是0.5,并且这个0.5是float类型的。为什么说不是double类型呢,当然如果你这样处理:double m = x/y,当然m是double类型的,也不会报错,而如果你写成int m = x/y,编译器报错提示的时候就会让你转换成float或者进行强制转换成int,他是不会提示你转换成double的,尽管这么写并没有报错,原因就是①
第六十题
- 在 HashMap 中,前后插入两个相同的键值,后一个键值对应的value会覆盖前一个键值的value
第六十一题
1 | public class Test { |
第六十二题
抽象方法 不能
有方法体,同时也 不能
有大括号,以分号结尾。
第六十三题
Java 中创建数组的几种写法
1 | float f[][] = new float[6][6]; |
第六十四题
异常相关
throws
用于在方法上声明
要抛出的异常,不是用来抛出异常的throw
用于抛出异常
第六十五题
内部类(也叫成员内部类)有四种访问权限
第六十六题
Object 对象中的方法
1.clone
方法
保护方法,实现对象的浅复制,只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常。
2.getClass
方法
final方法,获得运行时类型。
3.toString
方法
该方法用得比较多,一般子类都有覆盖。
4.finalize
方法
该方法用于释放资源。因为无法确定该方法什么时候被调用,很少使用。
5.equals
方法
该方法是非常重要的一个方法。一般equals和==是不一样的,但是在Object中两者是一样的。子类一般都要重写这个方法。
6.hashCode
方法
该方法用于哈希查找,重写了equals方法一般都要重写hashCode方法。这个方法在一些具有哈希功能的Collection中用到。
一般必须满足obj1.equals(obj2)==true。可以推出obj1.hash- Code()==obj2.hashCode(),但是hashCode相等不一定就满足equals。不过为了提高效率,应该尽量使上面两个条件接近等价。
7.wait
方法
wait方法就是使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait()方法一直等待,直到获得锁或者被中断。wait(long timeout)设定一个超时间隔,如果在规定时间内没有获得锁就返回。
调用该方法后当前线程进入睡眠状态,直到以下事件发生。
(1)其他线程调用了该对象的notify方法。
(2)其他线程调用了该对象的notifyAll方法。
(3)其他线程调用了interrupt中断该线程。
(4)时间间隔到了。
此时该线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException异常。
8.notify
方法
该方法唤醒在该对象上等待的某个线程。
9.notifyAll
方法
该方法唤醒在该对象上等待的所有线程。
第六十七题
以下表达式都是正确的
1 | long i = 0xfffL; |
第六十八题
hashMap
在单线程中使用大大提高效率
,在多线程的情况下使用hashTable来确保安全。hashTable
中使用synchronized
关键字来实现安全机制,但是synchronized是对整张hash表进行锁定即让线程独享整张hash表,在安全同时造成了浪费。concurrentHashMap采用分段加锁
的机制来确保安全- Arrays.asList() 将一个数组转化为一个List对象,这个方法会返回一个ArrayList类型的对象, 这个ArrayList类并非java.util.ArrayList类,而是Arrays类的
静态内部类
!用这个对象对列表进行添加删除更
新操作,就会报UnsupportedOperationException
异常。 加载驱动方法
1
2
31.Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
2. DriverManager.registerDriver(new com.mysql.jdbc.Driver());
3.System.setProperty("jdbc.drivers", "com.mysql.jdbc.Driver");Jsp只会在客户端第一次发请求的时候被编译,之后的请求不会再编译,同时tomcat能自动检测jsp变更与否,变更则再进行编译。
第一次编译并初始化时调用: init() ;销毁调用: destroy() 。在整个jsp生命周期中均只调用一次。
service()方法是接收请求,返回响应的方法。每次请求都执行一次,该方法被HttpServlet封装为doGet和doPost方法
创建Servlet的实例是由Servlet容器
来完成的,且创建Servlet实例是在初始化方法init()之前
Servlet的生命周期分为5个阶段:加载、创建、初始化、处理客户请求、卸载
。
(1)加载:容器通过类加载器使用servlet类对应的文件加载servlet
(2)创建:通过调用servlet构造函数创建一个servlet对象
(3)初始化:调用init方法初始化
(4)处理客户请求:每当有一个客户请求,容器会创建一个线程来处理客户请求
(5)卸载:调用destroy方法让servlet自己释放其占用的资源- Cookie是Web服务器发送给客户端的一小段信息,客户端请求时,可以读取该信息发送到服务器端
关闭浏览器意味着临时会话ID丢失,但所有与原会话关联的会话数据仍保留在服务器上,直至会话过期
在禁用Cookie时可以使用URL重写技术跟踪会话
第六十九题
就是源Ip地址,目标IP地址,源端口号和目标端口号的组合
服务器端:ServerSocket提供的实例1
ServerSocket server= new ServerSocket(端口号)
客户端:Socket提供的实例1
Socket soc=new Socket(ip地址,端口号)
第七十题
ResultSet跟普通的数组不同,索引从1
开始而不是从0
开始
第七十一题
1 | public interface Test { |
第七十二题
静态内部类
才可以声明静态方法
- 静态方法
不可以
使用非静态变量
- 抽象方法
不可以
有函数体
第七十三题
Java字节码是Java源文件编译产生的中间文件,java虚拟机是可运行java字节码的假想计算机,java的跨平台性也是相对与其他编程语言而言的,先介绍一下c语言的编译过程吧先是C语言源程序 也就是c的文件经过C编译程序编译后,生成windows可执行文件exe文件,然后在windows中执行。再介绍java的编译过程先是java源程序扩展名为java的文件,由java编译程序将java字节码文件,就是class文件然后在java虚拟机中执行。机器码是由CPU来执行的。Java编译后是字节码, 电脑只能运行机器码。Java在运行的时候
把字节码
变成机器码
。C/C++在编译的时候直接编译成机器码。
第七十四题
WSDL 可描述网络服务(Web Services)
WSDL 指网络服务描述语言 (Web Services Description Language)。
WSDL 是一种使用 XML 编写的文档。这种文档可描述某个 Web service。它可规定服务的位置,以及此服务提供的操作(或方法)
第七十五题
intValue()
是把Integer对象类型变成int的基础数据类型parseInt()
是把String 变成int的基础数据类型Valueof()
是把String 转化成Integer
对象类型
第七十六题
堆区分为三个区:年轻代(Young Generation)、年老代(Old Generation)、永久代(Permanent Generation,也就是方法区)。
年轻代:对象被创建时(new)的对象通常被放在Young(除了一些占据内存比较大的对象),经过一定的Minor GC(针对年轻代的内存回收)还活着的对象会被移动到年老代(一些具体的移动细节省略)。
年老代:就是上述年轻代移动过来的和一些比较大的对象。Minor GC(FullGC)是针对年老代的回收
永久代:存储的是final常量,static变量,常量池。
str3,str4都是直接new的对象,而substring的源代码其实也是new一个string对象返回
第七十七题
A. Vector相当于一个线程安全的List
B. HashMap是非线程安全的,其对应的线程安全类是HashTable
C. Arraylist是非线程安全的,其对应的线程安全类是Vector
D. StringBuffer是线程安全的,相当于一个线程安全的StringBuilder
E. Properties
实现了Map
接口,是线程安全的
第七十八题
- 构造函数是
不能继承
的,只是用来在子类调用
,(如果父类没有无参构造函数,创建子类时,必须在子类构造函数代码体的第一行显式调用父类的有参数构造函数,否则不能编译); - 如果父类没有有参构造函数,那么在创建子类时可以不显式调用父类构造函数,系统会默认调用父类的无参构造函数super();
- 如果父类没有无参构造函数,那系统就调不了默认的无参构造函数了,所以不显示调用编译也就无法通过了
补充说明:
- 在java中,创建有参构造函数后,系统就不在有默认的无参构造函数
- 如果父类中没有任何构造函数,系统会默认有一个无参的构造函数
第七十九题
A. Thread可以被继承,用于创建新的线程
B. Number类可以被继承,Integer,Float,Double等都继承自Number类
C. Double类的声明为
1
public final class Doubleextends Numberimplements Comparable
final生明的类不能被继承
D. Math类的声明为
1
public final class Mathextends Object
不能被继承
E. ClassLoader可以被继承,用户可以自定义类加载器
第八十题
枚举类在后台实现时,实际上是转化为一个继承了java.lang.Enum类的实体类,原先的枚举类型变成对应的实体类型,上例中AccountType变成了个class AccountType,并且会生成一个新的构造函数,若原来有构造函数,则在此基础上添加两个参数,生成新的构造函数,如上例子中:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27private AccountType() {
System.out.println("It is a account type");
}
会变成:
private AccountType(String s, int i) {
super(s,i); System.out.println(“It is a account type”);
}
而在这个类中,会添加若干字段来代表具体的枚举类型:
public static final AccountType SAVING;
public static final AccountType FIXED;
public static final AccountType CURRENT;
而且还会添加一段static代码段:
static{
SAVING = new AccountType("SAVING", 0);
CURRENT = new AccountType("CURRENT", 0);
$VALUES = new AccountType[]{
SAVING, FIXED, CURRENT
}
}
以此来初始化枚举中的每个具体类型。(并将所有具体类型放到一个$VALUE数组中,以便用序号访问具体类型)
在初始化过程中new AccountType构造函数被调用了三次,所以Enum中定义的构造函数中的打印代码被执行了3遍
第八十一题
for 循环的执行顺序
1 | public class Print{ |
最后的运行结果是:ABDCBDCB1
2
3
4
5
6
7
8
9
10for循环执行开始
首先执行out('A') 输出A;
然后执行out('B')&&(i<2)此时输出B,i=0,判断条件为真,执行for循环的循环体;
执行i++,out('D'),输出D i=1;
执行out('C'),输出C
然后执行out('B')&&(i<2) 此时输出B,i=1 判断条件为真 ,执行for循环的循环体;
执行i++,out('D'),输出D i=2;
执行out('C'),输出C
然后执行out('B')&&(i<2) 此时输出B,i=2,不满足i<2 判断条件为假 ,跳出循环;
所以结果为ABDCBDCB
第八十二题
010 八进制 8
0x8 十六进制 8
第八十三题
1 | public class StringDemo{ |
运行结果:
这题是在考编译器的优化,hotspot中 编译时”tao”+”bao”将直接变成”taobao”,b+c则不会优化,因为不知道在之前的步骤中bc会不会发生改变,而针对b+c则是用语法糖,新建一个StringBuilder来处理
第八十四题
父类没有无参的构造函数,所以子类需要在自己的构造函数中显式调用
父类的构造函数
第八十五题
1 | String x="fmn"; |
解析:
String x=”fmn”; “fmn”是在常量池里的不可变对象。
x.toUpperCase(); 在堆中new一个”FMN”对象,但无任何引用指向它。
String y=x.replace(‘f’,’F’); 在堆中 new一个”Fmn”对象,y指向它。
y=y+”wxy”; 在堆中 重新new一个”Fmnwxy”对象, 修改y指向,现在y指向它。
第八十六题
数组不是基本类型,在java中,数据类型就分为基本数据类型(即原生类)
和引用数据
类型,所以数组不是原生类。
第八十七题
序列化相关
一、序列化使用场景
对象的序列化:目的:将一个具体的对象进行持久化,写入到硬盘上。(注意:静态数据不能被序列化,因为静态数据不在堆内存中,而是在静态方法区中)
Serializable:用于启动对象的序列化功能,可以强制让指定类具备序列化功能,该接口中没有成员,这是一个标记接口。这个标记接口用于给序列化类提供UID。这个uid是依据类中的成员的数字签名进行运行获取的。如果不需要自动获取一个uid,可以在类中,手动指定一个名称为serialVersionUID id号。依据编译器的不同,或者对信息的高度敏感性。最好每一个序列化的类都进行手动显示的UID的指定。
二、非序列化使用场景
如何将非静态的数据不进行序列化?用transient 关键字修饰此变量即可。使用场景:为了安全起见,有时候我们不需要在网络间
传输一些数据(如身份证号码,密码,银行卡号等)
java 的transient
关键字为我们提供了便利,你只需要实现Serilizable
接口,将不需要序列化
的属性前添加关键字transient
,序列化对象的时候,这个属性就不会序列化到指定的目的地中。
第八十八题
标识符是以字母开头的字母数字序列:
数字是指0~9,字母指大小写英文字母、下划线(_)和美元符号($)
,也可以是Unicode字符集中的字符
,如汉
字;
字母、数字等字符的任意组合,不能包含+、- *
等字符;
不能使用关键字;
大小写敏感
第八十九题
动态 INCLUDE 用 jsp:include 动作实现 <jsp:include page=”included.jsp” flush=”true” /> 它总是会检查所含文件中的变化 , 适合用于包含动态页面 , 并且可以带参数。各个文件分别先编译,然后组合成一个文件。
静态 INCLUDE 用 include 伪码实现 , 定不会检查所含文件的变化 , 适用于包含静态页面 <%@ include file=”included.htm” %> 。先将文件的代码被原封不动地加入到了主页面从而合成一个文件,然后再进行翻译,此时不允许有相同的变量。
以下是对 include 两种用法的区别 , 主要有两个方面的不同 ;1
2
3
4
5
6
7一 : 执行时间上 :
<%@ include file="relativeURI"%> 是在翻译阶段执行
<jsp:include page="relativeURI" flush="true" /> 在请求处理阶段执行 .
二 : 引入内容的不同 :
<%@ include file="relativeURI"%>
引入静态文本 (html,jsp), 在 JSP 页面被转化成 servlet 之前和它融和到一起 .
<jsp:include page="relativeURI" flush="true" /> 引入执行页面或 servlet 所生成的应答文本 .
第九十题
1 | public class Demo { |
第九十一题
InputStreamReader(InputStream in, Charset cs)
创建使用给定字符集的 InputStreamReader。
第九十二题
Servlet 与 CGI 的比较
和CGI程序一样,Servlet可以响应用户的指令(提交一个FORM等等),也可以象CGI程序一样,收集用户表单的信息并给予动态反馈(简单的注册信息录入和检查错误)。
然而,Servlet的机制并不仅仅是这样简单的与用户表单进行交互。传统技术中,动态的网页建立和显示都是通过CGI来实现的,但是,有了Servlet,您可以大胆的放弃所有CGI(perl?php?甚至asp!),利用Servlet代替CGI,进行程序编写。
当用户浏览器发出一个Http/CGI的请求,或者说 调用一个CGI程序的时候,服务器端就要新启用一个进程 (而且是每次都要调用),调用CGI程序越多(特别是访问量高的时候),就要消耗系统越多的处理时间,只剩下越来越少的系统资源,对于用户来说,只能是漫长的等待服务器端的返回页面了,这对于电子商务激烈发展的今天来说,不能不说是一种技术上的遗憾。
而Servlet充分发挥了服务器端的资源并高效的利用。每次调用Servlet时并不是新启用一个进程 ,而是在一个Web服务器的进程敏感词享和分离线程,而线程最大的好处在于可以共享一个数据源,使系统资源被有效利用。传统的CGI程序,不具备平台无关性特征,系统环境发生变化,CGI程序就要瘫痪,而Servlet具备Java的平台无关性,在系统开发过程中保持了系统的可扩展性、高效性。
传统技术中,一般大都为二层的系统架构,即Web服务器+数据库服务器,导致网站访问量大的时候,无法克服CGI程序与数据库建立连接时速度慢的瓶颈,从而死机、数据库死锁现象频繁发生。而我们的Servlet有连接池的概念,它可以利用多线程的优点,在系统缓存中事先建立好若干与数据库的连接,到时候若想和数据库打交道可以随时跟系统”要”一个连接即可,反应速度可想而知。
第九十四题
- 标准ASCII只使用7个bit,扩展的ASCII使用8个bit。
- ANSI通常使用 0x00~0x7f 范围的1 个字节来表示 1 个英文字符。超出此范围的使用0x80~0xFFFF来编码,即扩展的ASCII编码。不同 ANSI 编码之间互不兼容。在简体中文Windows操作系统中,ANSI 编码代表 GBK 编码;在繁体中文Windows操作系统中,ANSI编码代表Big5;在日文Windows操作系统中,ANSI 编码代表 Shift_JIS 编码。
- ANSI通常使用 0x00~0x7f 范围的1 个字节来表示 1 个英文字符,即ASCII码
- ASCII码包含一些特殊空字符
第九十五题
- 在Java中,
先继承再实现
- java 的字符类型采用的是
Unicode
编码方案,每个Unicode
码占用16
个比特位。 - HashMap中改成
containsKey
和containsValue
方法来替换contains
方法。 - File类是java中
文件和目录路径名的抽象表示形式
。Java中对文件进行读写操作的基本类是IO类
。 - 有一个源代码,只包含import java.util.* ; 这一个import语句,我们
不能访问 util 子目录下的类
。 - DBMS 中实现事务持久性的子系统是
恢复管理子系统
- ceil:大于等于 x,并且与它最接近的数。floor:小于等于 x,且与 x 最接近的数。
- java的访问权限有public、protected、private和default的,default不能修饰变量。
- 普通变量不能用 abstract 修饰,abstract 一般修饰类和方法。
数值型变量在默认情况下为
int
型,byte和short型在计算时会自动转换为int型
计算,结果也是int 型。所以 a1*a2 的结果是 int 型的。1
2
3
41. byte a1 = 2, a2 = 4, a3;
2. short s = 16;
3. a2 = s;
4. a3 = a1 * a2;stream结尾
都是字节流
,reader和writer结尾
都是字符流
两者的区别就是读写的时候一个是按字节读写,一个是按字符。 实际使用通常差不多。 在读写文件需要对内容按行处理,比如比较特定字符,处理某一行数据的时候一般会选择字符流。 只是读写文件,和文件内容无关的,一般选择字节流。- Java 并发库 的Semaphore 可以很轻松完成信号量控制,Semaphore可以控制某个资源可被同时访问的个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。
- CyclicBarrier 主要的方法就是一个:await()。await() 方法没被调用一次,计数便会减少1,并阻塞住当前线程。当计数减至0时,阻塞解除,所有在此 CyclicBarrier 上面阻塞的线程开始运行。
- 直译过来就是倒计数(CountDown)门闩(Latch)。倒计数不用说,门闩的意思顾名思义就是阻止前进。在这里就是指 CountDownLatch.await() 方法在倒计数为0之前会阻塞当前线程。
- Counter不是并发编程的同步器
- 类中,可以有main方法,也可以没有main方法,而有一个main()方法的时候,也可以是
任意访问权限
。因为这个类不一定要执行,可以只是辅助类。 - 在java 中,声明一个数组时,
不能直接
限定数组长度,只有在创建实例化对象
时,才能给定数组长度
。 boolean类型
不能和任何类型
进行转换,会报出类型异常错误
。1
2
3
4如果两个操作数其中有一个是double类型,另一个操作就会转换为double类型。
否则,如果其中一个操作数是float类型,另一个将会转换为float类型。
否则,如果其中一个操作数是long类型,另一个会转换为long类型。
否则,两个操作数都转换为int类型。多线程是Java程序的并发机制,它能同步共享数、处理不同的事件。
- if的语句比较,
除boolean外
的其他类型都不能使用赋值语句,否则会提示无法转成布尔值。 - 管道为空,读操作会被阻塞;管道满了,写操作会被阻塞,管道是内存中的,匿名管道只能单向;命名管道可以双向,可以有多个进程对其读;也可以有多个进程写,只不过不能同时写
- 会产生信息丢失不如说丢失精度,这样可能更容易明白,而精度丢失只会发生在从
大范围到小范围的转换
,比如说 int 转换成 double 或 float。 1
2
3
41. 普通管道(PIPE):通常有两种限制,一是单工,即只能单向传输;二是血缘,即常用于父子进程间(或有血缘关系的进程间)。
2. 流管道(s_pipe):去除了上述的第一种限制,实现了双向传输。
3. 命名管道(name_pipe):去除了上述的第二种限制,实现了无血缘关系的不同进程间通信。
显然,要求是对于不同的服务器之间的通信,是要要求全双工形式的,而管道只能是半双工,虽然可以双向,但是同一时间只能有一个方向传输Java 技术允许使用
finalize()
方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在 Object 类中定义的,因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。finalize() 方法是在垃圾收集器删除对象之前
对这个对象调用的。注意:finalize不一定
被jvm调用,只有当垃圾回收器要清除垃圾时
才被调用。1
2
3
4
5
6
71. 尽量使用many-to-one,避免使用单项one-to-many
2. 灵活使用单向one-to-many
3. 不用一对一,使用多对一代替一对一
4. 配置对象缓存,不使用集合缓存
5. 一对多使用Bag 多对一使用Set
6. 继承使用显示多态 HQL:from object polymorphism="exlicit" 避免查处所有对象
7. 消除大表,使用二级缓存局部内部类前不能用修饰符public和private,protected
- 解决哈希冲突常用的两种方法是:
开放定址法
和链地址法
- 存根(Stub)与
动态链接
有关 - 泛型仅仅是java的一颗语法糖,它不会影响java虚拟机生成的汇编代码,在编译阶段,虚拟机就会把泛型的类型擦除,还原成没有泛型的代码,顶多
编译速度
稍微慢一些,执行速度
是完全没有什么区别的
。 - wait后进入等待锁定池,只有针对此对象发出notify方法后获得对象锁
进入就绪状态
,不是运行状态
1
2
3
4
5
6-Xmx:最大堆大小
-Xms:初始堆大小
-Xmn:年轻代大小
-XXSurvivorRatio:年轻代中Eden区与Survivor区的大小比值
年轻代5120m, Eden:Survivor=3,Survivor区大小=1024m(Survivor区有两个,即将年轻代分为5份,每个Survivor区占一份),总大小为2048m。
-Xms初始堆大小即最小内存值为10240mjava 中的数据类型分为
基本数据
和引用数据类型
,基本数据类型包括数值型
(整数类型:int、short、long、byte,浮点类型:double、float)、布尔型
(true)、字符型
(char),引用数据类型包括类、接口、数组
。1
2
3
4
5
6
7
8
9在java.util包中提供了一些集合类,常用的有List、Set和Map类,其中List类和Set类继承了Collection接口。
这些集合类又称为容器,长度是可变的,数组用来存放基本数据类型的数据,集合用来存放类对象的引用。
List接口、Set接口、Map接口以及Collection接口的主要特征如下:
Collection接口是List接口和Set接口的父接口,通常情况下不被直接使用。
List接口继承了Collection接口,List接口允许存放重复的对象,排序方式为按照对象的插入顺序。
Set接口继承了Collection接口,Set接口不允许存放重复的对象,排序方式为按照自身内部的排序规则。
Map接口以键值对(key—value)的形式存放对象,其中键(key)对象不可以重复,值(value)对象可以重复,排序方式为按照自身内部的规则。
C:Vector实现了List接口,即间接实现Collection接口
D:Iterator是Java迭代器最简单的实现,没有实现Collection接口1、三目运算是右结合的。2、&不短路,&&短路。
1
2
3
4
5
6基本类型数组:
byte[],short[],int[] ,默认值为0,
boolean[]默认值为false
float[],double[],默认值为0.0
对象类型数组:
默认值为null
37.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18EJB容器:Enterprise java bean 容器。更具有行业领域特色。他提供给运行在其中的组件EJB各种管理功能。
只要满足J2EE规范的EJB放入该容器,马上就会被容器进行高效率的管理。并且可以通过现成的接口来获得系统级别的服务。例如邮件服务、事务管理。
JNDI:(Java Naming & Directory Interface)JAVA命名目录服务。主要提供的功能是:提供一个目录系,让其它各地的应用程序在其上面留下自己的索引,从而满足快速查找和定位分布式应用程序的功能。
JMS:(Java Message Service)JAVA消息服务。主要实现各个应用程序之间的通讯。包括点对点和广播。
JTA:(Java Transaction API)JAVA事务服务。提供各种分布式事务服务。应用程序只需调用其提供的接口即可。
JAF:(Java Action FrameWork)JAVA安全认证框架。提供一些安全控制方面的框架。让开发者通过各种部署和自定义实现自己的个性安全控制策略。
RMI/IIOP:(Remote Method Invocation /internet对象请求中介协议)他们主要用于通过远程调用服务。
例如,远程有一台计算机上运行一个程序,它提供股票分析服务,我们可以在本地计算机上实现对其直接调用。当然这是要通过一定的规范才能在异构的系统之间进行通信。RMI是JAVA特有的。
- 局部内部类 和局部变量一样不能用访问权限修饰符修饰。
1
后台线程:指为其他线程提供服务的线程,也称为守护线程。JVM的垃圾回收线程就是一个后台线程。 前台线程:是指接受后台线程服务的线程,其实前台后台线程是联系在一起,就像傀儡和幕后操纵者一样的关系。傀儡是前台线程、幕后操纵者是后台线程。由前台线程创建的线程默认也是前台线程。可以通过isDaemon()和setDaemon()方法来判断和设置一个线程是否为后台线程。
同一个类的对象使用不同的内存段,但静态成员共享相同的内存空间
- java 中的接口是
多继承
的,类是单继承
的。 1
2
3
4
5System.out.println(1+"10"+3+"2");//11032
System.out.println(1+2+"10"+3+"2");//31032
System.out.println(1+"10"+3+1+"2");//110312
System.out.println(1 + "10" + (3 + 1) + 1); // 11041
在遇到string类型之前,int间使用 "+" 还是表示数值的相加,但是遇到第一个string后,后面就都是按string类型来了,变成字符串的拼接Log4j的日志打印级别
不可以
在运行时重新设置1
2
3
4
51. CopyOnWriteArrayList适用于写少读多的并发场景
2. ReadWriteLock即为读写锁,他要求写与写之间互斥,读与写之间互斥,
读与读之间可以并发执行。在读多写少的情况下可以提高效率
3. ConcurrentHashMap是同步的HashMap,读写都加锁
4. volatile只保证多线程操作的可见性,不保证原子性String是个不可继承类(final修饰),也是个不可变类(内部char数组被final修饰)。
StringBuffer和StringBuilder内部都是一般的动态数组
,所以可变。前者是线程安全的,因为方法基本都被synchronized修饰了- 一个.java文件中,可以有
多个类
,包括内部类和外部类
。考虑到内部类的原因,一个.java文件可以中可以有多个public
类。
但是对于外部类
而言,一个.java文件必须只能有一个public类
,同时这个类的类名必须和.java的文件名一致(包括大小写)
。 - java继承中对构造函数是不继承的,只是显式或者隐式调用
1
2
3
4
5
6
7vector:就比arraylist多了个同步化机制(线程安全),因为效率较低,现在已经不太建议使用。
在web应用中,特别是前台页面,往往效率(页面响应速度)是优先考虑的。
statck:堆栈类,先进后出
hashtable:就比hashmap多了个线程安全
enumeration:枚举,相当于迭代器1
2
3
4
5
6
7
8
9out->response.getWriter
request ->Service方法中的req参数
response ->Service方法中的resp参数
session ->request.getSession
application ->getServletContext
exception ->Throwable
page ->this
pageContext ->PageContext
Config ->getServletConfig数组元素在内存中是一个接着一个线性存放的,通过第一个元素就能访问随后的元素,避免了
数据覆盖
的可能性,和数据类型覆盖
并没有关系。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class Enclosingone {
//非静态内部类
public class InsideOne {}
//静态内部类
public static class InsideTwo{}
}
class Mytest02{
public static void main(String args []){
Enclosingone.InsideOne obj1 = new Enclosingone().new InsideOne();//非静态内部类对象
Enclosingone.InsideTwo obj2 = new Enclosingone.InsideTwo();//静态内部类对象
}
}
内部类其实和类的属性没什么区别,只是在声明的时候必须是Outer.Inner a
就像int a 一样,至于静态内部类和非静态内部类new的时候有点区别
Outer.Inner a=new Outer().new Inner()(非静态,先有Outer对象才能有属性)
Outer.Inner a=new Outer.Inner()要把Outer.Inner看成一部分,就像类变量一样不能用 this 来访问静态变量。
- 设为x进制,用十进制表示方法13就是
1*x^1+3*x^0=x+3
- Object 中有以下方法
getClass(), hashCode(), equals(), clone(), toString(), notify(), notifyAll(), wait(), finalize()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35use-a 是依赖关系
has-a 一般是组合关系
is-a 一般是继承关系
```---
title: Java专项练习(牛客网)
date: 2018-06-17 15:13:01
tags:
- Java
comments: true
categories: Java
---
# Java专项练习(牛客网)
### 第一题
1. What will happen when you attempt to compile and run the following code?
```java
public class Test {
static {
int x = 5;
}
static int x, y;
public static void main(String[] args) {
x--;
myMethod();
System.out.println(x + y + ++x);
}
public static void myMethod() {
y = x++ + ++x;
}
}
解析:
- 静态代码块中的
x
是局部变量,这里不会有什么影响,不用管 - 静态成员变量
x, y
的初始值都为0
- 然后执行
x--
之后x
的值为-1
,调用myMethod()
方法,执行x++
之后x
的值
变为0
,x++
的值为-1
,然后接着执行++x
,这时x
的值为1
,++x
的值也为1
,所以这时y
的值为0
,所以x + y + ++x
的值为1 + 0 + 2 = 3
- 静态成员常量必须被初始化,静态变量和普通成员变量不用初始化
第二题
- 以下程序的运行结果是?
1 | public class TestThread { |
解析:
这里需要注意的是,新建了一个线程之后,并没有调用 start()
方法,所以这里并不会体现多线程,就只是和调用普通的类的 run()
方法是一样的,
所以这时候的执行结果是 foobar
,如果此时新建的线程调用了 start()
方法,那么这时候,该线程会被加入到
等待队列当中,并不会马上开始执行该线程,等待CPU调用它,才会开始执行,所以这时候就可能有两个答案 foobar
或 barfoo
。
第三题
下面哪个行为被打断不会导致InterruptedException:
Thread.join
Thread.sleep
Object.wait
CyclicBarrier.await
Thread.suspend
解析:
抛InterruptedException的代表方法有:
java.lang.Object 类的 wait 方法
java.lang.Thread 类的 sleep 方法
java.lang.Thread 类的 join 方法
CyclicBarrier是一个屏障类,它的await方法可以简单的理解为:等待多个线程同时到达之后才能继续进行,在此之前它就是这些线程的屏障,线程不能继续进行,而对于失败的同步尝试,CyclicBarrier 使用了一种要么全部要么全不 (all-or-none) 的破坏模式:如果因为中断、失败或者超时等原因,导致线程过早地离开了屏障点,那么在该屏障点等待的其他所有线程也将通过 BrokenBarrierException(如果它们几乎同时被中断,则用 interruptedException)以反常的方式离开。因此它被中断也是可以抛出interruptedException的,如果还是不清楚,查看一下JavaAPI,对于这个类介绍的清清楚楚。
第四题
List,Set,Queue 都继承于 Collection,SortedMap 继承于 Map
Collection
—–List
—–LinkedList 非同步
—-ArrayList 非同步,实现了可变大小的元素数组
—-Vector 同步
——Stack
—–Set 不允许有相同的元素
Map
—–HashTable 同步,实现一个key–value映射的哈希表
—–HashMap 非同步,
—–WeakHashMap 改进的HashMap,实现了“弱引用”,如果一个key不被引用,则被GC回收
第五题
启动一个线程的方法是: thread.start()
结束一个线程的方式通常用interrupt()方法
让线程等待另一个线程的方法是 thread.wait()
将一个线程标记成daemon线程,意味着当主线程结束,并且没有其它正在运行的非daemon线程时,该daemon线程也会自动结束。
第六题
GenericServlet类的实现接口中包括了ServletConfig接口,但是它自身的init(ServletConfig config)方法又需要外界给它传递一个实现ServletConfig的对象,就是说GenericServlet和ServletConfig的依赖关系既是继承关系,也是一种关联关系。
第七题
在GoF设计模式中,结构型模式有:
1.适配器模式 Adapter
适配器模式是将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
两个成熟的类需要通信,但是接口不同,由于开闭原则,我们不能去修改这两个类的接口,所以就需要一个适配器来完成衔接过程。
2.桥接模式 Bridge
桥接模式将抽象部分与它的实现部分分离,是它们都可以独立地变化。它很好的支持了开闭原则和组合锯和复用原则。实现系统可能有多角度分类,每一种分类都有可能变化,那么就把这些多角度分离出来让他们独立变化,减少他们之间的耦合。
3.组合模式 Composite
组合模式将对象组合成树形结构以表示部分-整体的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。
4.装饰模式 Decorator
装饰模式动态地给一个对象添加一些额外的职责,就增加功能来说,它比生成子类更灵活。也可以这样说,装饰模式把复杂类中的核心职责和装饰功能区分开了,这样既简化了复杂类,有去除了相关类中重复的装饰逻辑。 装饰模式没有通过继承原有类来扩展功能,但却达到了一样的目的,而且比继承更加灵活,所以可以说装饰模式是继承关系的一种替代方案。
5.外观模式 Facade
外观模式为子系统中的一组接口提供了同意的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式中,客户对各个具体的子系统是不了解的,所以对这些子系统进行了封装,对外只提供了用户所明白的单一而简单的接口,用户直接使用这个接口就可以完成操作,而不用去理睬具体的过程,而且子系统的变化不会影响到用户,这样就做到了信息隐蔽。
6.享元模式 Flyweight
享元模式为运用共享技术有效的支持大量细粒度的对象。因为它可以通过共享大幅度地减少单个实例的数目,避免了大量非常相似类的开销。.
享元模式是一个类别的多个对象共享这个类别的一个对象,而不是各自再实例化各自的对象。这样就达到了节省内存的目的。
7.代理模式 Proxy
为其他对象提供一种代理,并由代理对象控制对原对象的引用,以间接控制对原对象的访问。
第八题
java.lang.OutOfMemoryError: PermGen space
查了一下为”永久代”内存大小不足,“永久代”的解释应该为JVM中的方法区,主要用于存储类信息,常量,静态变量,即时编译器编译后代码等。本错误仅限于Hotspot虚拟机,本区进行垃圾回收很少,不够直接加大简单粗暴。
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
直接翻译报错信息:数组过长导致堆内存溢出,加大堆内存或者减少数组长度。
java.lang.OutOfMemoryError: Java heap space
堆内存不足,直接增大堆内存。
java.lang.OutOfMemoryError: nativeGetNewTLA
参考这里
第九题
需要注意的是,null从技术上讲是一个直接量,而不是关键字
Java关键字
第十题
下列程序的输出结果是什么?
1 | public class Test1{ |
解析:
重新说下String的equals方法,不是说所有类的equals方法都只判断值。
例如Object的equals方法的作用和==是相同的,都是判断引用。
只不过String类重写了Object的equals方法而已,代码如下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String) anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
先判断地址是否相等,如果相等直接返回true.
否则先判断是否是String类型的,如果是在判断每个字符是否相等,如果都相等返回true,其余情况返回false
第十一题
静态变量只能在类中定义,不能在类的方法中定义,静态变量属于类而不属于方法
参考资料: Java修饰符大汇总
第十二题
持久带中主要存放用于存放静态类型数据,如 Java Class, Method 等, 与垃圾收集器要收集的Java对象关系不大。
而heapspace分为年轻带和年老带
年轻代的垃圾回收叫 Young GC, 年老代的垃圾回收叫 Full GC。
在年轻代中经历了N次(可配置)垃圾回收后仍然存活的对象,就会被复制到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象
年老代溢出原因有 循环上万次的字符串处理、创建上千万个对象、在一段代码内申请上百M甚至上G的内存,既A B D选项
持久代溢出原因 动态加载了大量Java类而导致溢出
参考资料:JVM 堆内存设置原理
第十三题
- 环境变量可在编译source code时指定
- 在编译程序时,所能指定的环境变量可以包括class path
- javac一次可同时编译数个Java源文件
- javac.exe能指定编译结果要置于哪个目录(directory)
第十四题
在《java虚拟机》一书中明确讲了,释放掉占据的内存空间是由 gc
完成,但是程序员 无法明确强制其运行
,该空间在不被引用的时候不一定会立即被释放,这取决于GC本身,无法由程序员通过代码控制
。
Java 把内存划分成两种:一种是 栈内存
,另一种是 堆内存
。
在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时,Java 就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java 会自动释放掉为该变量分配的内存空间,该内存空间可以立即被另作它用。数组和对象
在没有引用变量指向它的时候,才变为垃圾,不能再被使用,但 仍然占据内存空间不放
,在随后的一个不确定的时间被垃圾回收器收走(释放掉)。这也是 Java 比较占内存的原因。
第十五题
1 | package Wangyi; |
这个程序编译会不通过,因为向上转型,父类的引用无法访问子类独有的方法
第十六题
抽象类中可以有抽象方法,一般是非抽象子类在实例化时调用
第十七题
垃圾回收包含的内容不少,但顺着下面的顺序捋清知识也并不难。首先要
搞清垃圾回收的范围(栈需要GC去回收吗?),然后就是回收的前提条件
如何判断一个对象已经可以被回收(这里只重点学习根搜索算法就行了),
之后便是建立在根搜索基础上的三种回收策略,最后便是JVM中对这三种
策略的具体实现。
1.范围:要回收哪些区域?
Java方法栈、本地方法栈以及PC计数器随方法或线程的结束而自然被回收,
所以这些区域不需要考虑回收问题。Java堆和方法区是GC回收的重点区域,
因为一个接口的多个实现类需要的内存不一样,一个方法的多个分支需要
的内存可能也不一样,而这两个区域又对立于栈可能随时都会有对象不再
被引用,因此这部分内存的分配和回收都是动态的。
2.前提:如何判断对象已死?
(1)引用计数法
引用计数法就是通过一个计数器记录该对象被引用的次数,方法简单高效,
但是解决不了循环引用的问题。比如对象A包含指向对象B的引用,对象B
也包含指向对象A的引用,但没有引用指向A和B,这时当前回收如果采用的
是引用计数法,那么对象A和B的被引用次数都为1,都不会被回收。
下面是循环引用的例子,在Hotspot JVM下可以被正常回收,可以证实JVM
采用的不是简单的引用计数法。通过-XX:+PrintGCDetails输出GC日志。
1 | package com.cdai.jvm.gc; |
[Full GC (System) [Tenured: 2048K->366K(10944K), 0.0046272 secs] 4604K->366K(15872K), [Perm : 154K->154K(12288K)], 0.0046751 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
(2)根搜索
通过选取一些根对象作为起始点,开始向下搜索,如果一个对象到根对象
不可达时,则说明此对象已经没有被引用,是可以被回收的。可以作为根的
对象有:栈中变量引用的对象,类静态属性引用的对象,常量引用的对象等。
因为每个线程都有一个栈,所以我们需要选取多个根对象。
附:对象复活
在根搜索中得到的不可达对象并不是立即就被标记成可回收的,而是先进行一次
标记放入F-Queue等待执行对象的finalize()方法,执行后GC将进行二次标记,复活
的对象之后将不会被回收。因此,使对象复活的唯一办法就是重写finalize()方法,
并使对象重新被引用。
1 | package com.cdai.jvm.gc; |
要注意的两点是:
第一,finalize()方法只会被执行一次,所以对象只有一次复活的机会。
第二,执行GC后,要停顿半秒等待优先级很低的finalize()执行完毕。
3.策略:垃圾回收的算法
(1)标记-清除
没错,这里的标记指的就是之前我们介绍过的两次标记过程。标记完成后就可以
对标记为垃圾的对象进行回收了。怎么样,简单吧。但是这种策略的缺点很明显,
回收后内存碎片很多,如果之后程序运行时申请大内存,可能会又导致一次GC。
虽然缺点明显,这种策略却是后两种策略的基础。正因为它的缺点,所以促成了
后两种策略的产生。
(2)标记-复制
将内存分为两块,标记完成开始回收时,将一块内存中保留的对象全部复制到另
一块空闲内存中。实现起来也很简单,当大部分对象都被回收时这种策略也很高效。
但这种策略也有缺点,可用内存变为一半了!
怎样解决呢?聪明的程序员们总是办法多过问题的。可以将堆不按1:1的比例分离,
而是按8:1:1分成一块Eden和两小块Survivor区,每次将Eden和Survivor中存活的对象
复制到另一块空闲的Survivor中。这三块区域并不是堆的全部,而是构成了新生代。
从下图可以看到这三块区域如何配合完成GC的,具体的对象空间分配以及晋升请
参加后面第6条补充。
为什么不是全部呢?如果回收时,空闲的那一小块Survivor不够用了怎么办?这就是
老年代的用处。当不够用时,这些对象将直接通过分配担保机制进入老年代。那么
老年代也使用标记-复制策略吧?当然不行!老年代中的对象可不像新生代中的,
每次回收都会清除掉大部分。如果贸然采用复制的策略,老年代的回收效率可想而知。
(3)标记-整理
根据老年代的特点,采用回收掉垃圾对象后对内存进行整理的策略再合适不过,将
所有存活下来的对象都向一端移动。
4.实现:虚拟机中的收集器
(1)新生代上的GC实现
Serial:单线程的收集器,只使用一个线程进行收集,并且收集时会暂停其他所有
工作线程(Stop the world)。它是Client模式下的默认新生代收集器。
ParNew:Serial收集器的多线程版本。在单CPU甚至两个CPU的环境下,由于线程
交互的开销,无法保证性能超越Serial收集器。
Parallel Scavenge:也是多线程收集器,与ParNew的区别是,它是吞吐量优先
收集器。吞吐量=运行用户代码时间/(运行用户代码+垃圾收集时间)。另一点区别
是配置-XX:+UseAdaptiveSizePolicy后,虚拟机会自动调整Eden/Survivor等参数来
提供用户所需的吞吐量。我们需要配置的就是内存大小-Xmx和吞吐量GCTimeRatio。
(2)老年代上的GC实现
Serial Old:Serial收集器的老年代版本。
Parallel Old:Parallel Scavenge的老年代版本。此前,如果新生代采用PS GC的话,
老年代只有Serial Old能与之配合。现在有了Parallel Old与之配合,可以在注重吞吐量
及CPU资源敏感的场合使用了。
CMS:采用的是标记-清除而非标记-整理,是一款并发低停顿的收集器。但是由于
采用标记-清除,内存碎片问题不可避免。可以使用-XX:CMSFullGCsBeforeCompaction
设置执行几次CMS回收后,跟着来一次内存碎片整理。
5.触发:何时开始GC?
Minor GC(新生代回收)的触发条件比较简单,Eden空间不足就开始进行Minor GC
回收新生代。而Full GC(老年代回收,一般伴随一次Minor GC)则有几种触发条件:
(1)老年代空间不足
(2)PermSpace空间不足
(3)统计得到的Minor GC晋升到老年代的平均大小大于老年代的剩余空间
这里注意一点:PermSpace并不等同于方法区,只不过是Hotspot JVM用PermSpace来
实现方法区而已,有些虚拟机没有PermSpace而用其他机制来实现方法区。
6.补充:对象的空间分配和晋升
(1)对象优先在Eden上分配
(2)大对象直接进入老年代
虚拟机提供了-XX:PretenureSizeThreshold参数,大于这个参数值的对象将直接分配到
老年代中。因为新生代采用的是标记-复制策略,在Eden中分配大对象将会导致Eden区
和两个Survivor区之间大量的内存拷贝。
(3)长期存活的对象将进入老年代
对象在Survivor区中每熬过一次Minor GC,年龄就增加1岁,当它的年龄增加到一定程度
(默认为15岁)时,就会晋升到老年代中。
第十八题
- 功能性注释嵌在源程序中,用于说明程序段或语句的功能以及数据的状态。
- 可使用空行或缩进,以便很容易区分注释和程序。
- 修改程序也应修改注释。
第十九题
线程运行速度与线程的优先级无关。
第二十题
超文本传输协议(HTTP)的统一资源定位符将从因特网获取信息的五个基本元素包括在一个简单的地址中:
传送协议。
服务器。
端口号。(以数字方式表示,若为HTTP的默认值“:80”可省略)
路径。(以“/”字符区别路径中的每一个目录名称)
查询。(GET模式的窗体参数,以“?”字符为起点,每个参数以“&”隔开,再以“=”分开参数名称与数据,通常以UTF8的URL编码,避开字符冲突的问题)
典型的统一资源定位符看上去是这样的:
(带方括号[]的为可选项):
protocol :// hostname[:port] / path / [;parameters][?query]#fragment
第二十一题
java中的字符串存储在 字符串常量区
,不会改变,发生改变
是会 新创建一个对象
StringBuffer是 线程安全的StringBuilder
StringBuilder跟StringBuffer功能相同,区别是StringBuilder 不是
线程安全的
StringBuilder和StringBuffer底层都是以 字符数组
存放的,可以 修改
内容
第二十二题
一般关系数据模型和对象数据模型之间有以下对应关系:表对应类
,记录对应对象
,表的字段对应类的属性
第二十三题
sleep和wait的区别有:
- 这两个方法来自不同的类分别是Thread和Object
- 最主要是sleep方法没有释放锁,而wait方法释放了锁,使得敏感词线程可以使用同步控制块或者方法。
- wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在
任何地方使用
1 | synchronized(x){ |
- sleep必须捕获异常,而
和 notifyAll``` 不需要捕获异常 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
### 第二十四题
编译不会编译注释中的内容。javac编译后的字节码文件中也不会出现自己手打的注释。
### 第二十五题
Java 提供的事件处理模型是一种人机交互模型。它有三个基本要素:
1) 事件源(Event Source):即事件发生的场所,就是指各个组件,如按钮等,点击按钮其实就是组件上发生的一个事件;
2) 事件(Event):事件封装了组件上发生的事情,比如按钮单击、按钮松开等等;
3) 事件监听器(Event Listener):负责监听事件源上发生的特定类型的事件,当事件到来时还必须负责处理相应的事件;
### 第二十六题
对象的初始化方式:
1. new时初始化;
2. 静态工厂 newInstance;
3. 反射Class.forName();
4. clone方式;
5. 反序列化;
### 第二十七题
1. 类方法是指类中被 `static` 修饰的方法,`无` this指针。
2. 类方法是可以调用其他类的static方法的。
3. 可以在类方法中 `生成实例对象` 再调用实例方法。
### 第二十八题
在Java中,函数代码小,频繁调用的情况下适合采用内联函数
在说内联函数之前,先说说函数的调用过程。
调用某个函数实际上将程序执行顺序转移到该函数所存放在内存中某个地址,将函数的程序内容执行完后,再返回到
转去执行该函数前的地方。这种转移操作要求在转去前要保护现场并记忆执行的地址,转回后先要恢复现场,并按原来保
存地址继续执行。也就是通常说的压栈和出栈。因此,函数调用要有一定的时间和空间方面的开销。那么对于那些函数体
代码不是很大,又频繁调用的函数来说,这个时间和空间的消耗会很大。
那怎么解决这个性能消耗问题呢,这个时候需要引入内联函数了。内联函数就是在程序编译时,编译器将程序中出现
的内联函数的调用表达式用内联函数的函数体来直接进行替换。显然,这样就不会产生转去转回的问题,但是由于在编译
时将函数体中的代码被替代到程序中,因此会增加目标程序代码量,进而增加空间开销,而在时间代销上不象函数调用时
那么大,可见它是以目标代码的增加为代价来换取时间的节省。
在大学里学习写C代码时,我们都学到将一些简短的逻辑定义在宏里。这样做的好处是,在编译器编译的时候会将用
到该宏的地方直接用宏的代码替换。这样就不再需要象调用方法那样的压栈、出栈,传参了。性能上提升了。内联函数的
处理方式与宏类似,但与宏又有所不同,内联函数拥有函数的本身特性(类型、作用域等等)
写过C++代码的应该都知道,在C++里有个内联函数,使用inline关键字修饰。另外,写在Class定义内的函数也会被
编译器视为内联函数。
那么,在java中的内联函数长什么模样呢?在java中使用final关键字来指示一个函数为内联函数,例如:
```java
public final void method1() {
//TODO something
}
这个指示并不是必需的。final关键字只是告诉编译器,在编译的时候考虑性能的提升,可以将final函数视为内联函数。
但最后编译器会怎么处理,编译器会分析将final函数处理为内联和不处理为内联的性能比较了。
第二十九题
double m = 3; // 正确
double m = 3.0; // 正确
Double m = 3; // 错误
Double m = 3.0; // 正确
int m = 0.0 // 错误
float m = 3; // 正确
float m = 3.0; // 错误,需要强制类型转换
第三十题
算法包括0个或多个输入,1个或多个输出,中间有穷个处理过程。
存储结构不属于算法结构
下面是维基百科对算法的定义输入
:一个算法必须有零个或以上输入量。输出
:一个算法应有一个或以上输出量,输出量是算法计算的结果。明确性
:算法的描述必须无歧义,以保证算法的实际执行结果是精确地匹配要求或期望,通常要求实际运行结果是确定的。有限性
:依据图灵的定义,一个算法是能够被任何图灵完备系统模拟的一串运算,而图灵机只有有限个状态、有限个输入符号和有限个转移函数(指令)。而一些定义更规定算法必须在有限个步骤内完成任务。有效性
:又称可行性。能够实现,算法中描述的操作都是可以通过已经实现的基本运算执行有限次来实现。
第三十一题
下列程序输出结果是:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class Example {
String str = new String("good");
char[] ch = { 'a', 'b', 'c' };
public static void main(String args[]) {
Example ex = new Example();
ex.change(ex.str, ex.ch);
System.out.print(ex.str + " and ");
System.out.print(ex.ch);
}
public static void change(String str, char ch[])
{
str = "test ok";
ch[0] = 'g';
}
}
最后的输出结果是:
good and gbc
分析:
这里需要注意的是,在java中只有按值传递,不管是基本类型还是对象,并没有按引用传递(详情见119页Java核心技术卷I),
change函数被调用时,第一个形参str接收了类的成员变量str的值(虽然名称都是str,但是却是两个独立的String类型的引用变量),注意这两个str自身都是变量且都指向了堆内存中的String对象”good”,当我们在change函数内部将str指向了另一个String对象”test ok”后,类的成员变量str仍然保持指向”good”,所以最终打印出来就是”good”;对于第二个形参ch,它也是接收了类的成员变量ch的值拷贝,这一点和str没有差别,即两个ch都指向了字符数组{ ‘a’, ‘b’, ‘c’ }的首地址,但是ch[0]表示的是字符数组中’a’的地址,修改了它也就修改了字符数组的第一个元素,这个改变在change函数返回之后也会存在。所以本题中两个形参传参的本质区别在于,修改str只是将形参指向了新的对象,对外部的实参没有任何影响,而修改ch[0]是实实在在的修改了字符数组的首元素。
扩展:
- 可以试验一下,在Example中再定义一个字符数组char[] ch2={‘d’};然后在change函数中把ch[0] = ‘g’;这句改成ch=ch2;,那么就会和str传参一样的,change函数返回后不会对类的成员ch有任何影响。
- 本题和“String类是一个final类,不能被继承”以及“String底层的字符数组被声明为private final char value[];所以其值不能被修改”这些String的特性无关。
- 我们平时交换数组中的两个元素时,一般定义swap方法为 void swap(int[] a, int i, int j),想想看为什么能达到目的?如果不使用数组,能实现交换吗?数组中存放的不是基本类型变量而是引用类型变量呢?
第三十二题
A、Semaphore:类,控制某个资源可被同时访问的个数;
B、ReentrantLock:类,具有与使用synchronized方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大;
C、 Future:接口,表示异步计算的结果;
D、 CountDownLatch: 类,可以用来在一个线程中等待多个线程完成任务的类。
第三十三题
在单行文本输入区(Textfield)构件上可能发生的事件包括 FocusEvent
焦点事件,所对应的事件监听器是 FocusListener
; ActionEvent
动作事件,所对应的事件监听器是 ActionListener
; MouseEvent
鼠标事件,所对应的事件监听器是 MouseMotionListener
。
第三十四题
HttpServlet容器响应Web客户请求流程如下:
- Web客户向Servlet容器发出Http请求;
- Servlet容器解析Web客户的Http请求;
- Servlet容器创建一个HttpRequest对象,在这个对象中封装Http请求信息;
- Servlet容器创建一个HttpResponse对象;
- Servlet容器调用HttpServlet的service方法,这个方法中会根据request的Method来判断具体是执行doGet还是doPost,把HttpRequest和HttpResponse对象作为service方法的参数传给HttpServlet对象;
- HttpServlet调用HttpRequest的有关方法,获取HTTP请求信息;
- HttpServlet调用HttpResponse的有关方法,生成响应数据;
- Servlet容器把HttpServlet的响应结果传给Web客户。
第三十五题
- ConcurrentHashMap实际上时 HashTable的升级版,使用segment来分段和管理锁,并不是synchronized;
- HashMap实现的接口有:Serializable, Cloneable, Map<K,V> ,没有实现Cllectio
- Arrays.asList()方法返回的列表是Arrays.ArrayList类型的,并不是java.util.ArrayList;
- SimpleDateFormat是线程不安全的
第三十六题
事务属性的种类:传播行为
、隔离级别
、只读
和事务超时
传播行为定义了被调用方法的事务边界。
传播行为 | 意义 |
---|---|
PROPERGATION_MANDATORY | 表示方法必须运行在一个事务中,如果当前事务不存在,就抛出异常 |
PROPAGATION_NESTED | 表示如果当前事务存在,则方法应该运行在一个嵌套事务中。否则,它看起来和 PROPAGATION_REQUIRED看起来没什么俩样 |
PROPAGATION_NEVER | 表示方法不能运行在一个事务中,否则抛出异常 |
PROPAGATION_NOT_SUPPORTED | 表示方法不能运行在一个事务中,如果当前存在一个事务,则该方法将被挂起 |
PROPAGATION_REQUIRED | 表示当前方法必须运行在一个事务中,如果当前存在一个事务,那么该方法运行在这个事务中,否则,将创建一个新的事务 |
PROPAGATION_REQUIRES_NEW | 表示当前方法必须运行在自己的事务中,如果当前存在一个事务,那么这个事务将在该方法运行期间被挂起 |
PROPAGATION_SUPPORTS | 表示当前方法不需要运行在一个是事务中,但如果有一个事务已经存在,该方法也可以运行在这个事务中 |
隔离级别
在操作数据时可能带来 3 个副作用,分别是脏读、不可重复读、幻读。为了避免这 3 中副作用的发生,在标准的 SQL 语句中定义了 4 种隔离级别,分别是未提交读、已提交读、可重复读、可序列化。而在 spring 事务中提供了 5 种隔离级别来对应在 SQL 中定义的 4 种隔离级别,如下:
隔离级别 | 意义 |
---|---|
ISOLATION_DEFAULT | 使用后端数据库默认的隔离级别 |
ISOLATION_READ_UNCOMMITTED | 允许读取未提交的数据(对应未提交读),可能导致脏读、不可重复读、幻读 |
ISOLATION_READ_COMMITTED | 允许在一个事务中读取另一个已经提交的事务中的数据(对应已提交读)。可以避免脏读,但是无法避免不可重复读和幻读 |
ISOLATION_REPEATABLE_READ | 一个事务不可能更新由另一个事务修改但尚未提交(回滚)的数据(对应可重复读)。可以避免脏读和不可重复读,但无法避免幻读 |
ISOLATION_SERIALIZABLE | 这种隔离级别是所有的事务都在一个执行队列中,依次顺序执行,而不是并行(对应可序列化)。可以避免脏读、不可重复读、幻读。但是这种隔离级别效率很低,因此,除非必须,否则不建议使用。 |
只读
如果在一个事务中所有关于数据库的操作都是只读的,也就是说,这些操作只读取数据库中的数据,而并不更新数据,那么应将事务设为只读模式( READ_ONLY_MARKER ) , 这样更有利于数据库进行优化 。
因为只读的优化措施是事务启动后由数据库实施的,因此,只有将那些具有可能启动新事务的传播行为 (PROPAGATION_NESTED 、 PROPAGATION_REQUIRED 、 PROPAGATION_REQUIRED_NEW) 的方法的事务标记成只读才有意义。
如果使用 Hibernate 作为持久化机制,那么将事务标记为只读后,会将 Hibernate 的 flush 模式设置为 FULSH_NEVER, 以告诉 Hibernate 避免和数据库之间进行不必要的同步,并将所有更新延迟到事务结束。
事务超时
如果一个事务长时间运行,这时为了尽量避免浪费系统资源,应为这个事务设置一个有效时间,使其等待数秒后自动回滚。与设
置“只读”属性一样,事务有效属性也需要给那些具有可能启动新事物的传播行为的方法的事务标记成只读才有意义。
第三十六题
ArrayList的构造函数总共有三个:
- ArrayList()构造一个初始容量为
10
的空列表。 - ArrayList(Collection<? extends E> c)构造一个包含指定
collection
的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的。 ArrayList(int initialCapacity)
构造一个具有指定初始容量
的空列表。
调用的是第三个构造函数,直接初始化为大小为20的list,没有扩容,所以选择A
参考资料:
第三十七题
>>
为带符号右移,右移后左边的空位被填充为 符号位
>>>
为不带符号右移,右移后左边的空位被填充为 0
没有 <<<
因为 <<
后右边 总是补0
第三十八题
1 | package NowCoder; |
该代码能正常运行
解析:
类方法(就是静态方法)不依附于对象,所以当然可以正常运行
引用不同于指针,引用中既包含指向对象的指针、又包含指向类的指针,test中指向对象的指针确实为空,但指向Test的指针可不为空
第三十九题
Web容器在启动时为每个Web应用创建一个ServletContext对象,ServletConfig对象中维护了ServletContext的引用,开发人员在编写servlet时,可以通过ServletConfig.getServletContext方法获得ServletContext对象。由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。ServletContext对象通常也被称之为context域对象。
- 多个Servlet通过ServletContext对象实现数据共享。 在InitServlet的Service方法中利用ServletContext对象存入需要共享的数据
ServletContext context = this.getServletContext();
context.setAttribute(“name”, “haha”);
在其它的Servlet中利用ServletContext对象获取共享的数据
ServletContext context = this.getServletContext();
String name = context.getAttribute(“name”);
- 获取WEB应用的初始化参数。 在DemoServlet的doPost方法中测试获取初始化参数的步骤如下:
ServletContext context = this.getServletContext();
String url = context.getInitParameter(“url”);
第四十题
jvm虚拟机的功能:
- 通过 ClassLoader 寻找和装载 class 文件
- 解释字节码成为指令并执行,提供 class 文件的运行环境
- 进行运行期间垃圾回收
- 提供与硬件交互的平台
第四十一题
在Java中,对于不再使用的内存资源,如调用完成的方法,不是
由 垃圾回收器
自动将其释放,而是方法调用时,会创建 栈帧
在栈中,调用完是 程序自动出栈释放
第四十二题
Spring并没有提供一个AOP方式的日志系统,而是我们需要使用AOP(面向方面编程)的方式,借助Spring与日志系统log4j实现我们自己的日志系统。
Spring是一系列轻量级Java EE框架的集合,比如:
spring context ,spring aop ,springMVC ,spring ORm ,spring web ,spring dao,core context
Spring中包含一个“依赖注入”模式的实现
使用Spring可以实现声明式事务
第四十三题
HashTable和HashMap区别
- 继承不同。
1 | public class Hashtable extends Dictionary implements Map |
Hashtable 中的方法是同步的,而HashMap中的方法在缺省情况下是非同步的。在多线程并发的环境下,可以直接使用Hashtable,但是要使用HashMap的话就要自己增加同步处理了。
Hashtable中,key和value都不允许出现null值。
在HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,即可以表示 HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。两个遍历方式的内部实现上不同。
Hashtable、HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。哈希值的使用不同,HashTable直接使用对象的hashCode。而HashMap重新计算hash值。
Hashtable和HashMap它们两个内部实现方式的数组的初始大小和扩容的方式。HashTable中hash数组默认大小是11,增加的方式是 old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数。
第四十四题
在Java中,关于接口实现类的方法这里还有几点需要注意:
- 接口中的方法如果抛出了异常,那么实现类中的方法跑出的异常必须 <= 接口中的方法抛出的异常
- 接口的中方法的返回值类型如果是Person,它的一个子类是Student,父类是Animal,那么实现类中的方法的返回值类型必须是 Student 或 Person,不能是Animal
- 实现类中的方法名、参数类型个数顺序都必须和接口中的方法相同
- 接口中的方法的默认权限是
public
,那么实现类重写后的方法的权限只能是public
第四十五题
String a = “llo”;
String str1 = “hello”;
String str2 = “he” + new String(“llo”);
String str3 = “he” + “llo”;
String str4 = “he” + a;
System.out.println(str1 == str2); // false
System.out.println(str1 == str3); // true
System.out.println(str1 == str4); // false
解析:
String str1= “hello”, String str2=”he”+”llo”;之所以str1\=\=str2返回true是因为两者都是在字符串常量池中(由于初始化就会在此区域分布内存)而常量池中的有个与栈区类似的特性,就是当str2指向的常量在常量区已存在时,他不会创建新的内存空间来存此常量,而是指向已有常量的内存(应该是以此节约空间),此时str1与str2这两个引用变量的值都是存”hello”的内存空间地址,但是String str3= “he”+a;String a=”llo”;时str1\=\=str3返回的为false,是因为:str1指向的hello在编译期一如既往的还是分配在常量区内,a指向的llo也在常量区,虽然str3也是初始化但是编译器无法判断a这货到底是什么个情况,进而不会将str3的等号右侧声明在常量区内,而是在通过构造时在堆区中的非常量池外的内存中声明,至此str3与str1不止是分配内存的时期不同(一个在编译期,一个在运行期)而且在内存空间的区域也不同,上面最高票答案只区分了时间没区分空间。
第四十六题
java语言的下面几种数组复制方法中,哪个效率最高?
复制的效率 System.arraycopy > clone > Arrays.copyOf > for
循环,这个有兴趣自己测试一下就知道了。这里面在System类源码中给出了arraycopy的方法,是native方法,也就是本地方法,肯定是最快的。而Arrays.copyOf(注意是Arrays类,不是Array)的实现,在源码中是调用System.arraycopy的,多了一个步骤,肯定就不是最快的
第四十七题
1 | package algorithms.com.guan.javajicu; |
最终的运行结果是:
0
第四十八题
在java中线程是有分优先等级的所以优先级不能相同
Thread实现了Runnable接口是一个类不是接口
实现多线程的三种方式,一种是继承Thread类使用此方式就不能继承其他的类了。还有两种是实现Runnable接口或者实现Callable接口
第四十九题
Servlet生命周期
Servlet的生命周期一般可以用三个方法来表示:
init()
:仅执行一次,负责在装载Servlet时初始化Servlet对象
service()
:核心方法,一般HttpServlet中会有get,post两种处理方式。在调用doGet和doPost方法时会构造servletRequest和servletResponse请求和响应对象作为参数。
destroy()
:在停止并且卸载Servlet时执行,负责释放资源
初始化阶段:Servlet启动,会读取配置文件中的信息,构造指定的Servlet对象,创建ServletConfig对象,将ServletConfig作为参数来调用init()方法。
第五十题
- 从地址栏显示来说
forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器. 浏览器根本不知道服务器发送的内容从哪里来的,所以它的地址栏还是原来的地址. redirect是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是新的URL.
- 从数据共享来说
forward:转发页面和转发到的页面可以共享request里面的数据. redirect:不能共享数据.
- 从运用地方来说
forward:一般用于用户登陆的时候,根据角色转发到相应的模块. redirect:一般用于用户注销登陆时返回主页面和跳转到其它的网站等.
- 从效率来说
forward:高. redirect:低.
第五十一题
- 有两个类 A 和 B,B 继承 A,且 A 和 B 中均有静态代码块和非静态代码块,那么获取 B 的一个实例时,相应的执行顺序如下。
先执行 A 类中的静态代码块,再执行 B 类中的代码块
接着执行 A 类中的非静态代码块和构造函数
最后执行 B 类中的非静态代码块和构造和函数。
第五十二题
- java 中访问权限
作用域 | 当前类 | 同一package | 子孙类 | 其他package |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
default | √ | √ | × | × |
private | √ | × | × | × |
第五十三题
中间件运行于操作系统之上,应用软件之下,而不是操作系统内核之中。
中间件是一种独立的系统软件或服务程序
,分布式应用软件借助这种软件在不同的技术之间共享资源。中间件位于客户机/ 服务器的操作系统之上,管理计算机资源和网络通讯。是连接两个独立应用程序或独立系统的软件。相连接的系统,即使它们具有不同的接口,但通过中间件相互之间仍能交换信息。执行中间件的一个关键途径是信息传递。通过中间件,应用程序可以工作于多平台或OS环境。
(简单来说,中间件并不能提高内核的效率
,一般只是负责网络信息的分发处理
)
第五十四题
1 | A:设置HTTP头标 |
第五十五题
关于PreparedStatement 和 Statement 描述
- PreparedStatement 比 Statement 执行效率更高
- PreparedStatement 会预编译 SQL 语句
- Statement 每次都会解析/编译 SQL 语句确立并优化数据获取路径
第五十六题
关于运算符优先级
单目>运算>移位>比较>按位>逻辑>三目>赋值
第五十七题
当程序执行到try{}语句中的return方法时,它会干这么一件事,将要返回的结果存储到一个临时栈中,然后程序不会立即返回,而是去执行finally{}中的程序。并不是说return后面的语句不执行,只是要等到finally{}中的语句(不存在return语句)执行完后,再返回。如果finally{}里也有一个return,那么在执行这个return时,就会更新临时栈中的值。
第五十八题
一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
禁止进行指令重排序。
volatile只提供了保证访问该变量时,每次都是从内存中读取最新值,并不会使用寄存器缓存该值——每次都会从内存中读取。
而对该变量的修改,volatile并不提供原子性的保证。
由于及时更新,很可能导致另一线程访问最新变量值,无法跳出循环的情况
多线程下计数器必须使用锁保护
第五十九题
int x = 1, float y = 2, 则表达式 x / y 的值为:0.5
解析:
- 当多个数字进行运算的时候,最终结果以
最高精度
的为准
参考:①float x = 1;与float x = 1.0f,这两种对于float类型的变量来说定义的方式都是正确的,也是比较常见的笔试题里面考察类型转换的例子,当第一种情况时,是将低精度int向上转型到float,是由于java的特性导致而不需要进行强制转换,而第二种情况则是比较正式的对于float变量的定义,由于这种类型本身在工作项目中并不常见,常用的带小数的数字我们一般都直接使用double类型,而double类型直接定义是没有问题的:double x = 1.0。而由于float的精度没有double类型高,因此必须对其进行显示的格式书写,如果没有这个f,就默认是double类型了。当然double x = 1.0d也是正确的命名,不信你可以尝试,虽然这是一个令人窒息的操作。②当多个精度的数字同时进行运算时,最终结果以最高精度为准。在多数情况下,整数和小数的各级混合运算中,一般结果都是double类型的。但就本题而言,结果是float类型的,因为x,y两个数字精度最高的就是float,所以最终结果是0.5,并且这个0.5是float类型的。为什么说不是double类型呢,当然如果你这样处理:double m = x/y,当然m是double类型的,也不会报错,而如果你写成int m = x/y,编译器报错提示的时候就会让你转换成float或者进行强制转换成int,他是不会提示你转换成double的,尽管这么写并没有报错,原因就是①
第六十题
- 在 HashMap 中,前后插入两个相同的键值,后一个键值对应的value会覆盖前一个键值的value
第六十一题
1 | public class Test { |
第六十二题
抽象方法 不能
有方法体,同时也 不能
有大括号,以分号结尾。
第六十三题
Java 中创建数组的几种写法
1 | float f[][] = new float[6][6]; |
第六十四题
异常相关
throws
用于在方法上声明
要抛出的异常,不是用来抛出异常的throw
用于抛出异常
第六十五题
内部类(也叫成员内部类)有四种访问权限
第六十六题
Object 对象中的方法
1.clone
方法
保护方法,实现对象的浅复制,只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常。
2.getClass
方法
final方法,获得运行时类型。
3.toString
方法
该方法用得比较多,一般子类都有覆盖。
4.finalize
方法
该方法用于释放资源。因为无法确定该方法什么时候被调用,很少使用。
5.equals
方法
该方法是非常重要的一个方法。一般equals和==是不一样的,但是在Object中两者是一样的。子类一般都要重写这个方法。
6.hashCode
方法
该方法用于哈希查找,重写了equals方法一般都要重写hashCode方法。这个方法在一些具有哈希功能的Collection中用到。
一般必须满足obj1.equals(obj2)==true。可以推出obj1.hash- Code()==obj2.hashCode(),但是hashCode相等不一定就满足equals。不过为了提高效率,应该尽量使上面两个条件接近等价。
7.wait
方法
wait方法就是使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait()方法一直等待,直到获得锁或者被中断。wait(long timeout)设定一个超时间隔,如果在规定时间内没有获得锁就返回。
调用该方法后当前线程进入睡眠状态,直到以下事件发生。
(1)其他线程调用了该对象的notify方法。
(2)其他线程调用了该对象的notifyAll方法。
(3)其他线程调用了interrupt中断该线程。
(4)时间间隔到了。
此时该线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException异常。
8.notify
方法
该方法唤醒在该对象上等待的某个线程。
9.notifyAll
方法
该方法唤醒在该对象上等待的所有线程。
第六十七题
以下表达式都是正确的
1 | long i = 0xfffL; |
第六十八题
hashMap
在单线程中使用大大提高效率
,在多线程的情况下使用hashTable来确保安全。hashTable
中使用synchronized
关键字来实现安全机制,但是synchronized是对整张hash表进行锁定即让线程独享整张hash表,在安全同时造成了浪费。concurrentHashMap采用分段加锁
的机制来确保安全- Arrays.asList() 将一个数组转化为一个List对象,这个方法会返回一个ArrayList类型的对象, 这个ArrayList类并非java.util.ArrayList类,而是Arrays类的
静态内部类
!用这个对象对列表进行添加删除更
新操作,就会报UnsupportedOperationException
异常。 加载驱动方法
1
2
31.Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
2. DriverManager.registerDriver(new com.mysql.jdbc.Driver());
3.System.setProperty("jdbc.drivers", "com.mysql.jdbc.Driver");Jsp只会在客户端第一次发请求的时候被编译,之后的请求不会再编译,同时tomcat能自动检测jsp变更与否,变更则再进行编译。
第一次编译并初始化时调用: init() ;销毁调用: destroy() 。在整个jsp生命周期中均只调用一次。
service()方法是接收请求,返回响应的方法。每次请求都执行一次,该方法被HttpServlet封装为doGet和doPost方法
创建Servlet的实例是由Servlet容器
来完成的,且创建Servlet实例是在初始化方法init()之前
Servlet的生命周期分为5个阶段:加载、创建、初始化、处理客户请求、卸载
。
(1)加载:容器通过类加载器使用servlet类对应的文件加载servlet
(2)创建:通过调用servlet构造函数创建一个servlet对象
(3)初始化:调用init方法初始化
(4)处理客户请求:每当有一个客户请求,容器会创建一个线程来处理客户请求
(5)卸载:调用destroy方法让servlet自己释放其占用的资源- Cookie是Web服务器发送给客户端的一小段信息,客户端请求时,可以读取该信息发送到服务器端
关闭浏览器意味着临时会话ID丢失,但所有与原会话关联的会话数据仍保留在服务器上,直至会话过期
在禁用Cookie时可以使用URL重写技术跟踪会话
第六十九题
就是源Ip地址,目标IP地址,源端口号和目标端口号的组合
服务器端:ServerSocket提供的实例1
ServerSocket server= new ServerSocket(端口号)
客户端:Socket提供的实例1
Socket soc=new Socket(ip地址,端口号)
第七十题
ResultSet跟普通的数组不同,索引从1
开始而不是从0
开始
第七十一题
1 | public interface Test { |
第七十二题
静态内部类
才可以声明静态方法
- 静态方法
不可以
使用非静态变量
- 抽象方法
不可以
有函数体
第七十三题
Java字节码是Java源文件编译产生的中间文件,java虚拟机是可运行java字节码的假想计算机,java的跨平台性也是相对与其他编程语言而言的,先介绍一下c语言的编译过程吧先是C语言源程序 也就是c的文件经过C编译程序编译后,生成windows可执行文件exe文件,然后在windows中执行。再介绍java的编译过程先是java源程序扩展名为java的文件,由java编译程序将java字节码文件,就是class文件然后在java虚拟机中执行。机器码是由CPU来执行的。Java编译后是字节码, 电脑只能运行机器码。Java在运行的时候
把字节码
变成机器码
。C/C++在编译的时候直接编译成机器码。
第七十四题
WSDL 可描述网络服务(Web Services)
WSDL 指网络服务描述语言 (Web Services Description Language)。
WSDL 是一种使用 XML 编写的文档。这种文档可描述某个 Web service。它可规定服务的位置,以及此服务提供的操作(或方法)
第七十五题
intValue()
是把Integer对象类型变成int的基础数据类型parseInt()
是把String 变成int的基础数据类型Valueof()
是把String 转化成Integer
对象类型
第七十六题
堆区分为三个区:年轻代(Young Generation)、年老代(Old Generation)、永久代(Permanent Generation,也就是方法区)。
年轻代:对象被创建时(new)的对象通常被放在Young(除了一些占据内存比较大的对象),经过一定的Minor GC(针对年轻代的内存回收)还活着的对象会被移动到年老代(一些具体的移动细节省略)。
年老代:就是上述年轻代移动过来的和一些比较大的对象。Minor GC(FullGC)是针对年老代的回收
永久代:存储的是final常量,static变量,常量池。
str3,str4都是直接new的对象,而substring的源代码其实也是new一个string对象返回
第七十七题
A. Vector相当于一个线程安全的List
B. HashMap是非线程安全的,其对应的线程安全类是HashTable
C. Arraylist是非线程安全的,其对应的线程安全类是Vector
D. StringBuffer是线程安全的,相当于一个线程安全的StringBuilder
E. Properties
实现了Map
接口,是线程安全的
第七十八题
- 构造函数是
不能继承
的,只是用来在子类调用
,(如果父类没有无参构造函数,创建子类时,必须在子类构造函数代码体的第一行显式调用父类的有参数构造函数,否则不能编译); - 如果父类没有有参构造函数,那么在创建子类时可以不显式调用父类构造函数,系统会默认调用父类的无参构造函数super();
- 如果父类没有无参构造函数,那系统就调不了默认的无参构造函数了,所以不显示调用编译也就无法通过了
补充说明:
- 在java中,创建有参构造函数后,系统就不在有默认的无参构造函数
- 如果父类中没有任何构造函数,系统会默认有一个无参的构造函数
第七十九题
A. Thread可以被继承,用于创建新的线程
B. Number类可以被继承,Integer,Float,Double等都继承自Number类
C. Double类的声明为
1
public final class Doubleextends Numberimplements Comparable
final生明的类不能被继承
D. Math类的声明为
1
public final class Mathextends Object
不能被继承
E. ClassLoader可以被继承,用户可以自定义类加载器
第八十题
枚举类在后台实现时,实际上是转化为一个继承了java.lang.Enum类的实体类,原先的枚举类型变成对应的实体类型,上例中AccountType变成了个class AccountType,并且会生成一个新的构造函数,若原来有构造函数,则在此基础上添加两个参数,生成新的构造函数,如上例子中:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27private AccountType() {
System.out.println("It is a account type");
}
会变成:
private AccountType(String s, int i) {
super(s,i); System.out.println(“It is a account type”);
}
而在这个类中,会添加若干字段来代表具体的枚举类型:
public static final AccountType SAVING;
public static final AccountType FIXED;
public static final AccountType CURRENT;
而且还会添加一段static代码段:
static{
SAVING = new AccountType("SAVING", 0);
CURRENT = new AccountType("CURRENT", 0);
$VALUES = new AccountType[]{
SAVING, FIXED, CURRENT
}
}
以此来初始化枚举中的每个具体类型。(并将所有具体类型放到一个$VALUE数组中,以便用序号访问具体类型)
在初始化过程中new AccountType构造函数被调用了三次,所以Enum中定义的构造函数中的打印代码被执行了3遍
第八十一题
for 循环的执行顺序
1 | public class Print{ |
最后的运行结果是:ABDCBDCB1
2
3
4
5
6
7
8
9
10for循环执行开始
首先执行out('A') 输出A;
然后执行out('B')&&(i<2)此时输出B,i=0,判断条件为真,执行for循环的循环体;
执行i++,out('D'),输出D i=1;
执行out('C'),输出C
然后执行out('B')&&(i<2) 此时输出B,i=1 判断条件为真 ,执行for循环的循环体;
执行i++,out('D'),输出D i=2;
执行out('C'),输出C
然后执行out('B')&&(i<2) 此时输出B,i=2,不满足i<2 判断条件为假 ,跳出循环;
所以结果为ABDCBDCB
第八十二题
010 八进制 8
0x8 十六进制 8
第八十三题
1 | public class StringDemo{ |
运行结果:
这题是在考编译器的优化,hotspot中 编译时”tao”+”bao”将直接变成”taobao”,b+c则不会优化,因为不知道在之前的步骤中bc会不会发生改变,而针对b+c则是用语法糖,新建一个StringBuilder来处理
第八十四题
父类没有无参的构造函数,所以子类需要在自己的构造函数中显式调用
父类的构造函数
第八十五题
1 | String x="fmn"; |
解析:
String x=”fmn”; “fmn”是在常量池里的不可变对象。
x.toUpperCase(); 在堆中new一个”FMN”对象,但无任何引用指向它。
String y=x.replace(‘f’,’F’); 在堆中 new一个”Fmn”对象,y指向它。
y=y+”wxy”; 在堆中 重新new一个”Fmnwxy”对象, 修改y指向,现在y指向它。
第八十六题
数组不是基本类型,在java中,数据类型就分为基本数据类型(即原生类)
和引用数据
类型,所以数组不是原生类。
第八十七题
序列化相关
一、序列化使用场景
对象的序列化:目的:将一个具体的对象进行持久化,写入到硬盘上。(注意:静态数据不能被序列化,因为静态数据不在堆内存中,而是在静态方法区中)
Serializable:用于启动对象的序列化功能,可以强制让指定类具备序列化功能,该接口中没有成员,这是一个标记接口。这个标记接口用于给序列化类提供UID。这个uid是依据类中的成员的数字签名进行运行获取的。如果不需要自动获取一个uid,可以在类中,手动指定一个名称为serialVersionUID id号。依据编译器的不同,或者对信息的高度敏感性。最好每一个序列化的类都进行手动显示的UID的指定。
二、非序列化使用场景
如何将非静态的数据不进行序列化?用transient 关键字修饰此变量即可。使用场景:为了安全起见,有时候我们不需要在网络间
传输一些数据(如身份证号码,密码,银行卡号等)
java 的transient
关键字为我们提供了便利,你只需要实现Serilizable
接口,将不需要序列化
的属性前添加关键字transient
,序列化对象的时候,这个属性就不会序列化到指定的目的地中。
第八十八题
标识符是以字母开头的字母数字序列:
数字是指0~9,字母指大小写英文字母、下划线(_)和美元符号($)
,也可以是Unicode字符集中的字符
,如汉
字;
字母、数字等字符的任意组合,不能包含+、- *
等字符;
不能使用关键字;
大小写敏感
第八十九题
动态 INCLUDE 用 jsp:include 动作实现 <jsp:include page=”included.jsp” flush=”true” /> 它总是会检查所含文件中的变化 , 适合用于包含动态页面 , 并且可以带参数。各个文件分别先编译,然后组合成一个文件。
静态 INCLUDE 用 include 伪码实现 , 定不会检查所含文件的变化 , 适用于包含静态页面 <%@ include file=”included.htm” %> 。先将文件的代码被原封不动地加入到了主页面从而合成一个文件,然后再进行翻译,此时不允许有相同的变量。
以下是对 include 两种用法的区别 , 主要有两个方面的不同 ;1
2
3
4
5
6
7一 : 执行时间上 :
<%@ include file="relativeURI"%> 是在翻译阶段执行
<jsp:include page="relativeURI" flush="true" /> 在请求处理阶段执行 .
二 : 引入内容的不同 :
<%@ include file="relativeURI"%>
引入静态文本 (html,jsp), 在 JSP 页面被转化成 servlet 之前和它融和到一起 .
<jsp:include page="relativeURI" flush="true" /> 引入执行页面或 servlet 所生成的应答文本 .
第九十题
1 | public class Demo { |
第九十一题
InputStreamReader(InputStream in, Charset cs)
创建使用给定字符集的 InputStreamReader。
第九十二题
Servlet 与 CGI 的比较
和CGI程序一样,Servlet可以响应用户的指令(提交一个FORM等等),也可以象CGI程序一样,收集用户表单的信息并给予动态反馈(简单的注册信息录入和检查错误)。
然而,Servlet的机制并不仅仅是这样简单的与用户表单进行交互。传统技术中,动态的网页建立和显示都是通过CGI来实现的,但是,有了Servlet,您可以大胆的放弃所有CGI(perl?php?甚至asp!),利用Servlet代替CGI,进行程序编写。
当用户浏览器发出一个Http/CGI的请求,或者说 调用一个CGI程序的时候,服务器端就要新启用一个进程 (而且是每次都要调用),调用CGI程序越多(特别是访问量高的时候),就要消耗系统越多的处理时间,只剩下越来越少的系统资源,对于用户来说,只能是漫长的等待服务器端的返回页面了,这对于电子商务激烈发展的今天来说,不能不说是一种技术上的遗憾。
而Servlet充分发挥了服务器端的资源并高效的利用。每次调用Servlet时并不是新启用一个进程 ,而是在一个Web服务器的进程敏感词享和分离线程,而线程最大的好处在于可以共享一个数据源,使系统资源被有效利用。传统的CGI程序,不具备平台无关性特征,系统环境发生变化,CGI程序就要瘫痪,而Servlet具备Java的平台无关性,在系统开发过程中保持了系统的可扩展性、高效性。
传统技术中,一般大都为二层的系统架构,即Web服务器+数据库服务器,导致网站访问量大的时候,无法克服CGI程序与数据库建立连接时速度慢的瓶颈,从而死机、数据库死锁现象频繁发生。而我们的Servlet有连接池的概念,它可以利用多线程的优点,在系统缓存中事先建立好若干与数据库的连接,到时候若想和数据库打交道可以随时跟系统”要”一个连接即可,反应速度可想而知。
第九十四题
- 标准ASCII只使用7个bit,扩展的ASCII使用8个bit。
- ANSI通常使用 0x00~0x7f 范围的1 个字节来表示 1 个英文字符。超出此范围的使用0x80~0xFFFF来编码,即扩展的ASCII编码。不同 ANSI 编码之间互不兼容。在简体中文Windows操作系统中,ANSI 编码代表 GBK 编码;在繁体中文Windows操作系统中,ANSI编码代表Big5;在日文Windows操作系统中,ANSI 编码代表 Shift_JIS 编码。
- ANSI通常使用 0x00~0x7f 范围的1 个字节来表示 1 个英文字符,即ASCII码
- ASCII码包含一些特殊空字符
第九十五题
- 在Java中,
先继承再实现
- java 的字符类型采用的是
Unicode
编码方案,每个Unicode
码占用16
个比特位。 - HashMap中改成
containsKey
和containsValue
方法来替换contains
方法。 - File类是java中
文件和目录路径名的抽象表示形式
。Java中对文件进行读写操作的基本类是IO类
。 - 有一个源代码,只包含import java.util.* ; 这一个import语句,我们
不能访问 util 子目录下的类
。 - DBMS 中实现事务持久性的子系统是
恢复管理子系统
- ceil:大于等于 x,并且与它最接近的数。floor:小于等于 x,且与 x 最接近的数。
- java的访问权限有public、protected、private和default的,default不能修饰变量。
- 普通变量不能用 abstract 修饰,abstract 一般修饰类和方法。
数值型变量在默认情况下为
int
型,byte和short型在计算时会自动转换为int型
计算,结果也是int 型。所以 a1*a2 的结果是 int 型的。1
2
3
41. byte a1 = 2, a2 = 4, a3;
2. short s = 16;
3. a2 = s;
4. a3 = a1 * a2;stream结尾
都是字节流
,reader和writer结尾
都是字符流
两者的区别就是读写的时候一个是按字节读写,一个是按字符。 实际使用通常差不多。 在读写文件需要对内容按行处理,比如比较特定字符,处理某一行数据的时候一般会选择字符流。 只是读写文件,和文件内容无关的,一般选择字节流。- Java 并发库 的Semaphore 可以很轻松完成信号量控制,Semaphore可以控制某个资源可被同时访问的个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。
- CyclicBarrier 主要的方法就是一个:await()。await() 方法没被调用一次,计数便会减少1,并阻塞住当前线程。当计数减至0时,阻塞解除,所有在此 CyclicBarrier 上面阻塞的线程开始运行。
- 直译过来就是倒计数(CountDown)门闩(Latch)。倒计数不用说,门闩的意思顾名思义就是阻止前进。在这里就是指 CountDownLatch.await() 方法在倒计数为0之前会阻塞当前线程。
- Counter不是并发编程的同步器
- 类中,可以有main方法,也可以没有main方法,而有一个main()方法的时候,也可以是
任意访问权限
。因为这个类不一定要执行,可以只是辅助类。 - 在java 中,声明一个数组时,
不能直接
限定数组长度,只有在创建实例化对象
时,才能给定数组长度
。 boolean类型
不能和任何类型
进行转换,会报出类型异常错误
。1
2
3
4如果两个操作数其中有一个是double类型,另一个操作就会转换为double类型。
否则,如果其中一个操作数是float类型,另一个将会转换为float类型。
否则,如果其中一个操作数是long类型,另一个会转换为long类型。
否则,两个操作数都转换为int类型。多线程是Java程序的并发机制,它能同步共享数、处理不同的事件。
- if的语句比较,
除boolean外
的其他类型都不能使用赋值语句,否则会提示无法转成布尔值。 - 管道为空,读操作会被阻塞;管道满了,写操作会被阻塞,管道是内存中的,匿名管道只能单向;命名管道可以双向,可以有多个进程对其读;也可以有多个进程写,只不过不能同时写
- 会产生信息丢失不如说丢失精度,这样可能更容易明白,而精度丢失只会发生在从
大范围到小范围的转换
,比如说 int 转换成 double 或 float。 1
2
3
41. 普通管道(PIPE):通常有两种限制,一是单工,即只能单向传输;二是血缘,即常用于父子进程间(或有血缘关系的进程间)。
2. 流管道(s_pipe):去除了上述的第一种限制,实现了双向传输。
3. 命名管道(name_pipe):去除了上述的第二种限制,实现了无血缘关系的不同进程间通信。
显然,要求是对于不同的服务器之间的通信,是要要求全双工形式的,而管道只能是半双工,虽然可以双向,但是同一时间只能有一个方向传输Java 技术允许使用
finalize()
方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在 Object 类中定义的,因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。finalize() 方法是在垃圾收集器删除对象之前
对这个对象调用的。注意:finalize不一定
被jvm调用,只有当垃圾回收器要清除垃圾时
才被调用。1
2
3
4
5
6
71. 尽量使用many-to-one,避免使用单项one-to-many
2. 灵活使用单向one-to-many
3. 不用一对一,使用多对一代替一对一
4. 配置对象缓存,不使用集合缓存
5. 一对多使用Bag 多对一使用Set
6. 继承使用显示多态 HQL:from object polymorphism="exlicit" 避免查处所有对象
7. 消除大表,使用二级缓存局部内部类前不能用修饰符public和private,protected
- 解决哈希冲突常用的两种方法是:
开放定址法
和链地址法
- 存根(Stub)与
动态链接
有关 - 泛型仅仅是java的一颗语法糖,它不会影响java虚拟机生成的汇编代码,在编译阶段,虚拟机就会把泛型的类型擦除,还原成没有泛型的代码,顶多
编译速度
稍微慢一些,执行速度
是完全没有什么区别的
。 - wait后进入等待锁定池,只有针对此对象发出notify方法后获得对象锁
进入就绪状态
,不是运行状态
1
2
3
4
5
6-Xmx:最大堆大小
-Xms:初始堆大小
-Xmn:年轻代大小
-XXSurvivorRatio:年轻代中Eden区与Survivor区的大小比值
年轻代5120m, Eden:Survivor=3,Survivor区大小=1024m(Survivor区有两个,即将年轻代分为5份,每个Survivor区占一份),总大小为2048m。
-Xms初始堆大小即最小内存值为10240mjava 中的数据类型分为
基本数据
和引用数据类型
,基本数据类型包括数值型
(整数类型:int、short、long、byte,浮点类型:double、float)、布尔型
(true)、字符型
(char),引用数据类型包括类、接口、数组
。1
2
3
4
5
6
7
8
9在java.util包中提供了一些集合类,常用的有List、Set和Map类,其中List类和Set类继承了Collection接口。
这些集合类又称为容器,长度是可变的,数组用来存放基本数据类型的数据,集合用来存放类对象的引用。
List接口、Set接口、Map接口以及Collection接口的主要特征如下:
Collection接口是List接口和Set接口的父接口,通常情况下不被直接使用。
List接口继承了Collection接口,List接口允许存放重复的对象,排序方式为按照对象的插入顺序。
Set接口继承了Collection接口,Set接口不允许存放重复的对象,排序方式为按照自身内部的排序规则。
Map接口以键值对(key—value)的形式存放对象,其中键(key)对象不可以重复,值(value)对象可以重复,排序方式为按照自身内部的规则。
C:Vector实现了List接口,即间接实现Collection接口
D:Iterator是Java迭代器最简单的实现,没有实现Collection接口1、三目运算是右结合的。2、&不短路,&&短路。
1
2
3
4
5
6基本类型数组:
byte[],short[],int[] ,默认值为0,
boolean[]默认值为false
float[],double[],默认值为0.0
对象类型数组:
默认值为null
37.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18EJB容器:Enterprise java bean 容器。更具有行业领域特色。他提供给运行在其中的组件EJB各种管理功能。
只要满足J2EE规范的EJB放入该容器,马上就会被容器进行高效率的管理。并且可以通过现成的接口来获得系统级别的服务。例如邮件服务、事务管理。
JNDI:(Java Naming & Directory Interface)JAVA命名目录服务。主要提供的功能是:提供一个目录系,让其它各地的应用程序在其上面留下自己的索引,从而满足快速查找和定位分布式应用程序的功能。
JMS:(Java Message Service)JAVA消息服务。主要实现各个应用程序之间的通讯。包括点对点和广播。
JTA:(Java Transaction API)JAVA事务服务。提供各种分布式事务服务。应用程序只需调用其提供的接口即可。
JAF:(Java Action FrameWork)JAVA安全认证框架。提供一些安全控制方面的框架。让开发者通过各种部署和自定义实现自己的个性安全控制策略。
RMI/IIOP:(Remote Method Invocation /internet对象请求中介协议)他们主要用于通过远程调用服务。
例如,远程有一台计算机上运行一个程序,它提供股票分析服务,我们可以在本地计算机上实现对其直接调用。当然这是要通过一定的规范才能在异构的系统之间进行通信。RMI是JAVA特有的。
- 局部内部类 和局部变量一样不能用访问权限修饰符修饰。
1
后台线程:指为其他线程提供服务的线程,也称为守护线程。JVM的垃圾回收线程就是一个后台线程。 前台线程:是指接受后台线程服务的线程,其实前台后台线程是联系在一起,就像傀儡和幕后操纵者一样的关系。傀儡是前台线程、幕后操纵者是后台线程。由前台线程创建的线程默认也是前台线程。可以通过isDaemon()和setDaemon()方法来判断和设置一个线程是否为后台线程。
同一个类的对象使用不同的内存段,但静态成员共享相同的内存空间
- java 中的接口是
多继承
的,类是单继承
的。 1
2
3
4
5System.out.println(1+"10"+3+"2");//11032
System.out.println(1+2+"10"+3+"2");//31032
System.out.println(1+"10"+3+1+"2");//110312
System.out.println(1 + "10" + (3 + 1) + 1); // 11041
在遇到string类型之前,int间使用 "+" 还是表示数值的相加,但是遇到第一个string后,后面就都是按string类型来了,变成字符串的拼接Log4j的日志打印级别
不可以
在运行时重新设置1
2
3
4
51. CopyOnWriteArrayList适用于写少读多的并发场景
2. ReadWriteLock即为读写锁,他要求写与写之间互斥,读与写之间互斥,
读与读之间可以并发执行。在读多写少的情况下可以提高效率
3. ConcurrentHashMap是同步的HashMap,读写都加锁
4. volatile只保证多线程操作的可见性,不保证原子性String是个不可继承类(final修饰),也是个不可变类(内部char数组被final修饰)。
StringBuffer和StringBuilder内部都是一般的动态数组
,所以可变。前者是线程安全的,因为方法基本都被synchronized修饰了- 一个.java文件中,可以有
多个类
,包括内部类和外部类
。考虑到内部类的原因,一个.java文件可以中可以有多个public
类。
但是对于外部类
而言,一个.java文件必须只能有一个public类
,同时这个类的类名必须和.java的文件名一致(包括大小写)
。 - java继承中对构造函数是不继承的,只是显式或者隐式调用
1
2
3
4
5
6
7vector:就比arraylist多了个同步化机制(线程安全),因为效率较低,现在已经不太建议使用。
在web应用中,特别是前台页面,往往效率(页面响应速度)是优先考虑的。
statck:堆栈类,先进后出
hashtable:就比hashmap多了个线程安全
enumeration:枚举,相当于迭代器1
2
3
4
5
6
7
8
9out->response.getWriter
request ->Service方法中的req参数
response ->Service方法中的resp参数
session ->request.getSession
application ->getServletContext
exception ->Throwable
page ->this
pageContext ->PageContext
Config ->getServletConfig数组元素在内存中是一个接着一个线性存放的,通过第一个元素就能访问随后的元素,避免了
数据覆盖
的可能性,和数据类型覆盖
并没有关系。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class Enclosingone {
//非静态内部类
public class InsideOne {}
//静态内部类
public static class InsideTwo{}
}
class Mytest02{
public static void main(String args []){
Enclosingone.InsideOne obj1 = new Enclosingone().new InsideOne();//非静态内部类对象
Enclosingone.InsideTwo obj2 = new Enclosingone.InsideTwo();//静态内部类对象
}
}
内部类其实和类的属性没什么区别,只是在声明的时候必须是Outer.Inner a
就像int a 一样,至于静态内部类和非静态内部类new的时候有点区别
Outer.Inner a=new Outer().new Inner()(非静态,先有Outer对象才能有属性)
Outer.Inner a=new Outer.Inner()要把Outer.Inner看成一部分,就像类变量一样不能用 this 来访问静态变量。
- 设为x进制,用十进制表示方法13就是
1*x^1+3*x^0=x+3
- Object 中有以下方法
getClass(), hashCode(), equals(), clone(), toString(), notify(), notifyAll(), wait(), finalize()
1
2
3use-a 是依赖关系
has-a 一般是组合关系
is-a 一般是继承关系