1、Java
源程序(.java
)先编译成与平台无关的字节码文件(.class
)
2、Java
虚拟机(Java Virtual Machine
)将字节码文件再解释成机器码运行
3、采用字节码的最大好处是:可以实现一次编译到处运行,也就是java
的与平台无关性
4、所谓字节码,就是当Java
虚拟机加载某个类的对象时,首先需要把硬盘上关于该类的二进制源码编译成class
文件的二进制代码(字节码),然后把关于class
文件的字节码加载到内存中,然后再创建关于该类的对象。
编译型语言
需要通过编译器,将源代码编译成机器码之后才能执行的语言。一般是通过编译和链接两个步骤,编译是将我们的程序编译成机器码,链接是程序和依赖库等串联起来。
**优点:**编译器一般会有预编译的过程对代码进行了优化,因为编译只做了一次,运行时不会在编译,所以编译型语言效率高。
**缺点:**编译之后如果想要修改某一个功能,就需要整个模块重新编译。编译的时候根据对应的运行环境生成不同的机器码。不同的操作系统之间,可能会有问题。需要根据环境的不同,生成不同的可执行文件。
代表语言:C、C++、Pascal、Object-C、swift、GO
解析型语言
解释型语言不需要编译,相比编译型语言省了道工序,解释型语言在运行程序的时候才逐行进行翻译。字节码也是解释型的一部分。
**优点:**有良好的平台兼容性,只要安装了虚拟机,就可以。容易维护,方便快速部署,不用停机维护。
**缺点:**每次运行的时候都要解释一遍,性能上不如编译型语言。
代表语言:JavaScript、Python、Erlang、PHP、Perl、Ruby
1、项目名:全部小写 2、包名:全部小写,连续的单词只是简单地连接起来,不使用下划线。域名作为包的唯一前缀
indi
:个人发起,但非自己独自完成的项目,可公开或私有项目,copyright
主要属于发起者:indi.发起者名.项目名.模块名…pers
:指个人发起,独自完成,可分享的项目,copyright
主要属于个人:pers.个人名.项目名.模块名…priv
:私有项目,指个人发起,独自完成,非公开的私人使用的项目,copyright属于个人:priv.个人名.项目名.模块名…onem
:与indi
相同,推荐使用indi
3、类名:首字母大写,其余组成词首字母依次大写。测试类的命名以它要测试的类的名称开始,以Test
结束。
4、变量名、方法名、参数名:首字母小写,如果名称由多个单词组成,除首字母外的每个单词的首字母都要大写。下划线可能出现在JUnit
测试方法名称中用以分隔名称的逻辑组件。参数应该避免用单个字符命名。
5、常量名:全部大写,用下划线分隔单词
6、所有命名规则必须遵循以下规则 :
Java
中的关键字if, else, for, do, while
语句一起使用,即使只有一条语句(或是空),也应该把大括号写上。
Kernighan
和Ritchie
风格
else
或逗号,则不换行。public void method() { // 左大括号前不换行
do(); // 左大括号后换行
if (condition == 0) {
try {
something();
} catch(Exception e) { // 右大括号不换行
recover();
} // 右大括号前换行
} // 右大括号换行
}
{}
,不需要换行。例外:如果它是一个多块语句的一部分(if/else
或try/catch/finally
) ,即使大括号内没内容,右大括号也要换行。 中括号是类型的一部分:String[] args
, 而非String args[]
。
全局变量可以不用进行初始化赋值(有默认值),而局部变量必须要进行初始化赋值。
9种基本类型:boolean
、byte
、char
、short
、int
、long
、float
、double
和void
名称 | 字节 | 默认值 | 包装类 |
---|---|---|---|
字节型byte | 1字节 | 0 | Byte |
短整型short | 2字节 | 0 | Short |
整数型int | 4字节 | 0 | Integer |
长整型long | 8字节 | 0或0L | Long |
名称 | 字节 | 默认值 | 包装类 |
---|---|---|---|
单精度float | 4字节(符号1bit+指数8bit+位数23bit) | 0.0f | Float |
双精度double | 8字节(符号1bit+指数11bit+位数52bit) | 0.0d | Double |
名称 | 字节 | 默认值 | 包装类 |
---|---|---|---|
字符char | 2字节 | (空) | Character |
名称 | 字节 | 默认值 | 包装类 |
---|---|---|---|
布尔boolean | boolean类型数据通过int类型表示,此时boolean数据4字节32bit;boolean数组表示为byte数组,此时每个boolean数据1字节占8bit | false | Boolean |
优先级 | 运算符 | 结合性 |
---|---|---|
1 | ()、[]、{} | 从左向右 |
2 | !、+、-、~、++、– | 从右向左 |
3 | *、/、% | 从左向右 |
4 | +、- | 从左向右 |
5 | «、»、>>> | 从左向右 |
6 | <、<=、>、>=、instanceof | 从左向右 |
7 | ==、!= | 从左向右 |
8 | & | 从左向右 |
9 | ^ | 从左向右 |
10 | | | 从左向右 |
11 | && | 从左向右 |
12 | || | 从左向右 |
13 | ?: | 从右向左 |
14 | =、+=、-=、*=、/=、&=、|=、^=、~=、«=、»=、>>>= | 从右向左 |
public static final
(并且只能是 public
,用 private
修饰会报编译错误)
public abstract
(只能是 public abstract
,其他修饰符都会报错,不是 default
)
public
public
或default
public
、protected
、default
、private
放在所有的“import”
语句之后,类定义之前,主要声明该类可以做什么,以及创建者、创建日期、版本和包名等一些信息。
/**
@projectName(项目名称): project_name
@package(包): package_name.file_name
@className(类名称): type_name
@description(类描述): 一句话描述该类的功能
@author(创建人): user
@createDate(创建时间): datetime
@updateUser(修改人): user
@updateDate(修改时间): datetime
@updateRemark(修改备注): 说明本次修改内容
@version(版本): v1.0
*/
public class student{
// ...
}
紧靠在方法定义的前面,主要声明方法参数、返回值、异常等信息。
/**
@param num1: 加数1
@param num2: 加数2
@return: 两个加数的和
@throws: 可能抛出xx异常
*/
public int add(int num1,int num2) {
// ...
return value;
}
定义字段的前面,用来描述字段的含义。
/**
* 用户名
*/
public String name;
// 或
/**用户名*/
public String name;
单行:// ...
多行:/* ... */
private
的属性和方法。main
方法 -> 非静态变量/代码块 -> 构造方法 含有抽象方法的类一定是抽象类,但抽象类不一定含有抽象方法,也可以全部 都是具体的方法。
抽象类不能被实例化,即不能使用关键字new
来生成实例对象,但可以声明一个抽象类的变量指向具体子类的对象。
this
首先是一个对象,它代表调用这个函数的对象,可以理解为:指向对象本身的一个指针。
this
代表当前对象的一个引用。区分成员变量和局部变量:this.name = name;
this(name, age);
return this;
1、参数化类型与原始类型的兼容
当参数化类型引用一个原始类型的对象时,编译器只是警告而不报错。同样当原始类型引用一个参数化类型对象时,编译器也只是警告而不报错。
Vector<String> v = new Vector();
Vector v = new Vector(String);
2、参数化类型无继承性
错误示例:
Vector<String> v = new Vector(String);
Vector<Object> v1 = v;
v1.add(new Object());
String s = v.get(0);
// 因为v对象中的成员不再是String类型。所以代码“Vector<Object>v1=v”是错误的。
3、泛型的“去类型”特性
泛型中的类型只是提供给编译器使用的,当程序编译成功后就会去掉“类型”信息。泛型的作用只是限制集合中的输入类型,让编译器挡住源程序中的非法输入。这样有一个好处,程序具体运行时将不会受到泛型的影响。
正确示例:
// 创建对象arry1
ArrayList<String> arry1= new ArrayList<String>();
arry1.add("cjg");
// 创建对象arry2
ArrayList<Integer> arry2 = new ArrayList<Integer>();
arry2.add(27);
// 输出true。说明编译器生成的关于对象arry1集合和arry2集合的字节码为同一个对象
System.out.println((arry1.getClass()==arry2.getClass()));
4、利用反射绕过泛型的类型限制
由于编译器生成的字节码会去掉泛型的类型信息,所以只要能跳过编译器,还是可以给通过泛型限制类型的集合中加入其他类型的数据。
正确示例:
// 创建集合arry1
ArrayList<Integer> arry1 = new ArrayList<Integer>();
// 添加整数型数字
arry1.add(27);
// 通过反射向集合中添加字符型abc
arry1.getClass().getMethod("add", Object.class).invoke(arry1, "abc");
当程序运行时,Java
虚拟机将编译生成的.class
文件按照需求和一定的规则加载进内存,并组织成为一个完整的Java
应用程序,该过程由类加载器自动完成。
Java
虚拟机(JVM
)运行类的第一件事情就是将该类的字节码加载进来,即类加
载器根据类的名称,定位和生成类的字节码数据,然后返回给JVM
。
类加载器只要能提供给JVM
调用的类字节码就可以,因此类加载器也可以描述为字节码的制造器。类加载器装载某个类字节码的过程实际上就是创建Class
类的一个实例对象。
第一个类加载器:BootstrapLoader
(引导类加载器)。由于BootstrapLoader
加载器不需要加载,所以其不是Java
类而是利用C++
语言编写的。
两个内置类加载器:ExtClassLoader
(扩展类加载器)、AppClassLoader
(应用程序类加载器)。
当一个类被加载后,JVM
将其编译为可以执行的代码(字节码)存储到内存中,同时会将索引信息存储到一个HashTable
中,注意索引的关键字就是被加载类的完整名字。如果JVM
想运行某个类时,首先会使用类名作为关键字在HashTable
中查找相应的信息,如果该可执行代码已经存在,JVM
就直接会从内存里调用该可执行代码,否则就调用类加载器进行加载和编译。
程序是计算机指令的集合,它以文件形式存储在磁盘上。
进程就是一个执行中的程序,每一个进程都有其独立的内存空间和系统资源。支持多进程,就是CPU
在交替轮流执行多个程序。
线程是CPU
调度和分配的基本单位,一个进程可以由多个线程组成,而这多个线程共享同一个存储空间,这使得线程间的通信比较容易。
在一个多进程的程序中,如果要切换到另一个进程,需要改变地址空间的位置。然而在多线程的程序中,就不会出现这种情况,因为它们位于同一个内存空间内,只需改变运行的顺序即可。
多线程指单个程序可通过同时运行多个不同的线程,以执行不同任务。所谓同时,也要依据CPU
。如果是多个CPU
,则并发运行,如是一个CPU
,则根据系统具体情况,执行多个线程。
创建线程的方法一般有两种:
Runnable
接口的方式创建线程。Thread
类来创建线程。 Java规范中只定义了线程的4种状态,即新建状态、可运行状态、阻塞状态和死亡状态。为了更清晰地说明线程的状态变化过程,我们认为划分为5个状态更好理解,这里把可运行状态(Runnable)分解为就绪状态和运行状态,可以更好地理解可运行状态的含义。
new
关键字)已经建立,在内存中有一个活跃的对象,但是没有启动该线程,所以它仍然不能做任何事情
start()
方法,该线程就处于就绪状态。此时线程等待CPU
时间片,一旦获得CPU
时间周期,线程就可以执行。这种状态下的任何时刻线程是否执行完全取决于系统的调度程序。
CPU
执行周期,就处于运行状态。在选择哪个线程可以执行时,操作系统的调度程序考虑线程的优先级
wait()
方法或sleep()
方法。suspend()
方法,该方法已经不推荐使用。run()
方法就处于死亡状态。在Java2
中通过调用stop()
和destroy()
方法使得线程死亡,但这些方法都引起程序的不稳定,由于stop()
方法已经过时了,所以最好不要在自己的程序中调用该方法。
如一个线程创建后,可通过在线程中调用setPriority()
方法,来设置其优先级。
public final static int MIN_PRIORITY=1
:表示最低优先级public final static int MAX_PRIORITY=10
:表示最高优先级public final static int NORM_PRIORITY=5
:表示默认优先级Thread
类的sleep()
方法。
interrupt()
方法。
yield()
方法实现。这个方法不能将运行权让给指定的线程,只是允许这个线程把运行权让出来,至于给谁,这就需要看由哪个线程抢占到了。
一个程序运行到一半时,突然被另一个线程抢占了运行权,此时这个线程数据处理了一半,而另一个线程也在处理这个数据,那么就会出现重复操作数据的现象,最终导致整个系统的混乱。解决同步问题的方法有两种:一种是同步块,另一种是同步化方法。
使具有某个对象监视点的线程,获得运行权限的一种方法,每个对象只能在拥有这个监视点的情况下,才能获得运行权限:synchronized(obj) { 代码段 }
。
obj
是一个监视点对象,可以是实际存在的,也可以是假设的。在很多程序段中,这个监视点对象都是假设的。其实这个监视点就相当于一把锁,给一个线程上了锁,那么其他线程就会被拒之门外,就无法得到这把锁。直到这个线程执行完了,才会将这个锁交给其他线程。其他的线程得到锁后,将自己的程序锁住,再将其他线程拒之门
外。
对整个方法进行同步:synchronized void f() { 代码 }
。
Java
中的异常分为两大类:错误Error
和异常Exception
Error
一般是指Java
虚拟机相关的问题,如系统崩溃、虚拟机出错误、动态链接失败等,这种错误无法恢复或不可能捕获,将导致应用程序中断,通常应用程序无法处理这些错误,因此应用程序不应该捕获Error
对象,也无须在其throws
子句中声明该方法抛出任何Error
或其子类。
Java
提供了2种异常机制:
RuntimeExepction
):我们可以不处理。当出现这样的异常时,总是由虚拟机接管。
CheckedExecption
):对于这种异常,Java编译器要求我们必须对出现的这些异常进行catch
。 所以,面对这种异常不管我们是否愿意,要么用try-catch
语句捕获它,要么用throws
子句声明抛出它,否则编译不会通过。
常见的5种运行时异常:
ClassCastException
(类转换异常)IndexOutOfBoundsException
(数组越界)NullPointerException
(空指针)ArrayStoreException
(数据存储异常,操作数组时类型不一致)BufferOverflowException
(缓存区溢出异常) 垃圾回收的主要问题是程序无法估计时间延迟导致程序执行的延迟。虽然拥有了垃圾回收机制,但是Java
程序仍然可能存在内存泄漏。
有关内存管理的经验:
null
。在使用这种方式时,必须特别注意一些复杂的对象。例如,数组、队列、树、图等,这些对象之间的相互引用关系较为复杂。对于这类对象,GC
(垃圾回收)回收它的效率一般较低,如果程序允许,尽早将不用的引用对象赋为null
,这样可以加速GC
的工作。finalize
函数。finalize
函数是Java
给程序员提供的一个释放对象或资源的机会。但是,它会加大GC
的工作量,因此尽量少采用finalize
方式回收资源。GC
来说,回收更为复杂。另外,注意全局变量以及静态变量,这些变量往往容易引起悬挂对象,造成内存浪费。System.gc()
),增长系统做垃圾回收的最终时间,降低系统性能。 输入输出类中有关于文件操作的类File
,关于以字节方式访问文件的类InputStream
和类OutputStream
,关于以字符方式访问文件的类Reader
和类Writer
。
File
类提供了与文件或目录相关的信息,是唯一代表磁盘文件对象的类。
当具体到对文件访问时,就会涉及流(Stream
)的概念。流就是数据流向某个对象,并且到达这个对象的过程。
输入流:从目标程序中,将数据以流的形式复制到流对象中,然后,再从流对象中将数据读取出来。 输出流:将数据以流的形式复制到流对象中去,再从这些流对象中取出流,写入到目标中。 程序读取数据称为打开输入流,程序向其他源写入数据称为打开输出流。
数据流的父类是InputStream
和OutputStream
,为抽象类。提供了read
、write
、close
方法。处理以字节为单位的数据
FileInputStream
输入流、FileOutputStream
输出流Filter
类能够进行多字节数据的读取,可以方便地处理那些除字节数据类型以外的数据。有FileInputstream
和FilterOutputStream
。FileInputstream
类读取数据,然后通过FilterInputStream
类对数据进行组合,最后再输出数据FilterOutputStream
类,将所有这些类型的数据分解成字节类型的数据,再将字节类型的数据通过FileOutputStream
类,向目标对象输出数据FilterOutputStream
类和FilterInputStream
类同样很难处理整型、字符串型等数据DataInputStream
,BufferedInputStream
以及PushBackInputStream
等,总结为: DataInputStream
:数据输入流,以机器无关的方式读取Java
的基本类型BufferedInputStream
:缓冲输入流,由于基础输入流一个字节一个字节读取,频繁与磁盘进行交互,造成读取速度较低。缓冲流的存在就是先将数据读取到缓冲流(内存中),然后一次性从内存中读取多个字符,提高读取的效率PushInputStream
:回退输入流,Java
中读取数据的方式是顺序读取,如果某个数据不需要读取,需要程序处理,PushBackInputStream
就可以将某些不需要的数据回退到缓冲中DataInput
接口和DataOutput
接口,同时DataInputStream
类和DataOutputStream
类分别实现了以上两个接口,并继承了FilterInputStream
类和FilterOutputStream
类 可以一次性处理两个字节的流,称为字符流。字符流分为两个类:Reader
类和Writer
类。Reader
类负责字符输入工作,而Writer
类负责字符输出工作
Reader
和Writer
,为抽象类。提供了read
、write
方法但没有被实现
InputStreamReader
和BufferedReader
:先从文件中以字节形式读取数据,然后将数据组合成字符型数据,最后将所有读取的数据缓存起来一起输出OutputStreamWriter
和BufferedWriter
:先是以字符的形式将数据缓存起来,然后将其变成字节的形式写入文件FileReader
类和FileWriter
类分别是InputStreamReader
类和OutputStreamWriter
类的子类,它们提供了将字符数据直接写入文件,或从文件中直接读出字符数据的简便方法System.in
用于从标准键盘输入设备读入数据,其返回一个InputStream
类型PrintStream
与PrintWriter
类都是打印输出流,它们在许多方面提供了相似的功能。它们将各种基本类型的数据输出到字符串流中,并提供了自动刷新功能。这两个类的不同点,也是在自动刷新功能上PrintStream
类会调用println()
方法,其输出会包含换行符。但是PrintWriter
类只有在调用println()
方法时才会自动刷新RandomAccessFile
类实现了Datainput
与Dataoutput
接口,所以可以读取基本数据类型的数据。为了能够随机访问,必须先创建对象r
、rw
、rws
、rwd
。r
代表以只读方式打开文件,若此时进行写操作会出错;rw
、rws
、rwd
是以读写模式打开文件,若文件不存在,则创建它 对象序列化是将对象写入流,而序列化读取则指从流中获取数据后,重构对象的过程。只有实现了Serializable
接口的对象才是可序列化对象
ObjectOutputStream
类继承了OutputStream
类,同时实现了ObjectOutput
接口,提供将对象序列化并写入流中的功能:ObjectOutputStream (OutputStream out)
ObjectInputStream
类继承了InputStream
类,同时实现了ObjectInput
接口,提供将对象序列化并从流中读取出来的功能:ObjectInputStream (InputStream out)
基本概念:
数据元素相互之间的关系称为结构。根据数据元素之间关系的不同特性,通常分为下列4类基本结构:
数据又有逻辑结构和物理结构之分:
Collection
接口是数据集合接口,它位于数据结构API
的最上部。构成Collection
的单位,被称之为元素。接口提供了添加、删除元素等管理数据的功能。常用的集合有List
集合、Set
集合和Map
集合,其中List
与Set
继承了Collection
接口,各接口还提供了不同的实现类。上述集合类的继承关系如图所示。
key
,每个key
只能映射一个value
。提供3种集合的视图,Map
的内容可以被当作一组key
集合、一组value
集合或一组key-value
映射。
Hashtable
散列表类:任何非空(non-null
)的对象都可作为key
或value
。添加数据使用put(key,value)
方法,取出数据使用get(key)
方法,这两个基本操作的时间开销为常数。通常默认的load factor=0.75
较好地实现了时间和空间的均衡,增大load factor
可以节省空间,但相应的查找时间将增大,这会影响像get
和put
这样的操作。
HashMap
散列映射类:非同步的,并且允许null
,即null value
和null key
。如将HashMap
视为Collection
时(values()
方法可返回Collection
),其迭代器操作时间开销和HashMap
的容量成比例。因此,迭代操作的性能相当重要,切记不要将HashMap
的初始化容量设得过高,或者将load factor
设得过低。
实现原理:HashMap
内部其实是一个数组,每个数组下是一个单向链表。HashMap
中的数组是一个取名为Entry
的类,类包含(key,value, next
)这几个属性。存放规则为,数组下标按hash(key)%len
获得,取得数组后则查找对应数组的值。HashMap
还有个负载因子(默认0.75
),当里面数组填满了75%
的时候,会进行扩展到原来大小的2
倍。在Java 8
之后hashmap
进行了优化:由于单向链表的查询时间复杂度为o(n)
,在极端情况下(每次都查找)可能存在性能问题,于是Java 8
针对链表长度大于8
的情况会使用时间复杂度为O(log n)
的红黑树进行存储来提升存储查询的效率,时间复杂度就从原来的O(1)+O(n)
变成了O(1)+O(log n)
,优化了极端情况导致的性能问题。
LinkedHashMap
实现原理:LinkedHashMap
内部是双向链表和**HashMap
的结合,支持多种迭代顺序,默认按插入顺序,也可以按访问**顺序。
accessOrder=true
):调用过get
访问的元素会放到链尾,迭代会从链首开始accessOrder=false
) :按插入顺序迭代出来TreeMap
实现原理:TreeMap
内部是基于红黑树实现的,并且默认会通过compareTo
按照key
类型进行自然排序。TreeSet
的底层是TreeMap
。
Container
对象中各个元素,而又不需暴露该对象的内部细节。Iterator
接口中定义了以下3个方法。所有Collection
接口的子类、子接口都支持Iterator
迭代器。
hasNext()
:是否还有下一个元素。next()
:返回下一个元素。remove()
:删除当前元素。迭代器模式由以下角色组成:
Iterator
:负责定义访问和遍历元素的接口。Concrete Iterator
:实现迭代器接口,并记录遍历中的当前位置。Container
:负责提供创建具体迭代器角色的接口。Concrete Container
:实现创建具体迭代器角色的接口——这个具体迭代器角色与该容器的结构相关。迭代器模式给容器的应用带来以下好处:
java Collection
中为了提高可扩展性,容器还是提供了遍历的接口。迭代器模式的适用范围如下:
XML
(Extensible Markup Language
, 可扩展的标记语言)是SGML
(Standard Generalized Markup Language
, 标准通用标记语言)的一个子集,其目标是在网络上以类似HTML
的方式实现文件的发送、接收和处理。XML
的出现极大地简化且提高了SGML
与HTML
之间的通用性。
为了能够让XML
的文档具有可读性,XML文档采取了数据与文档样式分离的原则。XML
文档只提供数据,而XSL
包括数据样式,文档的结构则使用DTD
。
<?xml version="1.0" encoding="gb2312"?> <!—⽂档声明-->
<留⾔本>
<留⾔记录>
<留⾔者姓名>KAI</留⾔者姓名>
<电⼦邮件>kai@hostx.org</电⼦邮件>
<⽹址>http://www.17xml.com </⽹址>
<留⾔内容>千⼭万⽔总是情,常来泡妞⾏不⾏?咔咔:_) </留⾔内容>
</留⾔记录>
</留⾔本>
**服务器:**能够提供信息的计算机或程序。
**客户机:**指请求信息的计算机或程序。
TCP/IP
协议是整个网络通信的核心协议。其中TCP
协议运行在客户终端上,是集成在操作系统内的一套协议软件,它的任务是在网络上的两个机器之间实现端到端的、可靠的数据传输功能。IP
协议运行在组成网络的核心设备路由器上,它也是集成在系统内的一层协议软件,负责将数据分组从源端发送到目的端,通过对整个网络拓扑结构的理解为分组的发送选择路由。
IP
地址是一个32
位的二进制序列,点分的每个部分占一个字节,使用十进制表达。
IP
地址由网络部分和主机部分组成。网络部分表示一个通信子网,子网内的主机可以不通过路由器而直接通信,主机部分标识该通信子网内的主机。
为了区分IP
地址的网络部分和主机部分给出了掩码的概念,掩码也用点分十进制表达。并且还可以用IP
地址后加一个/
跟上掩码的全部1
的数量表达掩码,如掩码255.255.255.0
也可以表达为/24
。
通过主机的IP
地址和网络掩码就可以计算该主机所在的网络,如主机的IP
地址为192.168.2.155/24
,则网络地址的计算方式是把网络掩码同IP地址进行二进制与运算。则上述主机的网络号为192.168.2.0
。
http://www.abc.com/bbs/index.jsp
。http
表示一种应用层的传输协议超文本传输协议,www.abc.com
是域名,而bbs
是网页所在的路径,index.jsp
是要访问的网页。其中应用层协议不只是HTTP
协议,还有FTP
协议、FILE
协议等。
DNS
的主要用途就是将主机名字和主机的IP地址进行映射,将名字映射为IP
地址。DNS
是一个分布式数据库服务器系统,存储域名和对应IP的信息。当用户使用域名访问时,本机的DNS
协议会向已经设置的DNS
服务器发出请求完成域名到IP
地址的转换,直到搜索到对应的IP
地址。
IP
协议无法解决的。此时需要它的上层协议TCP
来处理。
TCP
协议实现可靠通信的基础是采用了握手机制实现了数据的同步传输,即在通信的双方发送数据前首先建立连接,协商一些参数,如发送的数据字节数量、缓冲区大小等。一旦连接建立再传送数据,并且对于收到的每一个分组进行确认,这样很好地保证了数据的可靠传输。
TCP
协议提供了端口号的概念,每个端口号对应一个应用进程,如端口号80
代表HTTP
连接,端口号21
代表FTP
连接服务。这样TCP
协议软件通过端口号识别不同的进程。
端口号的设置有一定的限制,最大数是65535
,在1024
之前是well-known
端口号,是全世界统一的,如FTP
服务进程的端口号是25
,HTTP
服务进程的端口号是80
等。而1024~65535
之间是用户自己选择使用。
TCP/IP
模型的传输层,该协议可以直接封装成IP
分组,不需要事先建立连接就可以发送这些封装好的IP
分组。
一个UDP
报文有两个端口,即源机器端口和目的机器端口、UDP
长度、UDP
校验和UDP
净荷组成,通过目的端口目的主机的传输层就知道把该报文递交给哪个处理进程。而源端口知道从目标主机返回的UDP
报文到达源主机后可以正确地提交给上层进程处理。UDP
数据段由**8
字节的头部和净荷部分组成,净荷中包含要传输的真实数据**。
UDP
协议不考虑流量控制、差错控制和损坏数据处理,即使收到的是受损的数据也不要求发送端重传。所有上述问题都要求应用层软件处理。但是,因为它是无连接的协议,所以也不需要事先建立连接,从而节约了建立连接的时间,传输数据是异步的,使得数据及时地发送到网络上,减少了数据处理和传输的时延。
网络程序中的套接字用来将应用程序与端口连接起来,套接字是一个软件实现,也是一个假想的装置。
在Java API
中,将套接字抽象化成为类,所以程序只需创建Socket
类的对象,就可以使用套接字。Java
使用Socket
的流对象进行数据传输,Socket
类中有输入流和输出流。
Java
中的**TCP
网络程序设计是指利用Socket
类编写通信程序**。设计TCP
程序的过程是:服务器的套接字等待客户机连接请求,当服务器接收到请求后就可以通过相应的方法获取输入流和输出流,从而实现相应的功能。
利用ServerSocket
和Socket
类来编写面向TCP
协议的程序步骤:
ServerSocket(int port)
,创建一个服务器端套接字,并绑定到指定端口上。accept()
,监听连接请求,如客户端请求连接,则接受连接,返回通信套接字。Socket
类的getOutputStream()
和getInputStream()
,获取输出流和输入流,开始网络数据的发送和接收。Socket()
,创建一个流套接字,并连接到服务器端。Socket
类的getOutputStream()
和getInputStream()
,获取输出流和输入流,开始网络数据的发送和接收。利用DatagramSocket
类来编写面向UDP
协议的程序步骤:
DatagramSocket(int port)
创建一个数据报套接字,并且绑定到指定端口上。DatagramPacket(byte[]buf,int length)
,建立一个字节数组以接收UDP
包。DatagramSocket
类的receive()
,接收UDP
包。DatagramSocket()
,创建一个数据包套接字。DatagramPacket(byte[]buf,int offset,int length,InetAddress address,int port)
,建立要发送的UDP
包。DatagramSocket
类的send()
,发送UDP
包。1、Java
文件的扩展名区分大小写
2、JDK
工具库:javac
编译器、java
解释器、appletviewer
小程序浏览器、javadoc
文档生成器、jdb
调试器、javah
生成C过程头文件、javap
反汇编器
3、类与类之间最常见的关系主要有3种:依赖(uses–a
) 、聚合(has–a
) 、继承(is–a
)
4、对于枚举的构造函数,必须放在枚举常量的后面,同时构造函数的修饰符必须是private
。枚举类型的自定义构造函数不能覆盖默认执行的构造函数,只会在其后面执行。
5、通过自动装箱方式返回同一数值的对象时,如果该数值在-128~127
之间(包含它们自己),返回的对象会引用同一对象;否则则创建新的对象。
这个要视具体情况而定,如果条件在三重之内,最好使用条件语句。如果超过了三重,最好使用分支语句。
根据情况不同而定,for
循环语句主要针对有限循环而言,也就是说,当循环有上限的时候,一般使用for
循环。while
循环语句则针对那些无限循环的代码而言,当循环没有明确上限,上限只是根据程序中的条件而定。
如果操作两边都是对象句柄,就比较两个句柄是否指向同一个对象。如果两边是基本类型,比较的就是值。
equals
比较的是两个对象的内容,如果不重载equals
方法,自动调用object
的equals
方法,则和“==”
样。在JDK
中像String
、Integer
,默认重载了equals
方法,则比较的是对象的内容。在实际编程中,建议使用equals
方法。
主要是考虑“效率”和“安全性”的缘故。若String
允许被继承,则其频繁地被使用,可能会降低程序的性能,所以String
被定义成final
。
protected
字段。
final
关键字,这样此类就不能再被继承。
允许对对象进行不同的操作,但具体的操作却取决于对象的类型。 程序在编译的时候,什么函数对哪个对象执行什么操作都已经确定,这就称作静态编译。多态是动态编译,动态编译就是在程序执行的过程中,根据不同对象类型有不同的绑定,其通过一个方法接口,实现多个不同的实现过程。这依赖于编译时编译器对同一个方法不同参数的识别。
重载是在一个类里,名字相同但参数不同的方法。多态是为了避免在父类里大量重载,而引起代码臃肿且难于维护的解决方案。多态有两种表现形式:重载和覆盖。
共同点:
不同点:
Java
不支持多重继承,即一个子类只能有一个父类,但一个子类可以实现多个接口。public
的,抽象类中的方法必须手动声明访问控制符。 不同线程共享一个变量,并对此变量的访问进行同步,因为它们共享一个内存空间,所以相比之下,它比进程之间通信要简单容易得多。
饥饿是指系统不能保证某个进程的等待时间上界,从而使该进程长时间等待,当等待时间给进程推进和响应带来明显影响时,称发生了进程饥饿。当饥饿到一定程度的进程所赋予的任务即使完成也不再具有实际意义时称该进程被饿死。饥饿是个异步过程。
当一个或多个进程,在一个给定的任务中,协同作用、互相干涉,而导致一个或者更多进程永远等待下去,死锁就发生了。
与此类似,当一个进程永久性地占有资源,使得其他进程得不到该资源,就发生了饥饿。在饥饿的情形下,系统不处于死锁状态中,因为有一个进程仍在处理之中,只是其他进程永远得不到执行的机会。
死锁的发生情况:
死锁和饥饿的不同点:
当遇到一个对象要做出多个动作,并且多个动作又是穿插在一起时,就要使用线程的概念来编写程序。
在网络编程中,网络上不同的用户操作一个对象时,也可以借助线程来完成程序。
由于线程会进入阻塞状态,并且对象同步锁的存在,使得只有获得对象的锁才能访问该对象。因此很容易发生循环死锁。如线程A等待线程B释放锁,而线程B等待线程C释放锁,线程C又等待线程A释放锁,这样就造成一个轮回等待。3个线程都无法继续运行。
避免死锁的基本原则:
suspend()
和resume()
方法,这些方法具有与生俱来产生死锁的缺点。I/O
操作的方法施加锁。CPU
时间片,如果线程数量增多到一定程度,如100
个以上,则线程的管理开销代价会增大。Java
也不例外,除了尽量不使用控制线程的一些方法如suspend()
,resume()
,需要认真地分析线程的执行过程,以避免线程间的死锁。 RuntimeException
与Error
可以在任何代码中产生。它们不需要由程序员显式地抛出,一旦出现错误,那么相应的异常会被自动抛出。
而检查异常是由程序员抛出的,这分为两种情况:程序员调用会抛出异常的库函数、程序员自己使用throw
语句抛出异常。
遇到Error
,程序员一般是无能为力的,遇到RuntimeException
,那么一定是程序存在逻辑错误,要对程序进行修改。
只有检查异常才是程序员所关心的,程序应该抛出或处理检查异常。覆盖父类某方法的子类方法,不能抛出比父类方法更多的异常。有时设计父类的方法时,会声明抛出异常,但实现方法的代码却并不抛出异常,这样做的目的就是,方便子类方法覆盖父类方法时可以抛出异常。
字节流是最基本的,所有的InputStrem
和OutputStream
的子类都是字节流,其主要用于处理二进制数据,并按字节来处理。
实际开发中很多的数据是文本,这就提出了字符流的概念,它按虚拟机的encode
来处理,也就是要进行字符集的转化。
这两者之间通过InputStreamReader
和OutputStreamWriter
来关联。实际上,通过byte[]
和String
来关联在实际开发中出现的汉字问题,这都是在字符流和字节流之间转化不统一而造成的。在从字节流转化为字符流时,实际上就是byte[]
转化为String
。
至于java.io
中还出现了许多其他的流,主要是为了提高性能和使用方便,如BufferedInputStream
、PipedInputStream
等。
管道流是输入输出并用。例如,将数据从输出管道进,从输入管道出。
Collections
是java.util
下的类,它包含各种有关集合操作的静态方法。Collection
是java.util
下的接口,它是各种集合结构的父接口。List
、Set
是继承自Collection
接口,Map
不是继承自Collection
接口。Vector
是线程安全的,是同步的。而ArrayList
是线程不安全的,不是同步的。Vector
默认增长为原来一倍,而ArrayList
却是原来的一半。 二者都属于Map
接口的类,作用都是将唯一键映射到特定的值上。它们的区别有两点:
HashMap
类没有分类或排序,它允许一个null
键和多个null
值。Hashtable
类似于HashMap
,但是不允许null
键和null
值,它也比HashMap
慢,因为它是同步的。 数据结构一般分为两大类:线性数据结构和非线性数据结构。
线性数据结构包括:线性表、栈、队列、串、数组和文件;
非线性数据结构包括:树、图等。
Set
:从Collection
接口继承而来,但没有提供新的抽象的实现方法,Set
不能包含重复元素。List
:是一个有序的集合,可以包含重复的元素,提供了按索引访问的方式。这里的有序就是指有顺序的排放,并不是排序。 Map
接口是键值对映射(即key-value
映射),而Collection
接口提供的是一组数据,这两个集合存储的数据类型就不同。
如果Map
继承了Collection
接口,那么所有实现了Map
接口的类到底是用Map
的键值对映射数据还是用Collection
的一组数据呢
Map
继承Collection
,违反了接口分离原则。数据结构不同,操作就不一样,所以接口是分开的。(接口分离原则:客户端不应该依赖它不需要的接口,目的是解耦,接口尽量小)
Vector
:比ArrayList
多了个同步化机制(线程安全)。Stack
:堆栈类,先进后出。Hashtable
:比HashMap
多了个线程安全。Enumeration
:枚举,相当于迭代器。 除了这些之外,其他的都是非线程安全的类和接口。线程安全类的方法是同步的,每次只能一个访问,是重量级对象,效率较低。对于非线程安全的类和接口,在多线程中需要程序员自己处理线程安全问题。
一般用数组列表代替它,因为它们的使用方法几乎一样,唯独不同的就在线程安全方面。数组列表是非线程安全类,在实现线程编程时,要自己处理安全问题,而Vector
则是线程安全类,自动会处理安全问题。
Vector
类提供实现可增长数组的功能,随着更多元素加入其中,数组变得更大。在删除一些元素之后,数组变小。
XML
和HTML
的目标不同:HTML
的设计目标是显示数据并集中于数据外观,而XML
的设计目标是描述数据并集中于数据的内容。
与HTML
相似,XML
不进行任何操作。程序中必须编写代码,来实现对XML
格式数据的操作。
与HTML
不同,XML
标记由架构或文档的作者定义,并且是无限制的。HTML
标记则是预定义的,HTML
作者只能使用当前HTML
标准所支持的标记。
UDP
不提供可靠的数据传输,事实上,该协议不能保证数据准确无误地到达目的地。UDP
在许多方面非常有效。当某个程序的目标是尽快地传输尽可能多的信息时(其中任意给定数据的重要性相对较低),可使用UDP
;ICQ
短消息使用UDP
协议发送消息。
TCP
的目的是提供可靠的数据传输,并在相互进行通信的设备或服务之间保持一个虚拟连接。TCP
在数据包接收无序、丢失或在交付期间被破坏时,负责数据恢复。它通过为其发送的每个数据包提供一个序号来完成此恢复。记住,较低的网络层会将每个数据包视为一个独立的单元,因此,数据包可以沿完全不同的路径发送,即使它们都是同一消息的组成部分。这种路由与网络层处理分段和重新组装数据包的方式非常相似,只是级别更高而已。
为确保正确地接收数据,TCP
要求在目标计算机成功收到数据时,发回一个确认(即ACK
)。如果在某个时限内未收到相应的ACK
,将重新传送数据包。如果网络拥塞,这种重新传送将导致发送的数据包重复,但是,接收计算机可使用数据包的序号来确定它是否为重复数据包,并在必要时丢弃它。
TCP/IP
(Transmission Control Protocol/Internet Protocol
,传输控制协议/互联网协议)协议族包含了TCP/IP
层次模型,协议共分为4
层:应用层、传输层、网络层、数据链路层。
应用层是用户所面向的应用程序的统称。TCP/IP
协议族在这一层面有很多协议来支持不同的应用,大家所熟悉的基于Internet
应用的实现,就离不开这些协议。如进行万维网(WWW
)访问用到了HTTP
协议,文件传输用FTP
协议,电子邮件发送用SMTP
,域名的解析用DNS
协议,远程登录用Telnet
协议等,都是属于TCP/IP
应用层。就用户而言,看到的是由一个个软件所构筑的、大多数为图形化的操作界面,而实际后台运行的便是上述协议。
传输层的功能主要是提供应用程序间的通信,TCP/IP
协议族在这一层的协议有TCP
和UDP
。
网络层是TCP/IP
协议族中非常关键的一层,主要定义了IP
地址格式,从而能够使得不同应用类型的数据在Internet
上传输,IP
协议就是一个网络层协议。
数据链路层是TCP/IP
软件的最底层,负责接收IP
数据包并通过网络发送之,或者从网络上接收物理帧,抽出IP数据报,交给IP
层。
TCP(Transmission Control Protocol)
和UDP(User Datagram Protocol)
协议属于传输层协议。其中TCP
提供IP
环境下的数据可靠传输,它提供的服务包括数据流传送、可靠性、有效流控、全双工操作和多路复用,通过面向连接、端到端和可靠的数据包发送。它是事先为所发送的数据开辟出连接好的通道,然后再进行数据发送。而UDP
则不为IP
提供可靠性、流控或差错恢复功能。
一般来说,TCP
对应的是可靠性要求高的应用,而UDP
对应的则是可靠性要求低、传输经济的应用。TCP
支持的应用协议主要有:Telnet
、FTP
、SMTP
等。UDP
支持的应用层协议主要有:NFS
(网络文件系统)、SNMP
(简单网络管理协议)、DNS
(主域名称系统)、TFTP
(通用文件传输协议)等。
IP
协议(Internet Protocol
)又称互联网协议,是支持网间互联的数据报协议,它与TCP
协议(传输控制协议)一起构成了TCP/IP
协议族的核心。它提供网间连接的完善功能,包括IP
数据包规定互连网络范围内的IP
地址格式。在互联网上,为了实现连接到网上的结点之间的通信,必须为每个结点(入网的计算机)分配一个地址,并且应当保证这个地址是全网唯一的,这便是IP
地址。
目前的IP
地址(IPv4
:IP
第4
版本)由32
个二进制位表示,每8
位二进制数为一个整数,中间由小数点间隔,如159.223.41.98
。整个IP
地址空间有4
组8
位二进制数,由表示主机所在的网络的地址,以及主机在该网络中的标识共同组成。为了便于寻址和层次化的构造网络,IP
地址被分为A、B、C、D、E
共5
类,商业应用中只用到A、B、C
这3
类。
A
类地址:A
类地址的网络标识由第一组8
位二进制数表示,网络中的主机标识占3
组8
位二进制数,A
类地址的特点是网络标识的第1
位二进制数取值必须为“0
”。不难算出,A
类地址允许有126
个网段,每个网络大约允许有1670
万台主机,通常分配给拥有大量主机的网络(如主干网)。
B
类地址:B
类地址的网络标识由前2
组8
位二进制数表示,网络中的主机标识占2
组8
位二进制数,B
类地址的特点是网络标识的前2
位二进制数取值必须为“10
”。B
类地址允许有16384
个网段,每个网络允许有65533
台主机,适用于结点比较多的网络(如区域网)。
C
类地址:C
类地址的网络标识由前3
组8
位二进制数表示,网络中主机标识占1
组8
位二进制数,C
类地址的特点是,网络标识的前3
位二进制数取值必须为“110
”。具有C
类地址的网络允许有254
台主机,适用于结点比较少的网络(如校园网)。
由于网络地址紧张、主机地址相对过剩,采取子网掩码的方式来指定网段号。TCP/IP
协议与低层的数据链路层和物理层无关,这也是TCP/IP
的重要特点,正因为如此,它能广泛地支持由低两层协议构成的物理网络结构。目前已使用TCP/IP
连接成洲际网、全国网与跨地区网。
Java
的引用和C++
的指针都是指向一块内存地址的,通过引用或指针来完成对内存数据的操作。但是它们在实现、原理、作用等方面却有区别。
**1)类型:**引用其值为地址的数据元素,Java
封装了的地址,可以转化成字符串查看,长度可以不必关心。C++
指针是一个装地址的变量,长度一般是计算机字长,可以认为是个int
。
**2)所占内存:**引用声明时没有实体,不占空间。C++
指针如果声明后被用到才会赋值,如果用不到不会分配内存。
**3)类型转换:**引用的类型转换,也可能不成功,运行时会抛出异常或者不能通过编译。C++
指针只是个内存地址,指向哪里,对程序来说都还是一个地址,但可能所指的地址不是程序想要的。
**4)初始值:**引用初始值为Java
关键字null
。C++
指针是int
类型,如不初始化指针,那它的值就不是固定的了,这很危险。
**5)计算:**引用是不可以计算的。C++
指针是int
,它可以计算,所以经常用指针来代替数组下标。
**6)控制:**引用不可以计算,所以它只能在自己程序里,可以被控制。C++
指针是内存地址,也可以计算,所以它有可能指向了一个不归自己程序使用的内存地址,对于其他程序来说是很危险的,对自己程序来说也是不容易被控制的。
7)内存泄露:Java
引用不会产生内存泄露。C++
指针是容易产生内存泄露的,所以程序员要小心使用,及时回收。
8)作为参数:Java
的方法参数只是传值,引用作为参数使用的时候,函数内引用的是值的COPY
,所以在函数内交换两个引用参数是没意义的,因为函数只交换参数的COPY
值,但在函数内改变一个引用参数的属性是有意义的,因为引用参数的COPY
所引用的对象和引用参数是同一个对象。C++
指针作为参数给函数使用,实际上就是它所指的地址
在被函数操作,所以函数内用指针参数的操作都将直接作用到指针所指向的地址(变量、对象、函数等)。
总的来说,Java
中的引用和C++
中的指针本质上都是想通过一个叫做引用或者指针的东西,找到要操作的目标,方便在程序里操作。所不同的是,Java
的办法更安全、方便一些,但没有C++
的指针那么灵活。
面向对象的思想把程序里的一切都看成是对象,对象拥有各种属性和动作。同时,这些对象拥有一种共性,也就是它们同属于一类。
在Java
里,把这种共性称为类(class
),每一个实例称为对象(object
)。在类里定义属性和方法,每个对象都用new
关键字和类名来创造。
类的构造方法是一种比较特殊的方法,它不能被程序员显式地调用,只能在创建对象时由系统自动调用。构造方法的名字必须与类名完全相同。另外,在没有提供任何的构造方法时,系统会为类创建一个默认的构造方法,该构造方法也是无参数的,它什么也不做,但是,一旦提供了任何一种构造方法,该默认的构造方法就不会被自动提供了。
当需要使用对象的属性和方法的时候,只需要通过对象的引用小数点号.
进行调用即可。
类也可以有属于它的属性和方法,也就是静态(static
)成员,通过static
关键字进行定义。这些静态属性和方法属于类所有,被该类的所有对象共享,但是它只有一份,并不会随着对象的创建而新增。
除了default
以外,其他都是Java
语言的关键字。default
代表的是对类成员没有进行修饰的情况,它本身也代表了一种访问控制符。对于这4
种访问控制符来说,它们都可以修饰类的成员(包括静态和非静态成员),它们的修饰就控制了被它们修饰的成员能被其他的地方访问的限制情况。
对于范围概念来说,Java
指的范围包括:类内部、所在包下、子父类之间和外部包4
种情况。如果一个成员需要被外部包所访问,则必须使用public
修饰符;如果一个成员需要被定义在不同包下的子类所访问,则可以使用public
或protected
修饰符;如果一个成员需要被本包下的其他类所访问,则可以不用写任何的修饰符,使用public
或protected
也行;若一个成员想使用同类里边的其他成员,则使用任意一个修饰符即可;若一个成员不想被任何一个外部的类所访问,则使用private
关键字比较恰当。
1)对于public
修饰符,它具有最大的访问权限,可以访问任何一个在CLASSPATH
下的类、接口、异常等。它往往用于对外的情况,也就是对象或类对外的一种接口形式。
2)对于protected
修饰符,它主要的作用就是用来保护子类的。它的含义在于子类可以使用它修饰的成员,其他的不可以,它相当于传递给子类的一种继承的东西。
3)对于default
来说,有的时候也称为friendly
(友员),它是针对本包访问而设计的,任何处于本包下的类、接口、异常等,都可以互相访问,即使是父类没有用protected
修饰的成员也可以。
4)对于private
来说,它的访问权限仅限于类的内部,是一种封装的体现,例如,大多数的成员变量都是修饰为private
的,它们不希望被其他任何外部的类访问。
Java
的访问控制是停留在编译层的,也就是它不会在class
文件里留下任何的痕迹,只在编译的时候进行访问控制的检查。其实,通过反射的手段可以访问任何包下任何类里边的成员,访问类的私有成员也是可能的。
Java
是一种强类型的语言,它对条件表达式有非常严格的规定,只能使用boolean
型的数据进行条件判断。如果使用整型的非0
数进行条件判断,则体现为语法错误。
通过String
直接相加来拼接字符串的效率是很低的,其中可能会产生多余的String
对象。如果程序中需要拼接的字符串数量成千上万的话,那么JVM
的负荷是非常大的,严重地影响到程序的性能。
如果遇到有大量字符串需要拼接的话,应该使用StringBuffer
和StringBuilder
类,它们是对String
的一种补充。
StringBuffer
无法保证线程的安全,StringBuilder
可以保证线程的安全。
可以不固定
foreach
循环就是遍历一个集合里的元素,起到替代迭代器的作用。从语法上来讲,数组或者实现了Iterable
接口的类实例,都是可以使用foreach
循环的。
数组是Java规定的东西,开发人员无法改变它,只能遵照它的使用语法来使用。但是,对于第二条规定“实现了Iterable
接口的类实例”,开发人员则可以自定义一个集合类。该自定义集合类主要需要做以下一些事情:
Iterable
接口。Iterator
接口的实现,或者它本身就实现Iterator
接口。Iterator
接口所需要的方法。 foreach
的运行原理也是比较简单的,它的主要运行步骤如下:
iterator()
方法,得到迭代器。hasNext()
方法判断是否有下一个元素来进行循环。next()
方法,就得到下一个元素。 对于对象的输入和输出,Java
的I/O
体系里主要提供了ObjectOutputStream
和ObjectInputStream
两个类供开发者使用,它们的大致使用思路如下:
java.io.Serializable
接口。long
型的常量serialVersionUID
。ObjectOutputStream
对象,然后调用writeObject()
方法。ObjectInputStream
对象,然后调用readObject()
方法,得到一个Object
类型的对象,然后再做类型的强制转换。 Java
提供了java.util.concurrent.ThreadPoolExecutor
类来使用线程池,通过它构造的对象,可以很容易地管理线程,并把线程代码与业务代码进行分离。
根据调用构造方法的不同,用反射机制来实例化一个类,可以有两种途径。
Class
类的newInstance()
方法即可;Constructor
实例,再用newInstance()
方法创建对象。SQL
语句。 数据库连接池技术是为了避免重复创建连接而设计的,它作为一个单独的程序模块运行,负责维护池子里面装的数据库的连接(Connection
)。程序员打开连接和关闭连接并不会造成真正意义上的连接创建和关闭,而只是连接池对连接对象的一种维护手段。
对于开发者来说,连接池与传统的JDBC
提供连接的方式不太一样,程序员必须使用数据源(Data source
)的形式获取连接池的连接,而数据源对象往往是以JNDI
的形式提供的。对于Java Web
和EJB
开发人员来说,需要参考一下具体的JavaEE
服务器关于连接池的使用手册。
抽象类是一种功能不全的类,接口只是一个抽象方法声明和静态不能被修改的数据的集合,两者都不能被实例化。
从某种意义上说,接口是一种特殊形式的抽象类,在Java
语言中,抽象类表示一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。
public static void main(String[] args) {
String str = new String("good"); //执⾏到这⼀⾏时,创建了⼏个对象?
String str1 = "good"; //执⾏到这⼀⾏时,创建了⼏个对象?
String str2 = new String("good"); //执⾏到这⼀⾏时,创建了⼏个对象?
System.out.println(str == str1); //输出结果是什么?
System.out.println(str.equals(str2)); //输出结果是什么?
System.out.println(str2 == str1); //输出结果是什么?
}
第2行处的答案是:两个对象。 第3行处的答案是:没有对象。 第4行处的答案是:1个对象。 第5行处的答案是:false。 第6行处的答案是:true。 第7行处的答案是:false。
ArrayList
和Vector
都使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢。由于Vector
使用了synchronized
方法(线程安全),通常性能上较ArrayList
差,而LinkedList
使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。