1
:JDK与JRE
JDK:JAVA Development Kit, java开发工具包; 包括各种类库和工具,当然也包括JRE
JRE:JAVA Runtime Environment,java程序运行环境,包括JAVA类库的
class
文件和JVM
2
:JAVA_HOME PATH CLASSPATH
JAVA_HOME :JDK的安装目录,很多web服务器如tomcat没有内置JDK,它们通过JAVA_HOME找到JDK
PATH:在原有值后加“;%JAVA_HOME%\bin”;通过配置PATH,可以再任何命令提示符窗口中使用JAVAC、JAVA等命令了
CLASSPATH:用来指定JAVA程序搜索类的路径的,JAVA程序在编译和运行时,先搜索jre/lib/rt.jar中的类,然后搜索CLASSPATH中指定的类;一般CLASSPATH会包括当前目录“.”
3
:JAVA程序动态的指定类搜索路径方法
利用-cp或-classpath选项,如
javac –cp D:\wrok\log4j.jar Hello.java (编译时指定D:\wrok\log4j.jar为搜索路径)
java –cp D:\wrok\log4j.jar Hello
4
:JAVA和C++程序在编译及运行上的区别
C,C++这类语言的编译器(例如 UNIX下的CC命令,WINDOWS下的CL命令)都是把源代码直接编译成计算机可以认识的机器码,如EXE、DLL之类的文件,然后直接运行。
JAVA为了实现跨平台,多了一个中间步骤,就是先生成字节码文件,javac命令先把源文件编译成计算机无法直接识别的
class
文件,但是它可以被JVM所认识,JVM有多个平台版本,因此可以在多个平台执行。
5
:什么是JVM及其工作原理
JVM是一个想象中的机器,在实际的计算机上通过软件模拟来实现。Java虚拟机有自己想象中的硬件,如处理器、堆栈、寄存器,还有相应的指令系统。JVM在执行字节码时,把字节码解释成具体平台上的机器指令执行。
6
:JAVA垃圾回收机制
JVM中栈存放的是非
static
的自动变量、函数参数、表达式的临时结果和函数返回值。栈中这些实体数据的分配和释放均是由系统自动完成的。
堆中存放的实体数据是程序员显示分配的(
new
),没有自动垃圾回收机制的系统中(有些JVM没有垃圾回收机制)必须显示的释放这些实体
C、C++中也有栈和堆,对堆的管理,C是通过malloc()和free();而C++是通过
new
和delete
Object对象中有个finalize(),它会在对象被回收之前被调用;
System.gc();和Runtime.getRunime().gc()两个方法都可以显示请求开始垃圾回收线程
7
:jar和war
两者都是java可执行文件,jar是对于桌面应用程序,war是对于web应用程序;
Jar和war打包都通过JDK的jar命令
8
:JAVA变量及作用范围
分为:静态变量、成员变量、局部变量
静态变量在类中用
static
修饰,生存周期由类决定;成员变量是类中没有用
static
修饰的变量,生存周期有对象来决定;局部变量是定义在方法里的变量、方法的参数、代码块里的变量,它们的作用范围用大括号{}来界定
9
:JAVA变量分为哪两种大的数据类型
基本数据类型和引用数据类型 ,它们最大的区别在于引用数据类型存放的是数据所在的地址,而基本数据类型直接存放数据的值;二者都保存在栈中
10
:装箱、拆箱指的是基本基本数据类型和包装类型的自动相互转化
11
:C++指针和JAVA引用的区别
相同:都是指向一块内存地址
不同:
一 类型转换:引用的类型转换,可能抛出java.lang.ClassCastException,引用对应的类型不同的话,转换不成功;C++指针则一定能转换成功,指向哪儿,还是一个地址
二 初始值:引用类型的初始值为
null
;C++指针是
int
,如不初始化,值是随机的
三 计算:引用不可计算;指针可计算,如++或—
四 内存泄露:JAVA引用基本不会产生内存泄露;指针容易,程序员须及时回收
五 作为参数:JAVA方法本质上只有传值,引用作为参数使用时,回给函数内引用的COPY,所以在函数内交换两个引用参数是没意义的,但改变一个引用参数的属性是有意义的,因为引用参数的COPY所引用的对象和引用参数是同一个对象;
C++指针作为参数,实际上就是它所指的地址在函数中被操作。
12
:equals()和==
==运用在基本数据类型是比较的是实际的值;用于比较引用类型时候,比较两个引用的地址是否相同;(都是比较栈中数据)
Object有equals()方法,默认的比较方式与==相同,String类重写了该方法,使其能比较字符串的内容;
13
:JAVA三元运算符
表达式
1
?表达式
2
:表达式
3
表达式
1
为
true
则执行表达式
2
;否则执行表达式
3
14
:Java注释类型
①行注释
②块注释
return
(p1.getName()).compareTo(p2.getName());
}
public
class
DemoPerson {
public
static
void
main(String[] args) {
Person p1 =
new
Person(
"c"
,
24
);
Person p2 =
new
Person(
"b"
,
8
);
Person p3 =
new
Person(
"a"
,
34
);
List<Person> list =
new
ArrayList<Person>();
list.add(p1);
list.add(p2);
list.add(p3);
for
(Person p:list){
System.out.println(p.getName()+
":"
+p.getAge());
}
System.out.println(
"按年龄排序之后"
);
Collections.sort(list,
new
CompareByAge());
for
(Person p:list){
System.out.println(p.getName()+
":"
+p.getAge());
}
}
示例
2
:
public
class
Dog
implements
Comparable{
private
String name;
private
int
age;
public
Dog(String name,
int
age){
this
.name = name;
this
.age = age;
}
public
int
compareTo(Object o) {
Dog d =
null
;
if
(o
instanceof
Dog){
d = (Dog)o;
}
if
(
this
.getAge()>d.getAge()){
return
1
;
}
if
(
this
.getAge()<d.getAge()){
return
-
1
;
}
return
0
;
}
}
public
class
DemoDog {
public
static
void
main(String[] args) {
Dog d1 =
new
Dog(
"c"
,
24
);
Dog d2 =
new
Dog(
"b"
,
8
);
Dog d3 =
new
Dog(
"a"
,
34
);
List<Dog> list =
new
ArrayList<Dog>();
list.add(d1);
list.add(d2);
list.add(d3);
for
(Dog d:list){
System.out.println(d.getName()+
":"
+d.getAge());
}
System.out.println(
"按年龄排序之后"
);
Collections.sort(list);
for
(Dog d:list){
System.out.println(d.getName()+
":"
+d.getAge());
}
}
}
34
:Vector和ArrayList
Verctor的大多数成员方法都会加上
synchronized
关键字,也就是说Vector是线程安全的;也正因如此,它的执行效率没ArrayList高;通常建议使用ArrayList
35
:HashTable和HashMap的区别
① HashTable是线程安全的,HashMap不是
② HashTable不允许
null
值(key和value都不可以),HashMap可以
③ HashTable有个contains()方法,功能和HashMap的containsValue()一样
④ HashTable使用Enumeration遍历,HashMap使用Iterator
36
:符合什么条件的数据集合可以使用foreach循环
从JDK5开始可以使用foreach代替迭代器,从语法上讲,数组或者实现了Iterable接口的类实例,都可以用foreach循环。
实例
1
:(迭代器模式方法)
public
class
Person {
private
String name;
private
int
age;
public
Person(String name,
int
age){
this
.name = name;
this
.age = age;
}
………setters and getters…
}
public
class
Persons
implements
Iterable<Person>{
List<Person>personlist =
new
ArrayList<Person>();
public
Iterator<Person> iterator() {
PersonIterate<Person> pt =
new
PersonIterate<Person>();
pt.setPersonList(personlist);
return
pt;
}
public
void
add(Person p){
personlist.add(p);
}
}
public
class
PersonIterate<Person>
implements
Iterator<Person>{
List<Person>personlist =
new
ArrayList<Person>();
private
int
index =
0
;
public
void
setPersonList(List<Person>personlist){
this
.personlist = personlist;
}
public
boolean
hasNext() {
return
personlist.size()>index;
}
public
Person next() {
return
personlist.get(index++);
}
public
void
remove() {
personlist.remove(index);
}
}
这样下面的foreach就能成功;
Persons persons =
new
Persons();
persons.add(
new
Person(
"a"
,
1
));
persons.add(
new
Person(
"b"
,
2
));
persons.add(
new
Person(
"c"
,
3
));
for
(Person p:persons){
System.out.println(p.getName()+
":"
+p.getAge());
}
实例
2
:(内部类方法)
public
class
Persons2
implements
Iterable<Person>{
List<Person>personlist =
new
ArrayList<Person>();
public
void
add(Person p){
personlist.add(p);
}
public
Iterator<Person> iterator() {
return
new
Iterator<Person>(){
private
int
index=
0
;
public
boolean
hasNext() {
return
personlist.size()>index;
}
public
Person next() {
return
personlist.get(index++);
}
public
void
remove() {
personlist.remove(index);
}
};
}
}
这样也能进行foreach循环了
说明:Persons也可直接实现Iterator接口,并实现其hasNext(),next()等方法,但是这样的话Persons必须维护一个索引index,其index值是不确定的,如进行循环一次,index变为a,紧接着进行第二次循环遍历会得到空结果;
方法
1
和方法
2
每次进行循环迭代都将产生一个新的索引为
0
;
37
:目录和文件操作
Java提供了java.io.File类对目录和文件进行操作,主要操作方法包括:路径字符串的构造方法、isDirectory、isFile、createNewFile、list(返回文件数组)、getName、delete、getLastModify(返回最近修改时间)、listFile(返回文件名数组)等
38
:随机存取文件RandomAccessFile类
主要方法包括
new
RandomAccessFile(“路径”,”rw|r|w…”);
length()方法获得文件内容长度
seek()定位
read()获取当前位置数据
write()写数据
close()关闭打开的文件
示例:(将文件中所有字母a替换成c)
RandomAccessFile raf =
new
RandomAccessFile(
"d:/1.txt"
,
"rw"
);
int
len = (
int
) raf.length();
for
(
int
i=
0
;i<len;i++){
byte
b = (
byte
) raf.read();
char
c = (
char
)b;
if
(c==
'a'
){
raf.seek(i);
raf.write(
'c'
);
}
}
raf.close();
38
:字节流和字符流
字节流处理的是计算机最基本的单位
byte
,它可以处理任何数据格式的数据。主要的操作对象是
byte
数组,通过read()和write()方法把
byte
数组的数据写入或读出
字符流有字节流包装而来,字符流创建时,一般要包装一个字节流;inputStreamReader提供字节流向字符流的转换,其构造函数中可以指定字符编码格式;
读取或输出文本一般用BufferedReader.readLine()和PrintWriter.println()方法;
PrintWriter和BufferedWriter同样提供可缓冲输出,但是PrintWriter可设置自动清除缓冲,不需要pw.flush();
PrintWriter pw =
new
PrintWriter(OutputStreamWirter(“d.txt”,”UTF-
8
”),
true
);
注意:①写时需要flush() ,PrintWriter可设定自动清除
②别忘了关闭流
39
:序列化
Java可以通过序列化保存或传输一个对象。序列化本质上是把数据,变成一系列的字节数据。然后把这些字节数据写入到流中。
java.io.Serializable接口是可以进行序列化的类的标志性接口,该接口本身没有任何需要实现的方法,仅提供一个能唯一辨别一个类的serialVersionUID.作用是在序列化和反序列化过程中,起到辨别作用。在反序列过程中,若有两个相同类名的类,就通过serialVersionUID来判断到底反序列化成哪个类。
序列化:ObjectInputStream.readObject()
反序列化:ObjectOutputStream.writeObject()
40
:进程与线程区别
① 线程隶属于某个进程
② 进程能独占资源,而线程不能
③ 线程是调度和分配的基本单位,进程是拥有资源的基本单位
④ 进程间通信困难,而线程间共享一块内存区域,通信方便
⑤ 创建销毁进程及进程切换系统开销大,而对于线程开销小
41
:Runnable接口和Thread类的区别
两者都可以实现多线程编程,区别如下:
① 通过继承Thread方法定义线程就不能继承其他类,而实现Runnable接口方法可以
② Thread方法为“多线程多实例”;Runnable:当多个线程公用一个Runnable时为“多线程单实例”,这时可以共享一个变量,但可能有线程安全问题;当每个线程都有各自的Runnable,则为“多线程多实例”
③ Runnable可以方便的访问同一变量,而Thread需要用内部类实现。
ThreadLocal: 线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。一个ThreadLocal只能存放一个专有对象,若线程需要多个专有对象的话,必须设计多个ThreadLocal。ThreadLocal主要有两个方法set(Object)
和 get()
42
:线程安全问题是指两方面的问题:变量安全和线程同步(见深入JDK206页)
解决变量安全的方法:
1
:多线程多实例
2
:变量定义在方法中
3
:使用ThreadLocal
解决线程同步问题用
synchronized
(监视器) 监视器默认为
this
;
(单实例,多线程中多个Thread基于同一个Runnable创建,Runnable中的变量为多个
Thread共享,所以存在变量安全问题)
ThreadLocal示例:(每个线程都实现从
1
~
10
累加)
public
class
Accumulator
implements
Runnable{
ThreadLocal threadLocal =
new
ThreadLocal();
public
void
run() {
for
(
int
i=
1
;i<=
10
;i++){
if
(threadLocal.get()==
null
){
threadLocal.set(
new
Integer(
0
));
}
int
x = (Integer) threadLocal.get();
x = x+i;
threadLocal.set(x);
try
{
Thread.sleep(
1000
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+
"\t累加和="
+threadLocal.get());
}
}
}
public
class
Main {
public
static
void
main(String[] args) {
Accumulator a =
new
Accumulator();
Thread t1 =
new
Thread(a);
Thread t2 =
new
Thread(a);
t1.start();
t2.start();
}
}
说明:单实例多线程,假如不用ThreadLocal方法,在Accumulator中定义一个成员变量SUM保存每次累加和,则线程共享Runnable的变量SUM,会有变量安全问题;若改为多实例多线程则没有这问题;若将SUM定义在run()方法中也没有这样的问题;
43
:同步锁Synchronized(括号内为可能发生同步冲突的监视器,默认为
this
)
当线程执行到Sychronized时候,检查传入的监视器对象,并得到该对象的同步锁,如果得不到(被占用),就会被加入到一个与该监视器对象相关联的等待线程池中,一直等待同步锁被释放,池中的等待线程就会得到该同步锁,然后继续执行下去,当线程执行完同步代码块,就会自动释放它占的同步锁。
44
:协调运行—线程通信
wait()方法:使当前调用同步方法的线程等待,直到其他线程调用notify()或notifyAll()
notify():唤醒当前同步监视器(sychronized的参数)上正在等待的单个线程,如果有多个,随机唤醒一个。
notifyAll():唤醒当前同步监视器(sychronized的参数)上正在等待的所有线程
示例:(实现生产者消费者模式)
public
class
Store {
private
final
int
MAX_SIZE;
private
int
count;
public
Store(
int
size,
int
c){
MAX_SIZE = size;
count = c;
}
synchronized
public
void
add(){
if
(count>=MAX_SIZE){
System.out.println(
"仓库满了"
);
try
{
wait();
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
count++;
System.out.println(
"put "
+count);
notifyAll();
}
synchronized
public
void
get(){
if
(count<=
0
){
System.out.println(
"仓库空了"
);
try
{
wait();
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(
"get "
+count);
count--;
notifyAll();
}
}
public
class
Producer
extends
Thread{
private
Store s;
public
Producer(Store s){
this
.s = s;
}
public
void
run(){
while
(
true
){
s.add();
try
{
Thread.sleep(
500
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
}
}
public
class
Consumer
extends
Thread{
private
Store s;
public
Consumer(Store s){
this
.s = s;
}
public
void
run(){
while
(
true
){
s.get();
try
{
Thread.sleep(
500
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
}
}
public
class
Main {
public
static
void
main(String[] args) {
Store s =
new
Store(
5
,
0
);
Producer producer1 =
new
Producer(s);
Producer producer2 =
new
Producer(s);
Consumer consumer1 =
new
Consumer(s);
Consumer consumer2 =
new
Consumer(s);
producer1.start();
producer2.start();
consumer1.start();
consumer2.start();
}
}
45
:JAVA实现线程池
思想:启动一个线程的开销是相当高的,利用线程池,程序向线程池传递一个实现Runnable接口的类的实例时,程序就会从线程池中启动一个空闲线程,并执行run()方法,run方法执行结束后,该线程又成为空闲线程,回到线程池,等待下一个Runnable对象,并执行对象的run()方法,从而大大提高了系统的性能。
通过java.util.concurrent包中的Executors类可以创建线程池对象,即创建线程
池类ExecutorService的实例。
示例:
public
class
Thread1
implements
Runnable{
public
void
run() {
System.out.println(
"this is thread1"
);
}
}
public
class
Thread2
implements
Runnable{
public
void
run(){
System.out.println(
"this is thread2"
);
}
}
import
java.util.concurrent.ExecutorService;
import
java.util.concurrent.Executors;
public
class
Main {
public
static
void
main(String[] args) {
ExecutorService es = Executors.newFixedThreadPool(
5
);
es.submit(
new
Thread1());
es.submit(
new
Thread2());
es.shutdown();
}
}
46
:反射的原理:反射是为了能够动态的加载一个类,动态地调用一个方法,动态地访问
一个属等动态要求而设计的。它的出发点在于JVM会为每个类创建一个java.lang.Class类的实例,通过该对象可以获取这个类的信息,然后通过使用java.lang.reflect包下的API以达到各种动态要求。
类在以下情况下会被加载:
① 需要使用该类创建对象。如Student s =
new
Student() Student会被加载
② 访问该类的静态成员。 如system.out.println(Calendar.MONDAY);
③ 使用Class类的forName()方法
类一旦被加载,JVM就会为其创建一个Class对象,如何得到一个类的Class对象?
① Class.forName()返回就是一个Class对象
② 类名.
class
③ 对象名.getClass()
47
:Field、Method、Construtor
Field通过Class类对象的getDeclaredField()和getDeclaredFields()方法得到;Field方法主要分为两大类:setXXX(object,value)和getXXX(object)其中object为实例对象。
Method通过Class类对象的getMethod()或getMethods()获得,Method最常用的方法是invoke(object,参数…)。
Constructor类通过Class类对象的getConstructor(参数类型.
class
…)获得。Constructor类最常用的方法是newInstance(),通过构造函数创建实例(Class对象也能通过newInstance()创建实例,但该类中提供无参构造函数)
获得一个类中私有变量:调用Field的setAccessible(
true
)
示例:
public
class
Person {
public
String name;
private
int
age;
public
Person(){}
public
Person(String name,
int
age){
this
.name = name;
this
.age = age;
}
public
void
getInfo(){
System.out.println(name+
":"
+age);
}
}
public
class
Main {
public
static
void
main(String[] args) {
Class personclass = Person.
class
;
try
{
Constructor con = personclass.getConstructor(String.
class
,
int
.
class
);
Person person = (Person) con.newInstance(
"li"
,
20
);
Field f = personclass.getDeclaredField(
"name"
);
String name = (String) f.get(person);
Field f2 = personclass.getDeclaredField(
"age"
);
f2.setAccessible(
true
);
int
age = (Integer) f2.get(person);
System.out.println(name+
":"
+age);
Method m = personclass.getDeclaredMethod(
"getInfo"
,
null
);
m.invoke(person,
null
);
}
catch
(Exception e) {
e.printStackTrace();
}
}
}
48
:TCP通信特点
① 面向连接的传输
② 端到端的通信
③ 可靠性,确保传输数据的正确性,不出现丢失或乱序
④ 采用字节流方式,即以字节为单位传输字节序列
编程模型:
服务器端:①创建ServerSocket
②accept()等待客户请求
③获得输入流和输出流,并进行传输
④释放资源,关闭输入流输出流、Socket、ServerSocket
49
:UDP通信特点
① UDP是一个无连接协议,当它想传送数据时就简单地抓取来自应用程序的数据,并尽可能快地把它扔到网络上
② 不需要维护连接状态,包括收发状态等
③ 字节开销很小,传输速度快,效率高
编程模型:(点对点,没有服务器与客户端之分;分为接收端和发送端)
接收端和客户端都需要有一下步骤:
① 创建DatagramSocket,指定一个端口号.
② 提供一个
byte
数组进行数据的存储;对于发送端,还需要提供对端的IP地址和端口号。
③ 调用DatagramPacket的receive()或send()方法进行数据的接收或发送
④ 调用DatagramPacket的getData()方法得到
byte
数组的数据。
⑤ 释放资源
说明: DatagramSocket ds =
new
DatagramSocket(port);
接收:DatagramPacket dp =
new
DatagramPacket(buff,len);
ds.receive(dp);
发送: Datagrampacket dp =
new
DatagramPacket (str.getBytes() ,
0
,str.length(),InetAddress.getByName(“localhost”),sendPort);
ds.send(dp);
50
:TCP实现web服务器
public
class
WebServer {
public
static
void
main(String[] args)
throws
Exception{
ServerSocket ss =
new
ServerSocket(
80
);
Socket s =
null
;
System.out.println(
"服务器已启动!"
);
while
(
true
){
s = ss.accept();
new
HTTPThread(s).start();
}
}
}
public
class
HTTPThread
extends
Thread{
private
Socket socket;
public
HTTPThread(Socket s){
this
.socket = s;
}
public
void
run(){
try
{
OutputStream os = socket.getOutputStream();
PrintWriter pw =
new
PrintWriter(os);
pw.println(
"<HTML>"
);
pw.println(
"<body>"
);
pw.println(
"hello world"
);
pw.println(
"</body>"
);
pw.println(
"</html>"
);
pw.flush();
pw.close();
socket.close();
}
catch
(IOException e){
e.printStackTrace();
}
}
}
在浏览器输入http:
50
:UDP实现一个即时聊天软件
QQ、MSN对安全的要求不是太高,一般会采用UDP的通信模式。UDP为点对点模式,没有服务器和客户端之分,两端都创建两个线程,SendThread(发送消息)还有ReceiveThread (接收消息)。代码如下:
public
class
ReceiveThread
extends
Thread{
private
DatagramSocket ds;
public
ReceiveThread(
int
recport){
try
{
this
.ds =
new
DatagramSocket(recport);
}
catch
(SocketException e) {
e.printStackTrace();
}
}
public
void
run(){
try
{
byte
[]buff =
new
byte
[
1024
];
DatagramPacket dp =
new
DatagramPacket(buff,
1024
);
while
(
true
){
ds.receive(dp);
String str =
new
String (dp.getData(),
0
,dp.getLength());
System.out.println(
"receive:"
+str);
}
}
catch
(IOException e) {
e.printStackTrace();
}
finally
{
ds.close();
}
}
}
public
class
SendThread
extends
Thread{
private
DatagramSocket ds;
private
int
sendPort;
public
SendThread(
int
sendport,
int
otherPort){
this
.sendPort = sendPort;
try
{
this
.ds =
new
DatagramSocket(sendport);
}
catch
(SocketException e) {
e.printStackTrace();
}
}
public
void
run(){
try
{
BufferedReader br =
new
BufferedReader(
new
InputStreamReader(System.in));
String str =
null
;
while
((str = br.readLine())!=
null
){
DatagramPacket dp =
new
DatagramPacket (str.getBytes(),
0
,str.length(),
InetAddress.getByName(
"localhost"
),otherPort);
ds.send(dp);
System.out.println(
"send:"
+str);
}
}
catch
(Exception ex){
ex.printStackTrace();
}
finally
{
ds.close();
}
}
}
public
class
Chat {
public
static
void
main(String[] args) {
new
ReceiveThread(
7000
).start();
new
SendThread(
7500
,
9000
).start();
}
}
说明:在本机模拟执行时要两次运行Chat,并且设置好端口。
如第一次为:
7000
7500
9000
第二次则为:
9000
7600
7000
TCP实现聊天室思路:多线程实现,服务器维护一个socket的List,每次接收消息时遍历list将消息发送给每个客户端。
UDP实现聊天室思路:可以用UDP组播 MultiCastSocekt实现
51
:使用JAVA访问WEB站点
网络爬虫的原理就是模拟浏览器挨个访问web站点,得到站点网页的映射
JAVA也可以用编程的方式去访问网站,步骤:
① 用URL创建一个资源定位对象
② 调用URL的openConnection()得到HttpURLConnection对象
③ 调用HttpURLConnection的connect()方法打开连接
④ 用getHeaderFields()方法得到响应结果的头信息
⑤ 用getInputStream()方法得到输入流对象,得到响应内容
public
class
HttpConnTest {
public
static
void
main(String[] args)
throws
Exception{
URL url =
new
URL(
"http://www.baidu.com"
);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.connect();
Map<String,List<String>> header = conn.getHeaderFields();
for
(String key:header.keySet()){
System.out.println(key+
":"
+header.get(key));
}
BufferedReader br =
new
BufferedReader(
new
InputStreamReader(conn.getInputStream(),
"UTF-8"
));
String str =
null
;
while
((str=br.readLine())!=
null
){
System.out.println(str);
}
conn.disconnect();
}
}
52
:JDBC(java data base connectivity)工作原理
JDBC采用了一种驱动模式的设计,提供了两套的接口:开发者使用的API和数据库厂商使用的SPI(service provider
interface
数据库厂商需要实现的接口),充分体现了面向接口编程的好处。程序员无需关心具体数据库的连接和调用,只需要使用JDK中提供的标准API编程即可,而具体的实现由特定的数据库生产商提供,也就是JDBC驱动。
53
:JDBC编程步骤:
① 注册驱动程序。就是把驱动程序类加载到JVM,一般用Class.forName(“完整类名”)
② 获取数据库连接Connection conn = DriverManager.getConnection(“URL”,”用户名”,”密码”);
③ 创建会话Statement(用于向数据库发送SQL命令,并返回结果) 实际开发中用的更多的是PreparedStatement,它是一种预编译的会话,用占位符的方法效率高,且可以避免SQL注入
④ 执行SQL语句。 executeQuery()或executeUpdate()
⑤ 处理结果集。若是查询操作的话,会返回ResultSet 用其next()方法操作结果集
⑥ 关闭连接。关闭顺序ResultSet、Statement、Conection
53
:如何使用JDBC事务
事务特性:ACID(原子性、一致性、隔离性、持久性)
1
. Atomicity(原子性)
原子性很容易理解,也就是说事务里的所有操作要么全部做完,要么都不做,事务成功的条件是事务里的所有操作都成功,只要有一个操作失败,整个事务就失败,需要回滚。
2
. Consistency(一致性)
一致性也比较容易理解,也就是说数据库要一直处于一致的状态,事务开始前是一个一致状态,事务结束后是另一个一致状态,事务将数据库从一个一致状态转移到另一个一致状态。
3
. Isolation(独立性)
从字面上来说,独立性是其中最难理解的一点,但如果结合Oracle中的undo,也就不难理解了。所谓的独立性就是指并发的事务之间不会互相影响,如果一个事务要访问的数据正在被另外一个事务修改,只要另外一个事务还未提交,它所访问的数据就不受未提交事务的影响。换句话说,一个事务的影响在该事务提交前对其它事务是不可见的。
注意:这里的Isolation跟隔离级别(Isolation Level)是无关的。
4
. Durability(持久性)
持久性也不难理解,是指一旦事务提交后,它所做的修改将会永久的保存在数据库上,即使出现宕机也不会丢失。
JDBC使用事务步骤:
① 关闭自动提及事务:conn.setAutoCommit(
false
);
② 捕获(
try
catch
)执行代码,如果发生异常,在
catch
中conn.rollback()
③ 关闭连接。一般在
finally
{}中
54
:如何使用可滚动的结果集
Statement stmt = conn.createStatement(sql,type,concurrency);
其中concurrency变量用于指定是否可更新的结果集,可取以下值:
① TYPE_FORWARD_ONLY:不允许滚动,只能向前(next());
② TYPE_SCROLL_INSENSITIVE:可滚动,对数据库变化不敏感,数据库查询生产结果集后若发生变化,结果集不发生变化
③ TYPE_SCROLL_SENSITIVE:可滚动,且对数据库变化敏感
可滚动的结果集方法:
rs.next()、rs.last();
rs.absolute(
6
);
55
:如何使用可更新的结果集
当concurrency变量为以下值时:
① CONCUR_READ_ONLY:不能用于更新数据库
② CONCUR_UPDATABLE:结果集可用于更新数据库
如:
PreparedSatement pstmt = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);
rs = pstmt.executeQuery();
rs.last();
rs.updateString(
2
,”aaaa”);
rs.updateRow();
56
:什么是Servlet
Servlet在JAVA Web服务器中充当了信息资源的最小表示单位,代表了一个用户可以通过浏览器获取的资源,Servlet可以进行无限的扩展,它可以使用java的所有类库资源,为用户返回文本、图片、视频等各类信息资源。
从编程角度看,Servlet是一个java类,这个类需要实现Servlet接口,提供一个公开的无参构造方法。有WEB容器来控制它的创建、初始化、提供服务、销毁等。它的各种行为方式通过在web.xml中配置决定。
说明:在实际开发中,servelt一般通过继承自javax.servlet.http.HttpServlet
来创建(HttpServlet也是Servlet的实现),它可以为开发者提供一些方法的默认实现,而且可以区别不同的请求方法(doGet() doPost())
57
:Servlet生命周期
① 加载。Servlet类加载到JVM中,并且实例化。这个过程中,web容器会调用Servlet类的无参构造方法。默认下,Servlet是在第一次请求时被加载,但可通过在web.xml中配置<load-on-startup>标签设置在web容器启动时加载。
② 初始化。调用init()方法,如为Servelt配置的初始化参数是在init()中取得的。
③ 提供服务。当有HTTP请求时,调用service()方法。如果是继承自HttpServlet的话,service()会根据请求类型不同调用doGet()或doPost()方法
④ 销毁。当重新部署web应用,关闭web容器时被销毁并调用destroy()方法
Servlet运行时的基本原理
1
)、当WEB客户请求Servlet服务或当WEB服务启动时,容器环境加载一个Java Servlet类。
2
)、容器环境也将根据客房请求创建一个Servlet对象实例,或者创建多个Servlet对象实例,并把这些实例加入到Servlet实例池中。
3
)、容器环境调用Servlet的初始化方法HttpServlet.init()进行Servlet实例化。在调用初始化时,要给init()方法传入一个ServletConfig对象,ServletConfig对象包含了初始化参数和容环境的信息,并负责向servlet传递信息,如果传递失败,则会发生ServletException。Servlet将不能正常工作。
4
)、容器环境利用一个HttpServletRequest和HttpServletResponse对象,封装从Web客户接收到的HTTP请求和由Servlet生成的响应。
5
)、容器环境把HttpServletRequest和HttpServletResponse对象传递给HttpServlet.Service()方法。这样,一个定制的Java Servlet就可以访问这种HTTP请求和响应接口。Servlet()方法可被多次调用,各调用过程运行在不同的线程中,互不干扰。
6
)、定制的Java Servlet从HttpServletRequest对象读取HTTP请求数据,访问来自HttpSession或Cookie对象的状态信息,进行特定应用的处理,并且用HttpServletResponse对象生成HTTP响应数据。
7
)、当WEB服务器和容器关闭时,会自动调用HttpServlet.destroy()方法关闭任何打开的资源,并进行一些关闭前的处理。
servlet 的生命周期。
Servlet 运行在 Servlet 容器中,其生命周期由容器来管理。 Servlet 的生命周期通过 Servlet 接口中 init ()、 service ()、 destroy ()方法来表示。
Servlet 的生命周期包含了下面
4
个阶段。
(
1
) 加载和实例化
Servlet 容器负责加载和实例化 Servlet 。当 Servlet 容器启动时,或者在容器检查到需要这个 Servlet 来响应一个请求时,创建 Servlet 实例。当 Servlet 容器启动后,它必须要知道所需的 Servlet 类在什么位置, Servlet 容器可以从本地文件系统、远程文件系统或者其他网络服务器中通过类加载器加载 Servlet 类,成功加载后,容器创建
Servlet 实例。因为容器是通过 Java 的反射 API 来创建 Servlet 实例,调用的是 Servlet 的默认构造函数,也就是那个不带参数的构造函数,所以我们在编写 Servlet 类的时候,不应该提供带参数的构造函数。——这也就是为什么 Servlet 类可以不写构造函数的原因。
(
2
) 初始化
在 Servlet 实例化之后,容器必须调用 Servlet 的 init ()方法初始化这个对象。初始化的目的是为了让 Servlet 对象在处理客户请求前完成一些初始化工作,如建立数据库连接,获取配置信息等。对于每一个 Servlet 实例, init ()方法只能被调用一次。在初始化期间, Servlet 实例可以使用容器为它准备的 ServletConfit 对象从
web 应用程序的配置信息(在 web.xml 中配置)中获取初始化的参数信息。在初始化期间,如果发生错误, Servlet 实例可以抛出异常来通知容器。
(
3
) 请求处理
Servlet 容器调用 Servlet 的 service ()方法对请求进行处理。要注意的是,在 service ()方法调用之前, init ()方法必须成功执行。在 service ()方法中, servlet 实例通过 ServletRequest 对象得到客户端的相关信息和请求信息,在对请求进行处理后,调用 servletResponse 对象的方法设置响应信息。
(
4
) 服务终止
当容器检测在一个 Servlet 实例应该从服务中被移除的时候,容器就会调用实例的 destroy ()方法,以便让该实例可以释放它所使用的资源,保存数据到持久存储设备中。当需要释放内存或者容器关闭时,容器就会调用 Servlet 实例的 destroy ()方法。在调用 destroy ()方法后,容器会释放这个 Servlet 实例,该实例随后会被 java 的垃圾收集器回收。
在整个 Servlet 的生命周期过程中,创建 Servlet 实例、调用实例的 init ()和 destroy ()方法都只进行一次,当初始化完成后, Servlet 容器会将该实例保存在内存中,通过调用它的 service ()方法,为接收到的请求服务。
58
:在web.xml中Servlet的标准配置
<servlet>
<!-- Servlet的名字,请求时以名字访问 -->
<servlet-name>LoginServlet</servlet-name>
<!-- 完整类名 -->
<servlet-
class
>com.abc.LoginServelt</servlet-
class
>
<!-- 初始化参数,这些参数在init()方法中取得 -->
<init-param>
<param-name>myparam</param-name>
<param-value>
100
</param-value>
</init-param>
<!-- 在容器启动时加载 -->
<load-on-startup>
0
</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/*Servlet</url-pattern>
</servlet-mapping>
59
:Forward(直接转发或请求转发)和Redirect(间接转发或重定向)的区别
① 直接转发或请求转发:
RequestDspatcher rs = request.getRequestDispatcher(“url”);
Rs.forward(request,response);
本质上是一次请求,共享同一个request对象,地址栏地址不会改变
② 间接转发或重定向response.sendRedirect(“url”);
本质上是两次请求,对应两个不同request对象,请求信息不一样。
forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器,浏览器根本不知道服务器发送的内容是从哪儿来的,所以它的地址栏中还是原来的地址。还有,转发是在web应用程序之内进行的,可以访问web应用程序所设定的内部目录,像是WEB-INF目录,只能在Web应用程序中进行,不能指定至其它的Web应用程序的地址。
redirect就是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址,一般来说浏览器会用刚才请求的所有参数重新请求,所以session,request参数都可以获取。web应用程序会要求客户端浏览器重新发出请求地址,客户端会重新连接至所指定的地址,因此浏览器的地址会出现重新导向的信息,重新导向后的请求由浏览器发出,所以不能访问Web应用程序中的隐藏目录,像是WEB-INF,重新是由浏览器重新要求一个网页,可以指定至其他的Web应用程序地址。
RequestDispatcher.forward()方法和HttpServletResponse.sendRedirect()方法的区别是:前者仅是容器中控制权的转向,在客户端浏览器地址栏中不会显示出转向后的地址,他是不会改变Request的值,如果你需要在下一个页面中能从中获取新的信息的话,你可以Request.setAttribute()来放置一些标志,这样从下一个页面中获取;后者则是完全的跳转,浏览器将会得到跳转的地址,并重新发送请求链接。这样,从浏览器的地址栏中可以看到跳转后的链接地址。所以,前者更加高效,在前者可以满足需要时,尽量使用RequestDispatcher.forward()方法,并且,这样也有助于隐藏实际的链接。在有些情况下,比如,需要跳转到一个其它服务器上的资源,则必须使用 HttpServletResponse.sendRequest()方法。
1
、forward与include共亨Request范围内的对象,而redirect则不行,即:如果一个javabean被声明为request范围的话,则被forward到的资源也可以访
问这个javabean,而redriect则不行。
2
、forward与include基本上都是转发到context内部的资源,而redirect可以重定向到外部的资源,如: req.sendRedriect
1
、从地址栏显示来说
forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器.浏览器根本不知道服务器发送
的内容从哪里来的,所以它的地址栏还是原来的地址.
redirect是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是新的URL.
2
、从数据共享来说
forward:转发页面和转发到的页面可以共享request里面的数据.
redirect:不能共享数据.
3
、从运用地方来说
forward:一般用于用户登陆的时候,根据角色转发到相应的模块.
redirect:一般用于用户注销登陆时返回主页面和跳转到其它的网站等.
4
、从效率来说
forward:高.
redirect:低.
不要仅仅为了把变量传到下一个页面而使用session作用域,那会无故增大变量的作用域,转发也许可以帮助你解决这个问题。
redirect:以前的request中存放的变量全部失效,并进入一个新的request作用域。
forward:以前的request中存放的变量不会失效,就像把两个页面拼到了一起。
他们的调用分别如下:
request.getRequestDispatcher(
"apage.jsp"
).forward(request, response);
response.sendRedirect(
"apage.jsp"
);
在jsp页面中你也会看到通过下面的方式实现转发:
<jsp:forward page=
"apage.jsp"
/>
60
:过滤器的作用和原理
过滤器是处于web容器内的一个组件,它会过滤特定请求和响应.当一个请求到来时,web容器判断是否有过滤器与该信息资源关联,若有,则交给过滤器—处理,然后交给目标资源。响应的时候则以相反的顺序交给过滤器,最后返回给用户。过滤器是一种很重要的设计模式(基于AOP思想)可以再不侵入原有代码的基础上为它们提供一些功能。Struts就是利用过滤器工作的。
过滤器要实现javax.servlet.Filter接口,并实现doFilter()方法。
public
class
MyFilter
implements
Filter{
public
void
doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws
IOException, ServletException {
chain.doFilter(request, response);
}
public
void
init(FilterConfig arg0)
throws
ServletException {
}
public
void
destroy() {
}
}
Web.xml中配置:
<filter>
<filter-name>MyFilter</filter-name>
<filter-
class
>com.abc.MyFilter</filter-
class
>
</filter>
<filter-mapping>
<filter-name>MyFilter</filter-name>
<!-- 凡是以
"Servlet"
结尾的URL资源,该过滤器都起作用 -->
<url-pattern>/*Servlet</url-pattern>
</filter-mapping>
61
:监听器的作用和工作原理
对于web应用程序来说,监听器是处于web容器内的一个组件,它会对web容器中的request,session和application
3
种对象进行监听。当这些对象在创建或销毁时,web容器主动调用它们的初始化和销毁方法。
Request事件监听接口ServletRequestListener;
Session事件监听接口HttpSessionListener;
Application事件监听接口ServletContextListener;
应用:当用户第一次访问web应用程序是,程序将在线人数加
1
62
:JSP的运行机制
JSP本质上是Servlet,但JSP的存在是必要的,因为Servlet在处理静态内容(如HTML标签)时非常笨拙,不得不以字符串形式进行拼接,而JSP可以很好的实现动态和静态内容的分离。
当客户端发出一次对某个JSP的请求,web容器处理该请求过程如下:
① web容器会检验JSP的语法是否正确
② 将JSP文件转换成Servlet的源代码文件
③ 编译该源代码文件称为
class
文件
④ 创建一个该Servlet类的对象实例,为请求提供服务
说明:JSP只会在第一次访问时才转换盒编译。以后的访问web容器就直接调用编译好的servlet对象实例了,如果JSP被修改过,整个过程重新执行一次。
63
:JSP内置对象及用途(见文档)
request 类型 javax.servlet.ServletRequest 作用域 Request
response 类型 javax.servlet.SrvletResponse 作用域 Page
pageContext 类型 javax.servlet.jsp.PageContext 作用域 Page
session 类型 javax.servlet.http.HttpSession 作用域 Session
application 类型 javax.servlet.ServletContext 作用域 Application
out 类型 javax.servlet.jsp.JspWriter 作用域 Page
config 类型 javax.servlet.ServletConfig 作用域 Page
page 类型 javax.lang.Object 作用域 Page
exception 类型 javax.lang.Throwable 作用域 page
64
:JSP作用域
JSP比Servlet多了一种页面范围(page),一共四种作用域:page,request,session和application。
Request范围指的是一次请求,如果请求指向一个单一的JSP文件,则此时的page和request的生命周期是一样的,但是如果一次请求经过多次请求转发(forward),则这个request周期可以为多个page周期之和
65
:jsp中使用javabean
JavaBean规范:
① 是一个公开类
② 提供一个无参数的构造方法
③ 提供了公开的setXXX和getXXX
JSP使用javabean两种方法
① 纯JAVA代码,如Dog dog =
new
Dog()
② Jsp动作标签
<jsp:useBean id=”myBean”
class
=”javabean.MyBean” scope=”page”/>
<jsp:setProperty name=”myBean” property=”username” param=”lidiansheng”/>
<jsp:getProperty name=”myBean” property=”username”/>
Scope属性默认为page
66
:表达式语言EL和JSTL(详见文档EL OGNL JSTL)
EL是一种数据表现语言 JSTL是一个标签库,而EL是从JSTL诞生出来的
EL用${people.name}方法 区别于OGNL{#person.name}或{person.name}(根对象)
67
:Struts框架是如何体现MVC模式的?
Controller由ActionServlet、Action和Struts-config.xml组成。
Model由ActionForm来实现
View主要由JSP实现
一次典型的Struts请求是这样的:客户端(浏览器)发送请求,然后ActionServlet接收到请求后,会根据请求的路径和参数来判断由哪个Action来处理该次请求。等到Action处理完成以后,通常是Execute方法调用完成以后,Struts会根据该方法返回的ActionForward来判断由哪个JSP来作为最终响应。
68
:Hibernate实体存在哪几种状态
瞬时态、持久态、脱管态
69
:Hibernate继承关系的映射策略
有三个类 Animal Dog Cat
单表策略:当所有子类都在一张表中,只需建一个Animal.hbm.xml 用<subclass>添加子类
<
class
name=”Animal” table=”animal”>
<id …></id>
<discriminator column=”type” type=”string”></discriminator>
<subclass name=”Dog” discriminator-vlaue=”dog”>
<property>…</property>
</subclass>
<subclass …>…</subclass>
</
class
>
多表策略:当每个子类一张表,只需建一个Animal.hbm.xml 用<joined-subclass>添加子类
<
class
name=”Animal” table=”animal”>
<id …></id>
< joined-subclass name=”Dog” table=”dog”>
<key column=”dog_id”></key>
<property>…</property>
</subclass>
< joined-subclass …>…</subclass>
</
class
>
这样数据库有三个表animal dog cat 其中dog,cat共同属性放在animal表中
单表策略:无需表连接,查询速度快,适合多态查询(查询有继承关系的类时,同时查询到多个子类),缺点是可能造成表太大的结果(因为每个子类属性不一样,造成很多NULL),不利于维护。
多表策略:数据存储比较紧凑,当查询某个子类的数据时速度比较快,缺点是可能会有很多的表连接,不太适合多态查询
69
:AOP原理
AOP是一种对OOP有益补充的编程技术,它可以解决OOP和过程化方法不能够很好解决的横切问题,如事务,安全,日志等。随着软件系统变得越来越复杂,横切关注点成为一个大问题,AOP可以很轻松的解决横切关注点这个问题,Spring框架对AOP提供了很好的支持。简单来说,AOP就是一种功能比较复杂的拦截器。在代码真正达到目标以前,AOP可以对其进行拦截,提供一些通用的中间件的服务,例如,加上事务服务,记录日志等。Spring的声明式事务也就是基于AOP实现的。
70
:打印
1000
以内的回文数字
for
(
int
i=
10
;i<
10000
;i++){
int
temp = i;
int
reverse =
0
;
while
(temp>
0
){
reverse = reverse*
10
+temp%
10
;
temp=temp/
10
;
}
if
(i==reverse){
System.out.println(i);
}
}
String str =
"abababa"
;
StringBuffer str2 =
new
StringBuffer(str);
str2 = str2.reverse();
if
(str.equals(str2.toString())){
System.out.println(
"回文"
);
}
71
:
50
个人围城一圈数到
3
和
3
的倍数时出圈,问剩下的人是谁,在原来的位置是多少?
public
static
void
main(String []args) {
List<Integer>list =
new
LinkedList<Integer>();
for
(
int
i=
1
;i<=
50
;i++){
list.add(i);
}
int
index=-
1
;
while
(list.size()>
1
){
index = (index+
3
)%list.size();
list.remove(index--);
}
System.out.println(list.get(
0
));
}