Starter
特点:
- 跨平台
- 面向对象
- 无指针 (无法直接操作内存,速度无法加快)
- 自动收集内存(占用系统资源,且无法实时收集无用资源)
缺点(相较于c,c++):
- 运行速度慢
- 占用系统资源多
跨平台
- 跨平台:编译后的文件跨平台编译为中间语言,再由解释器二次编译,解释执行)
- 平台:CPU处理器+操作系统OS,软硬件的结合 (不同的OS支持不同的CPU指令集)
对比:
源程序(.c):编译执行,速度快,无法跨平台,eg:
- 编译生成可执行文件
- windows编译器(VC) =>
.exe
- linux编译器(GCC,ICC) =>
.elf
- 其他OS编译器 => 其他OS运行程序
- windows编译器(VC) =>
- 编译生成可执行文件
源程序(.java):解释执行,速度慢,跨平台(暴露源程序)
- 编译生成中间码(平台无关)
- 执行
javac
命令:.java
=>.class
- 执行
- 解释执行(依赖平台)
- windows下执行
java
命令:调用windows的Java解释器,生成windows平台运行码执行 - linux下执行
java
命令:调用linux的Java解释器,生成linux平台运行码执行 - 其他平台下执行
java
命令:调用其他OS的Java解释器,生成其他OS运行码执行
- windows下执行
- PS: 这里编译器和解释器均由Sun公司提供
- 编译生成中间码(平台无关)
JDK介绍
JDK(Java Developent ToolKit)包含:
- Java编译器
- JRE (Java Runtime Environment):Java运行时环境
- JVM (Java Virtual Machine):Java虚拟机(平台相关),包含:
- 类加载器(装入代码)
- 字节码校验器(检查)
- Java解释器(解释执行)
- Java 运行支持文件 (类似内置函数)
- JVM (Java Virtual Machine):Java虚拟机(平台相关),包含:
PS:
- JAVA SDK (Java Software develop kit):JDK的另一个称呼而已
- 若不需要进行Java程序开发,只需安装JRE即可
- JVM会执行垃圾回收,安全性检查等
JDK安装
windows下
- 安装exe(eg: jdk-7u25-windows-i586.exe)
//路径 D:\Soft\Java (JDK) D:\Program Files\Java (JRE,另放一个目录下或无需更改)
- 配置环境变量
Java_Home= D:\Soft\Java (JDK的安装路径) Path=%Java_Home%\bin (JDK的bin目录,D:\Soft\Java\bin) ClassPath=%Java_Home%\jre\lib\rt.jar;. (D:\Soft\Java\jre\lib\rt.jar)
- 测试JDK是否安装成功(cmd下)
> javac
Linux下
- 安装rpm(eg: jdk-8u45-linux-x64.rpm)
> rpm -ivh jdk-8u45-linux-x64.rpm # 默认安装到/usr/java > rpm -q --whatprovides java
配置环境变量
vi /etc/profile
export JAVA_HOME=/usr/java/jdk1.8.0_45 export PATH=$PATH:$JAVA_HOME/bin export CLASSPATH=.:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
source /etc/profile
测试JDK是否安装成功
> java -version > javac -version
切换使用另一版本的JDK(设置默认JDK)
> cd /usr/bin > ln -s -f /usr/java/jdk1.7.0_10/jre/bin/java > ln -s -f /usr/java/jdk1.7.0_10/bin/javac > java -version > javac -version
Java cmd
- 编译:
javac <Options> <Sourcefile>
-classpath <路径>
:指定用户类文件的位置-encoding <编码>
:指定源文件中所用的字符集编码-d <目录>
:指定输出类文件的位置
- 执行:
java <class>
,java -jar <jar>
打包:
jar {ctxu}[vfm0M] [destination] [manifest] [input-files]
{ctxu}
:子命令,每次jar命令只能包含其中的一个-c
:创建新的JAR文件包-t
:列出JAR文件包的内容列表-x
:展开JAR文件包的指定文件或者所有文件-u
:更新已存在的JAR文件包(添加文件到JAR文件包中)
[vfm0M]
:可选项-v
:生成具体报告并打印到标准输出-f
:指定JAR文件名,通常这个参数是必须的-m
:指定需要包含的MANIFEST清单文件-0
:只存储,不压缩,这样产生的JAR文件包会比不用该参数产生的体积大,但速度更快-M
:不产生所有项的清单(MANIFEST〕文件,此参数会忽略-m参数。
[destination]
:需要生成、查看、更新或者解开的包,-f
参数的附属参数[manifest]
:MANIFEST清单文件,它是-m
参数的附属参数- PS:使用JDK的jar命令打包,会自动在压缩包中生成一个
META-INF
目录,其中有一个MANIFEST.MF
文件,eg:Manifest-Version: 1.0 Main-Class: com.cj.mytest.DailyMail Class-Path: lib/slf4j-log4j12-1.6.6.jar lib/slf4j-api-1.6.6.jar lib/log4j-1.2.17.jar
使用示例:
javac Hello.java # 生成Hello.class java Hello # 执行Hello的main方法 jar -cvf test.jar *.* # 打jar包 java -jar test.jar # 执行test.jar包的main方法 jar -cvf test.war *.* # 打war包
三个分支
根据应用方向解决问题的不同,Java分为三个分支:
- JavaSE(Java Standard Edition)
- 为创建和运行Java提供了最基本的环境
- Java基础
- 支持C/S架构
- 为创建和运行Java提供了最基本的环境
- JavaEE(Java Enteriprise Edition)
- 为基于服务器的分布式企业应用提供开发和运行环境
- 支持B/S架构
- 企业级功能
- 为基于服务器的分布式企业应用提供开发和运行环境
- JavaME(Java Micro Edition)
- 为嵌入式应用提供开发和运行环境,一套比JavaSE精简高效的类库
数据类型
数据类型
- 值类型:基本数据类型
- 整数:long,int,short,byte
- 浮点数:float,double
- 字符:char
- 布尔:boolean
- 引用类型:底层封装指针的数据类型
- 数组
- 类
- 接口
注意:字符串 String为引用类型
存放结构
- 栈:
- 先进后出,
{}
后回收清空,存放变量 - 内容可以为:值类型的值,引用对象的地址
- 先进后出,
- 堆:
- 自动回收存放(哪空放哪)
- 内容:引用对象的具体内容
- 可通过
Runtime
类获取JVM内存信息- getRuntime():获取Runtime对象
- totalMemory():获取JVM分配给程序的内存数量
- freeMemory():获取当前可用的内存数量
- maxMemory():获取JVM可以申请到的最大内存数量
- PS:JVM通过OS管理内存空间(创建,收集,...)
PS:方法中变量传递:
- 会在栈中备份一份数值给参数变量
- 值传递:使用替身(因为栈中存放的是具体内容)
- 引用传递:使用真身(因为栈中存放的是内存地址)
异常处理
异常处理:
- 自行处理:try,catch,finally
- 回避处理: throw,throws
字符串
String
String是类(引用数据类型)
- 使用
new
关键字创建String a=new String("Hello"); String b=new String("Hello"); System.out.println(a==b); //false System.out.println(a.equals(b)); //true
- 直接创建(会自动使用字符串池)
String a="Hello"; String b="Hello"; System.out.println(a==b); //true System.out.println(a.equals(b)); //true
String a=new String("Hello"); String b="Hello"; System.out.println(a==b); //false System.out.println(a.equals(b)); //true
说明:
==
:比较的是两个变量是否指向同一块空间equals()
:比较的是指向的两个内存空间的值是否相等- 字符串池
- 存放直接创建的字符串(没有使用new关键字创建的)
- 对于直接创建的字符串,首先会去字符串池找有没有相同值的字符串:
- 如果有就直接指向它;
- 如果没有就创建新的空间,且放入字符串池
StringBuffer & StringBuilder
字符串的拼接时,JVM每次都再给分配一块新的内存空间,原来分配的那块空间会变为垃圾内存等待回收,造成浪费
StringBuffer
& StringBuilder
- 创建时系统默认分配一定长度的内存
- 使用
append方法
往其中放入字符串 - 如果放入字符串超出分配的范围,则会自动在后面追加一倍长度的内存(这样前面的内存就可以继续使用,没有浪费)
- 区别:
- StringBuffer 同步,线程安全
- StringBuilder 线程非安全,但效率高
String.format
格式化数字:
@Test
public void testDigit()
{
System.out.println(String.format("%1$,09d", -3123)); // -0003,123
System.out.println(String.format("%1$9d", -31)); // -31
System.out.println(String.format("%1$-9d", -31)); //-31
System.out.println(String.format("%1$(9d", -31)); // (31)
System.out.println(String.format("%1$#9x", 5689)); // 0x1639
// 数字9表示九位
System.out.println(String.format("%9d", 12345678)); // 不足9时,也要空出空间: 12345678
System.out.println(String.format("%-9d", 12345678)); //12345678
System.out.println(String.format("%,09d", 123456)); //00123,456
// 如为负数,则用括号括起来,如不是则不处理
System.out.println(String.format("%(9d", -123456)); // (123456)
System.out.println(String.format("% 9d", -123456)); // -123456
// 选择第二个args,即选择”1234“,而非”12345678“
System.out.println(String.format("%2$9d", 12345678, 1234)); // 1234
System.out.println(String.format("%#9x", 255)); // 0xff
System.out.println(String.format("%#9o", 255)); // 0377
System.out.println(String.format("%9x", 255)); // ff
System.out.println(String.format("%9o", 255)); // 377
System.out.println(String.format("%d%%", 12)); //12%
}
格式化日期:
@Test
public void testDate()
{
Date date = new Date();
System.out.println(String.format("%td", date)); // 输出两位数字的日期,不够位补零:06
System.out.println(String.format("%te", date)); // 输出两位数字的日期,不补零:6
System.out.println(String.format("%tm", date)); // 输出两位数字的月份,不够位自动补零:06
System.out.println(String.format("%ty", date)); // 输出两位数字的年份:14
System.out.println(String.format("%tY", date)); // 输出四位数字的年份:2014
System.out.println(String.format("%tj", date)); // 输出一年中的第几天(001-366):157
System.out.println(String.format("%ta", date)); // 输出指定语言环境的星期简称:Fri
System.out.println(String.format("%tA", date)); // 输出指定语言环境的星期全称:Firday
System.out.println(String.format("%tb", date)); // 输出指定语言环境的月份简称:Jun
System.out.println(String.format("%tB", date)); // 输出指定语言环境的月份全称:June
System.out.println(String.format("%tc", date)); // 输出指定语言环境的全部日期和时间信息:Fri Jun 06 09:07:56 CST 2014
}
格式化时间:
@Test
public void testTime()
{
Date date = new Date();
System.out.println(date); //Fri Jun 06 09:08:11 CST 2014
System.out.println(String.format("%tH", date)); // 输出两位24小时制的小时:09
System.out.println(String.format("%tk", date)); // 输出不补零24小时制的小时:9
System.out.println(String.format("%tI", date)); // 输出两位12小时制的小时:09
System.out.println(String.format("%tl", date)); // 输出不补零12小时制的小时:9
System.out.println(String.format("%tM", date)); // 输出两位的分钟:08
System.out.println(String.format("%tS", date)); // 输出两位的秒钟:11
System.out.println(String.format("%tZ", date)); // 输出时区缩写形式的字符串:CST
System.out.println(String.format("%tp", date)); // 输出特定语言环境的上午或下午标记:am
}
格式化时间日期:
@Test
public void testDateTime()
{
Date date = new Date();
System.out.println(String.format("%tR", date)); // 输出24小时制,被格式化为”%tH:%tM:%tS“:09:10
System.out.println(String.format("%tT", date)); // 输出24小时制,被格式化为”%tH:%tM“:09:10:23
System.out.println(String.format("%tr", date)); // 输出24小时制,被格式化为”%tH:%tM:%tS:%tp“:09:10:23 AM
System.out.println(String.format("%tD", date)); // 输出日期,被格式化为”%tm/%td/%ty“:06/06/14
System.out.println(String.format("%tF", date)); // 输出日期,ISO 8061格式的完整日期,被格式化”%tY-%tm-%td“:2014-06-06
System.out.println(String.format("%tc", date)); // 输出日期和时间,被格式化”%ta%tb%td%tT%Z%tY“:Fri Jun 06 09:10:23 CST 2014
}
时间处理
SimpleDateFormat
@Test
public void testFormatDateTime() throws ParseException{
SimpleDateFormat sdf = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss a",Locale.ENGLISH);
Date now=new Date();
System.out.println(sdf.format(now));
}
@Test
public void testParseTime() throws ParseException{
SimpleDateFormat sdf = new SimpleDateFormat ("h:mma",Locale.ENGLISH);
Date d=sdf.parse("6:20pm");
System.out.println(d);
}
ISO8601Utils
@Test
public void testISOTime() throws ParseException{
System.out.println("-----------1------------------");
String isoStr="2014-12-17T00:00:00Z";
Date date=ISO8601Utils.parse(isoStr);
System.out.println(date);
System.out.println("-----------2------------------");
String dateStr="2014-12-17";
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
date=sdf.parse(dateStr);
System.out.println(date);
System.out.println(ISO8601Utils.format(date));
System.out.println("-----------3------------------");
Calendar c=Calendar.getInstance();
c.set(Calendar.YEAR, 2014);
c.set(Calendar.MONTH,11);
c.set(Calendar.DAY_OF_MONTH,17);
date=c.getTime();
System.out.println(date);
System.out.println(ISO8601Utils.format(date));
System.out.println("-----------4------------------");
c.set(Calendar.HOUR_OF_DAY,0);
c.set(Calendar.MINUTE,0);
c.set(Calendar.SECOND,0);
date=c.getTime();
System.out.println(date);
System.out.println(ISO8601Utils.format(date));
}
类
封装
封装: 隐藏属性,方法和方法实现细节的过程
包与访问修饰符:
- public :所有类都能访问
- protected :子类可以访问(可在不同包)
- 默认 :同包中可访问
- private :同类中看访问
继承
继承: 在一个类基础上定义一个新类 (Java中只有单继承)
- 子类拥有父类的属性和方法
this
调用本类中的属性和方法super
调用父类中的属性和方法
- 子类可以覆盖父类的方法(方法重写,方法覆盖)
- 方法覆盖(方法重写):子类方法与父类方法具有相同的方法声明(方法头),不同的实现(方法体)
- 可是用关键字@Override约束(便于在编译时就发现错误)
- 可以声明父类,创建子类
- 创建时会先调用父类构造方法
- 默认调用父类无参构造方法
- 在构造方法中,可使用super调用父类其他构造方法(必须置于第一行)
- 只能调用声明类型所包含的属性和方法签名
- 具体执行时调用创建类型的方法
- 创建时会先调用父类构造方法
示例:
public class Father{
private String name="Father";
public void t1(){
System.out.println(this.name+" t1");
}
public void t2(){
System.out.println(this.name+" t2");
}
}
public class Son extends Father{
private String name="Son";
@Override
public void t2(){
System.out.println(this.name+" t2-extends "+super.name);
}
public void t3(){
System.out.println(this.name+"t3"+);
}
}
@Test
public void testExtend(){
Father father= new Son(); //声明父类,创建子类
father.t1(); //Father t1
father.t2(); //Son t2-extends Father
((Son)father).t3() //Son t3
}
Object类
- 老祖宗
- 如果某个类没有父类,那它就默认的继承自Object类
public String toString()
:当打印对象时只写对象名不写方法名,默认调用此方法protected void finalize() throws Throwable
:类似C++的析构方法,在对象消亡时由系统自动调用public boolean equals(Object obj)
:比较两个对象的数值(堆空间中存储的对象数值)是否一致,一般也由子类覆盖hashCode
wait()
- ...
多态
多态: 具有表现多种形态能力的特征
同一个接口,使用不同的实例,执行不同的操作 (运行时,JVM根据实际创建的对象类型,动态调用对应方法)
抽象类
抽象类
- 使用
abstract
关键字修饰类,eg:public abstract class A{...}
- 可包含抽象方法(无方法体)和非抽象方法
- 子类必须实现所有抽象方法
接口
接口
- 使用
interface
关键字,eg:public interface A{...}
- 高度抽象类,方法特征的集合,只能继承自接口
- 成员自动设为
public
,不能设为private - 属性自动设为
public final static
,即为公有常量 - 一个类可以实现多个接口
Enum 枚举
- 实际是继承自
java.lang.Enum<E>
,且不可被继承(final
)的一个类 - 继承自Enum的方法
ordinal()
返回枚举值在枚举类种的顺序(根据枚举值声明的顺序而定)compareTo(enum)
返回的是两个枚举值的顺序之差(Enum实现了Comparable接口)values()
静态方法,返回一个包含全部枚举值的数组toString()
返回枚举常量的名称valueOf(enumName)
返回带指定名称的指定枚举类型的枚举常量 (和toString方法是相对应的)equals(enum)
比较两个枚举类对象的引用
- 其枚举值都是类静态常量(
public static final
) - 构造函数:只能私有private,不能有public
- 保证外部代码无法新构造枚举类的实例
- 只是在构造枚举值的时候被调用
示例:
public enum Color{
RED,BLUE,BLACK,YELLOW,GREEN
}
扩展:
public enum Color{
RED(255,0,0),BLUE(0,0,255),BLACK(0,0,0),YELLOW(255,255,0),GREEN(0,255,0);
//自定义数据域
private int redValue;
private int greenValue;
private int blueValue;
//构造枚举值,比如RED(255,0,0)
private Color(int rv,int gv,int bv){
this.redValue=rv;
this.greenValue=gv;
this.blueValue=bv;
}
}
使用:
@Test
public void testEnum(){
Color.RED.ordinal(); // return: 0
Color.BLUE.ordinal(); //return: 1
Color.RED.compareTo(Color.BLUE); //return: -1
Color[] colors=Color.values();
for(Color c:colors){
System.out.print(c+",");
}//print: RED,BLUE,BLACK YELLOW,GREEN
Color c=Color.RED;
System.out.println(c); //print: RED
Color.valueOf("BLUE"); //return: Color.BLUE
}
final
- 属性上:常量
- 方法上:不能被子类重写
- 类上:不能被继承
static
static:在类中使用,方便在没有创建对象的情况下来进行调用(方法/变量)
static方法 & static变量
- 属于类,类初次加载时初始化,在内存中只有一个副本
- 不依赖于任何对象就可以进行访问
- 可通过
类.xxx
调用,也可使用该类的对象.xxx
调用 - static方法中不能使用
this
,不能访问类的非静态方法和非静态成员方法(因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用) - final结合使用:
- 修饰成员变量:全局常量,不可改值
- 修饰成员方法:可通过类名访问,且不可覆盖
- PS:通过static final修饰的容器类型变量中所“装”的对象是可改变的
- 注意:
- static不会改变变量和方法的访问权限
- static不允许用来修饰局部变量
- 类的构造器实际上也是静态方法(只是没有显示地声明而已)
static代码块(可以用来优化程序性能)
- 在类加载的时候按static代码块的顺序各执行一次
- 可以置于类中的任何地方
示例:
public class Test {
Person person = new Person("Test");
static{
System.out.println("test static");
}
public Test() {
System.out.println("test constructor");
}
public static void main(String[] args) {
new MyClass();
}
}
public class Person{
static{
System.out.println("person static");
}
public Person(String str) {
System.out.println("person "+str);
}
}
public class MyClass extends Test {
Person person = new Person("MyClass");
static{
System.out.println("myclass static");
}
public MyClass() {
System.out.println("myclass constructor");
}
}
Result: (各个类的static块->某对象成员变量->某对象构造方法)
test static
myclass static
person static
person Test
test constructor
person MyClass
myclass constructor
自定义注解
示例:
定义注解
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelResources {
String title() default "";
int[] order() default {};
public Class<? extends ExcelFormat> format() default ExcelFormat.None.class;
public boolean isRefClass() default false;
}
使用注解
public class Schedule{
@ExcelResources(title="标识",order=1)
public int getId() {
return id;
}
@ExcelResources(title="Boarding Time",order=3,format=ScheduleFormat.class)
public CrossSchedule getSchedule(){
return schedule;
}
...
}
public class ScheduleFormat extends ExcelFormat{
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
@Override
public String format(Object obj){
if(obj!=null && obj instanceof CrossSchedule){
CrossSchedule schedule=(CrossSchedule)obj;
return sdf.format(schedule.getServiceDate())+" ("+schedule.getDayOfWeekCn()+")";
}
return null;
}
}
解析处理注解
Method[] methods = Schedule.class.getDeclaredMethods();
for(Method method:methods) {
if(method.isAnnotationPresent(ExcelResources.class)) {
ExcelResources er = method.getAnnotation(ExcelResources.class);
String title=er.title();
int[] orders=er.order();
ExcelFormat format=er.format();
boolean isRef=er.isRefClass();
...
}
}
数组 && 集合框架
数组
创建数组(赋初值与赋长度只能出现一个):
数据类型[] 数组名 = new 数据类型[长度];
数据类型 数组名[] = new 数据类型[长度];
数据类型[] 数组名 = new 数据类型[]{初始值,初始值};
数据类型[] 数组名 = {初始值,初始值};
eg:
一维数组:
int[] a=new a[3];
a[0]=1;
a[1]=2;
a[2]=3;
int[] a={1,2,3};
二维数组:
int[][] a=new a[2][3];
a[0]={1,2,3};
a[1]={4,5,6};
int[][] a={
{1,2,3},
{4,5,6}
}
集合框架
说明:
- Collection & Map
- Collection:直接把元素放入集合
- Map:存放的是键值对,键不能重复,值可以重复
- List & Set
- List:有下标,元素可重复
- Set: 无下标,元素不可重复
- Vector & ArrayList
- Vector:同步,线程安全
- ArrayList:线程非安全
- Hashtable & HashMap
- Hashtable:同步,线程安全,不允许key或value为null
- HashMap:线程非安全,允许key或value为null
Collections
- Collection的算法类
- 全是静态方法,eg:
- 排序:sort(list),sort(list, comparator)
- 求最大值:max(list)
- 求最小值:min(list)
均在java.util包中(包括Collections)
Collection使用示例:
Student stu1=new Student(1,"Tom");
Student stu2=new Student(2,"Jerry");
Student stu3=new Student(3,"Lucy");
List<Student> students=new ArrayList<Student>();
students.add(stu1);
students.add(stu2);
students.add(0,stu3); //students:(3,Lucy),(1,Tom),(2,Jerry)
students.add(stu1); //students:(3,Lucy),(1,Tom),(2,Jerry),(1,Tom)
stu1.setName("Susan"); //students:(3,Lucy),(1,Susan),(2,Jerry),(1,Susan)
students.remove(1); //students:(3,Lucy),(2,Jerry),(1,Susan)
students.remove(stu1); //students:(3,Lucy),(2,Jerry)
students.contains(stu1); //return false;
students.contains(stu2); //return true;
students.contains(new Students(2,"Jerry")); //false,若重写Student的equals方法可为true
for(int i=0;i<students.size();i++){
System.out.println(students.get(i));
}
for(Student stu:students){
System.out.println(stu);
}
Interator<Student> it=students.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
Map使用示例:
Map<Integer,Student> studentMap=new HashMap<Integer,Student>();
studentMap.put(1,stu1);
studentMap.put(2,stu2);
studentMap.put(3,stu3);
studentMap.put(null,null);
studentMap.containsKey(2);
Student stu=studentMap.get(3);
Set<String> keys=studentMap.keySet();
Collection<Student> values=studentMap.values();
for(String key:keys){
System.out.println(studentMap.get(key));
}
for (Map.Entry<Integer, Student> entry : map.entrySet()) {
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
Collections使用:排序
方式一:
Collections.sort(students);
Collections.reverse(students);
public class Student implements Comparable<Student>{
...
@Override
public int compareTo(Student o){
return id - o.getId(); //正数:一个比一个大
}
}
方式二:
Collections.sort(students,new StudentComparator());
public class StudentComparator implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
return o2.id-o1.id;
}
}
IO && 反射 && 序列化
IO
File类:处理文件、文件夹(不能编辑文件)
File file=new File(fileUrl); if(file.exists()) file.delete(); file.createNewFile(); file.mkdir(); File dir=new File(dirUrl); dir.isDirectory(); String[] list=dir.list(); File[] str=dir.listFiles();
- 流
- 字节流(节点流):直接创建的流
InputStream/OutputStream
InputStream in=FileInputStream(fileUrl); OutputStream out=FileOutputStream(fileUrl2); int len=0; byte[] buffer=new byte[512]; while((len=in.read(buffer))!=-1){ System.out.println(buffer); out.write(buffer,0,len); } in.close(); out.close();
- 字符流(过滤流):需依赖已创建的节点流
Reader/Writer
BufferedReader reader=new BufferedReader(new FileReader(fileUrl)); String str=null; while ((str=reader.readLine())!=null) { System.out.print(str); }
- 转换流:字节流=>字符流
InputStreamReader/OutputStreamWriter
File file=new File(fileUrl); Reader reader=InputStreamReader(new FileInputStream(file)); String str=null; while ((str=reader.readLine())!=null) { System.out.print(str); } reader.close();
- 字节流(节点流):直接创建的流
反射
程序运行中,动态获取对象的信息,或构造某个对象 (可加载运行时才得知名称的class,并创建)
Class cls=class.forName("com.cj.my.MyTest");
MyTest myTest=(MyTest)cls.newInstance();
序列化
序列化:读取内存中对象的机制(把对象写到流里)
implements Serializable
或implements Externalizable
- 引用类型的成员也需可序列化
- 可使用
transient
关键字标识不序列化 - 在类中实现以下方法,可以以更细粒度的方式控制序列化/反序列化的过程
private void writeObject(ObjectOutputStream out) throws IOException
private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException
- 注意:static的也无法序列化(序列化保存的是对象的状态,static属于类的状态)
反序列化:序列化的反过程,将对象读取到内存的机制(把对象从流中读出来)
使用示例:
将Java 对象序列化为二进制文件,使用ObjectInputStream
和 ObjectOutputStream
进行对象的读写
public class Person implements Serializable
{
int age;
String name;
double height;
public Person(int age, String name, double height){
this.age = age;
this.name = name;
this.height = height;
}
//序列化控制
private void writeObject(ObjectOutputStream out) throws IOException
out.writeInt(age);
out.writeUTF(name);
System.out.println("write object");
}
//反序列化控制
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException{
age = in.readInt();
name = in.readUTF();
System.out.println("read object");
}
}
@Test
public void serializeTest(){
//序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Person.txt"));
Person p = new Person2(20, "zhangsan", 4.55);
oos.writeObject(p);
oos.close();
//反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Person.txt"));
p = (Person) ois.readObject();
System.out.println(p.age + "," + p.name + "," + p.height);
ois.close();
}
JDBC
JDBC:Java DataBase Connectivity,提供连接各种数据库的能力
- JDBC API :提供了Java与各种不同数据库交互的标准接口
- Connection 与数据库建立连接
- Statement 发送SQL语句
- ResultSet 处理结果
- JDBC 驱动:由各个数据库厂商提供,负责连接对应的数据库(实现了JDBC API 中定义的各种接口)
- JDBC-ODBC桥接:将对JDBC API的调用,转换为对另一组数据库(ODBC)API的调用(rt.jar 中有实现,无需再导入)
- JDBC Driver Manager:管理各种不同的JDBC驱动
使用:
- 加载JDBC驱动
Class.forName(“JDBC 驱动类的名称”); // JDBC 驱动字符串:“包名.类名”
- 建立连接
Connection conn= DriverManager.getConnection(URL,user,password);
发送SQL语句,获取执行结果
//使用Statement Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(“查询的SQL语句”); //返回结果集对象 int rows = stmt.executeUpdate(“增删改的SQL语句”); //返回受影响的行数 //循环结果集 while(rs.next()){ int x= rs.getInt("id"); // 或rs.getInt(1); String strName =rs.getString(“name”); … }
//使用预处理器PreparedStatement(继承自Statement接口,快,灵活) String strSql=“UPDATE TABLE1 SET a=? WHERE b=?”; PreparedStatement pstmt = conn.prepareStatement(strSql); pstmt.setString(1,”Hello”); pstmt.setInt(2,10); int rows = pstmt.executeUpdate(); //ResultSet rs = pstmt.executeQuery();
关闭资源(一般放于finally 块中)
rs.close(); //关闭记录集 stmt.close(); //关闭处理器对象 conn.close(); //关闭连接
线程
线程的5个状态:
- 创建(
new
)- new时
- 可运行(
runnable
)- 调用start()后
- sleep结束后
- 被notify唤醒后
- I/O完成
- 运行(
running
) - 不可运行 (
blocked/waiting
)- 调用了sleep()
- 调用了wait()等待特定通知
- I/O阻塞
- 消亡(
dead
)- run()执行完成后(注意:不能使用stop()终止线程的执行)
说明:
thread.sleep(ms)
:睡眠,线程阻塞(不释放锁)object.wait()
:等待,放入与该对象相关的等待池中(执行线程释放锁)object.notify()/notifyAll()
:唤醒对应等待池中的线程(被唤醒的线程重新获得锁)thread.join()/join(ms)
:- 等待此线程死亡后再继续(可使异步线程变为同步线程)
- 不会释放锁
thread.yield()
:退让,把运行机会让给了同等优先级的其他线程- 让当前线程暂时交出CPU执行时间
- 不能控制具体的交出时间
- 不会释放锁
- 只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行
thread.interrupt()
:中断处于阻塞状态的线程(抛出InterruptedException),配合isInterrupted()
能够中断正在运行的线程
线程类
线程类实现方式:
- 方式一:
implements Runnable
@Override run()
- 示例:
public class MyRunnable implements Runnable{ @Override public void run(){ ... } } //创建使用 Thread myThread=new Thread(new MyRunnable()); myThread.start();
- 方式二:
extends Thread
(实际上public class Thread implements Runnable
)@Override run()
- 示例:
public class MyThread extends Thread{ @Override public void run(){ ... } } //创建使用 MyThead myThread=new MyThread(); myThread.start();
线程同步
- Java中的每个对象都有一个锁(lock)或者叫做监视器(monitor)
- 不同的线程在执行同一个对象的同步代码块时,因为要获得对象的同步锁而互相牵制
- 当访问某个对象的synchronized方法时,将该对象上锁,此时其他任何线程都无法再去访问该synchronized方法
- 在synchronized方法中:使用wait()释放锁,暂停执行;直到被notify(),重新获取锁,继续执行
- 当synchronized方法执行完或发生异常时,会自动释放锁
- 注意:
- synchronized声明不会被继承
- 在一个非静态方法中 ,使用this(当前的实例对象)代表锁对象
- 在静态方法中,锁对象是类(用Object.class,而不能用this)
示例1:创建10个线程,每个线程都打印从0到99这100个数字,希望线程之间不会出现交叉乱序打印,而是顺序地打印
实现方式1:每个线程的传入唯一的共享的对象锁
public class MyThread implements Runnable{
private int threadId;
private Object lock;
public MyThread(int id,Object lock){
this.threadId = id;
this.lock=lock;
}
@Override
public void run() {
synchronized(lock){
for (int i = 0; i < 100; ++i){
System.out.println("Thread ID: " + this.threadId + " : " + i);
}
}
}
}
@Test
public static void main(String[] args) throws InterruptedException{
Object obj=new Object();
for (int i = 0; i < 10; ++i){
new Thread(new MyThread(i,obj)).start();
Thread.sleep(1);
}
}
使用方式二:使用静态变量或静态方法
//静态方法或变量是所有类实例对象所共享的,因此线程对象在访问此静态方法时是互斥访问的,从而可以实现线程的同步
public class MyThread implements Runnable{
private int threadId;
//定义静态变量作为锁
private static Object lock = new Object();
public MyThread(int id){
this.threadId = id;
}
@Override
public void run() {
//方式一:使用静态方法
//taskHandler(this.threadId);
//方式二:使用静态变量
synchronized(lock){
for (int i = 0; i < 100; ++i){
System.out.println("Thread ID: " + this.threadId + " : " + i);
}
}
}
//在静态方法中加入sychronized关键字封锁的就是类本身
private static synchronized void taskHandler(int threadId) {
for (int i = 0; i < 100; ++i) {
System.out.println("Thread ID: " + threadId + " : " + i);
}
}
}
@Test
public static void main(String[] args) throws InterruptedException{
for (int i = 0; i < 10; ++i){
new Thread(new MyThread(i)).start();
Thread.sleep(1);
}
}
示例2:
@Test
public void testThread(){
Operation operation = new Operation();
Thread t1 = new IncreaseThread(operation);
Thread t2 = new DecreaseThread(operation);
Thread t3 = new IncreaseThread(operation);
Thread t4 = new DecreaseThread(operation);
t1.start();
t2.start();
t3.start();
t4.start();
}
public class Operation{
private int number;
public synchronized void increase(){
while (0 != number){
try{
wait();
}catch (InterruptedException e){
e.printStackTrace();
}
}
number++;
System.out.println(number);
notify();
}
public synchronized void decrease(){
while (0 == number){
try{
wait();
}catch (InterruptedException e){
e.printStackTrace();
}
}
number--;
System.out.println(number);
notify();
}
}
public class IncreaseThread extends Thread{
private Operation operation;
public IncreaseThread(Operation operation){
this.operation = operation;
}
@Override
public void run(){
for(int i = 0; i < 20; i++){
try{
Thread.sleep((long)(Math.random() * 1000));
}catch (InterruptedException e){
e.printStackTrace();
}
operation.increase();
}
}
}
public class DecreaseThread extends Thread{
private Operation operation;
public DecreaseThread(Operation operation){
this.operation = operation;
}
@Override
public void run(){
for(int i = 0; i < 20; i++){
try{
Thread.sleep((long)(Math.random() * 1000));
}catch (InterruptedException e){
e.printStackTrace();
}
operation.decrease();
}
}
}
线程池
Java通过class Executors
提供的静态方法,创建四种线程池(interface ExecutorService extends Executor
)
Executors.newSingleThreadExecutor(..)
- 创建唯一的工作线程来执行任务
- 如果这个线程异常结束,会有另一个取代它
- 保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
Executors.newFixedThreadPool(...)
- 创建一个指定工作线程数量的线程池
- 不会释放空闲线程
- 定长线程池的大小最好根据系统资源进行设置(如
Runtime.getRuntime().availableProcessors()
)
Executors.newCachedThreadPool(...)
- 创建一个可缓存的线程池
- 会回收空闲线程,释放相应资源
Executors.newScheduleThreadPool(...)
- 创建一个定长的线程池
- 支持定时及周期性的任务执行,类似于Timer
- 使用:
- 直接使用
execute(Runnable command)
方法ExecutorService pool = Executors.newXxxThreadExecutor(); pool.execute(new Runnable(){ public void run(){ //执行任务 } }
- 使用
submit(Runnable task)
,submit(Callable<T> task)
提交任务并等待获取执行结果ExecutorService pool = Executors.newXxxThreadExecutor(); Future<?> future=pool.submit(new Runnable(){ //执行任务 }) //获取执行结果 future.get();
- 直接使用
- 注意:
Executor
并不是一个线程池,而只是一个执行线程的工具- 真正的线程池接口是
ExecutorService
Executors
实现的线程池,底层使用的就是ThreadPoolExecutor
(interface ExecutorService
的一种实现)- 构造方法:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
corePoolSize
:池中所保存的线程数,包括空闲线程maximumPoolSize
:池中允许的最大线程数keepAliveTime
: 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间unit
: keepAliveTime 参数的时间单位workQueue
: 执行前用于保持任务的队列,此队列仅保持由 execute方法提交的 Runnable任务threadFactory
: 执行程序创建新线程时使用的工厂handler
: 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序
- 构造方法:
使用示例:
@Test
public void testSingleThreadPool(){
//结果依次输出,相当于顺序执行各个任务
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
singleThreadExecutor.execute(new Runnable() {
public void run() {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
@Test
public void testFixedThreadPool() {
//线程池大小为3,每个任务输出index后sleep 2秒,所以每两秒打印3个数字
//若任务数量大于 poolSize ,任务会被放在一个 queue 里顺序执行
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int index = i;
fixedThreadPool.execute(new Runnable() {
public void run() {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
@Test
public void testCachedThreadPool(){
//线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
try {
Thread.sleep(index * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
cachedThreadPool.execute(new Runnable() {
public void run() {
System.out.println(index);
}
});
}
}
@Test
public void testScheduleThreadPool(){
//延迟1秒后每3秒执行一次
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
public void run() {
System.out.println("delay 1 seconds, and excute every 3 seconds");
}
}, 1, 3, TimeUnit.SECONDS);
}
PS:可以使用JDK自带的监控工具(JDK安装目录/bin/jconsole.exe
)来监控创建的线程
ThreadLocal
线程本地变量/线程局部变量:
- 相当于一个工具类(辅助管理各个线程的本地变量们),通常是全局的,支持泛型
- 各线程保存自己使用的对象们(每一个线程都维护各自的变量副本们)
- 各个Thread内部都有一个ThreadLocalMap成员变量
- ThreadLocalMap:键为ThreadLocal对象(因为每个线程中可有多个threadLocal变量),值为对应线程的变量副本的值
- 提供的方法:
void set(T value)
:将此ThreadLocal的当前线程副本中的值设置为指定值void remove()
:移除此ThreadLocal的当前线程的值protected T initialValue()
:返回此ThreadLocal的当前线程的“初始值”T get()
:返回此ThreadLocal的当前线程副本中的值(若无,则初始化,内部会调用initialValue方法返回的值进行设置)//ThreadLocal中get方法的定义: public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } return setInitialValue(); }
- 应用场景: 数据库连接、Session管理
public class ConnectionManager{ private static ThreadLocal<Connection> theadLocal= new ThreadLocal<Connection>() { public Connection initialValue() { return DriverManager.getConnection(DB_URL); } } public static Connection getConnection() { return theadLocal.get(); } public static void closeConnection(){ Connection conn = threadLocal.get(); try { if(conn != null && !conn.isClosed()) { conn.close(); threadLocal.remove(); conn = null; } } catch (SQLException e) { } } }
使用示例:
@Test
public void testThreadLocal(){
//3个线程共享sn,各自产生序列号
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
t1.start();
t2.start();
t3.start();
}
public class SequenceUtils{
private static ThreadLocal<Integer> threadLocal=new ThreadLocal<Integer>(){
protected Integer initialValue(){
return 0;
}
}
public static Integer genNextNum(){
threadLocal.set(threadLocal.get()+1);
return threadLocal.get();
}
}
public class MyThread extends Thread{
public void run(){
//每个线程打印5个序列号
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+":"+SequenceUtils.genNextNum());
}
}
}
}
PS:对于多线程资源共享的问题(多线程中相同变量的访问冲突问题)
- Synchronized用于线程间的数据共享:仅提供一份变量,让不同的线程排队访问(“以时间换空间”)
- ThreadLocal用于线程间的数据隔离:为每一个线程都提供一个独立的变量副本,从而可以同时访问而互不影响(“以空间换时间”)
- 两者处理不同的问题域!!
- 使用示例:使用ThreadLocal以空间换时间解决SimpleDateFormat线程安全问题(为每个线程创建一个SimpleDateFormat变量的副本)
public class DateUtil { private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; @SuppressWarnings("rawtypes") private static ThreadLocal threadLocal = new ThreadLocal() { protected synchronized Object initialValue() { return new SimpleDateFormat(DATE_FORMAT); } }; public static DateFormat getDateFormat() { return (DateFormat) threadLocal.get(); } public static Date parse(String textDate) throws ParseException { return getDateFormat().parse(textDate); } }
网络
ISO/OSI 7层参考模型(解决异质性问题):
- 应用层 Application:(可使用协议:Telnet,FTP,SMTP,Http,。。)
- 表示层 Presention:数据表示
- 会话层 Session:主机间通信
- 传输层 Transport:端到端的连接(可使用协议:TCP,UDP,。。)
- 网络层 IP:寻址和最短路径
- 链路层 Link:介质访问(接入)
- 物理层 Physical:网线,无线信号收发...传输电信号(二进制传输)
TCP/IP 模型:
- 应用层
- 传输层:解释数据(端)
- 网络层:定位IP,确定连接路径
- 网络接口:与硬件驱动对接
eg:
Hello
=>app/Hello
=>tcp/app/Hello
=>ip/tcp/app/Hello
=>帧头/ip/tcp/app/Hello/帧尾
对等通讯:
- 对等实体键虚拟通讯
- 下层向上层提供服务,实际通讯在最底层完成
端口:
- 在互联网上传输的数据都包含有用来识别目的地的IP地址和端口号
- IP地址用来标识网络上的计算机
- 端口号用来指明该计算机上的应用程序
- 端口是一种抽象的软件结构(包括一些数据结构和I/O缓冲区)
- 应用程序通过系统调用与某端口建立连接(binding)
- 传输层接收输出数据都通过该端口
Socket 套接字:
- 抽象概念(地址簇,网络程序设计接口,异步函数)
- 是连接运行在网络上的两个程序间的双向通讯的端点
使用Socket进行网络通信的过程(相当于电话机)
基于TCP协议:
- 服务器程序将一个套接字绑定到一个特定的端口,并通过此套接字等待和监听客户的连接请求(server app:bind socket on port)
- 客户程序根据服务器程序所在的主机名和端口号发出连接请求(client:send connect request depends server host & ip)
- 服务器接受连接请求,并获得一个新的绑定到不同端口地址的套接字(server app:accept connection and return a new socket which binded on a new port)
客户和服务器通过读、写套接字进行通讯(client and server communication via the new socket)
//Server ServerSocket ss = new ServerSocket(5000); //accept connection Socket socket = ss.accept(); //send & receive msg InputStream is = socket.getInputStream(); OutputStream os = socket.getOutputStream(); //... socket.close();
//Client Socket socket = new Socket("127.0.0.1", 5000); //send and receive msg InputStream is = socket.getInputStream(); OutputStream os = socket.getOutputStream(); //... socket.close();
基于UDP协议:
//Server:必须指定端口号 DatagramSocket socket = new DatagramSocket(7000); //receive msg:接受消息(作用类似于TCP方式中accept方法,接受连接) byte[] buffer = new byte[1000]; DatagramPacket receivePacket = new DatagramPacket(buffer, 1000); socket.receive(receivePacket); //send msg: 从接收到的数据包中可以获得客户端ip,port String str="send Message"; DatagramPacket sendPacket = new DatagramPacket(str.getBytes(), str.length(), receivePacket.getAddress(), receivePacket.getPort()); socket.send(sendPacket); socket.close();
//Client: 使用系统随机分配的一个本地计算机的未用端口号,当然也可以指定端口号 DatagramSocket socket = new DatagramSocket(); //send msg String str="hello"; DatagramPacket sendPacket = new DatagramPacket(str.getBytes(), str.length(), InetAddress.getByName("localhost"), 7000); socket.send(packet); //receive msg byte[] buffer = new byte[1000]; DatagramPacket receivePacket = new DatagramPacket(buffer,1000); socket.receive(packet2); socket.close();
TCP & UDP:
TCP | UDP |
---|---|
传输控制协议 | 用户数据报协议 |
面向连接,可靠 | 无连接,不可靠 |
顺序传输,无重复,无差错(有重传机制) | 实时性较高 |
应用举例:下载安装程序 | 应用举例:视频会议 |
JDK Class: ServerSocket,Socket | JDK Class: DategramSocket,DatagramPacket |
TCP综合示例:
//Server端
public static void main(String[] args) throws Exception{
ServerSocket serverSocket = new ServerSocket(4000);
//可以不断接受新的Client端连接
while(true){
Socket socket = serverSocket.accept();
new InputThread(socket).start();
new OutputThread(socket).start();
}
}
//Client端
public static void main(String[] args) throws Exception, IOException{
Socket socket = new Socket("127.0.0.1", 4000);
new InputThread(socket).start();
new OutputThread(socket).start();
}
public class InputThread extends Thread
{
private Socket socket;
public InputThread(Socket socket){
this.socket = socket;
}
@Override
public void run(){
try{
InputStream is = socket.getInputStream();
while(true){
byte[] buffer = new byte[1024];
int length = is.read(buffer);
String str = new String(buffer, 0, length);
System.out.println(str);
}
}catch (IOException e){
e.printStackTrace();
}
}
}
public class OutputThread extends Thread{
private Socket socket;
public OutputThread(Socket socket){
this.socket = socket;
}
@Override
public void run(){
try{
OutputStream os = socket.getOutputStream();
while(true){
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String line = reader.readLine();
os.write(line.getBytes());
}
}catch (IOException e){
e.printStackTrace();
}
}
}
使用URL访问网上资源:
URL url=new URL(location);
//方式一:
URLConnection conn = url.openConnection();
InputStream is = conn.getInputStream();
//方式二:
InputStream is = url.openStream();
Servlet
是一个可以部署到webServer,可以被客户端访问调用的Java类(可以处理客户端请求)
与普通Java类的比较:
Servlet | 普通Java类 |
---|---|
extends HttpServlet | extends Object |
须在WebServer中才能运行 | 不需要WebServer支持也能运行 |
可以处理客户端请求 | 不能处理客户端请求 |
启动不需要main函数 | 启动需要main函数 |
由容器创建(new)实例 | 一般我们自己创建实例 |
由容器调用doGet或doPost方法 | 程序员自己调用 |
Servlet的等级继承结构:
interface Servlet
init(servletConfig)
:调用一次,第一次访问或web程序启动(配置<load-on-startup>
)时service(request,response)
:可调用多次,每次访问时调用destroy()
:调用一次,服务器或应用程序关闭时
abstract class GenericServlet
:与协议无关的Servletabstract class HttpServlet
:实现了Http协议的Servlet- 自定义Servlet,步骤:
- extends HttpServlet
- @Override doGet & doPost
- web.xml中配置Servlet
<servlet> <servlet-name>myServlet</servlet-name> <servlet-class>com.cj.MyServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>myServlet</servlet-name> <url-pattern>/service/*</url-pattern> <url-pattern>/myservice/*</url-pattern> </servlet-mapping>
(Get请求)
WebServer
Servlet需运行在支持Servlet规范的WebServer上,由WebServer:
- 管理Servlet的生命周期(比如创建servlet 对象,调用servlet的init,service,destroy等方法);
- 生成request的实例
三种基本的Servlet引擎:
- 大多数的JavaEE服务器(也称中间件服务器),如:
- BEA WebLogic;
- IBM WebSphere Application;
- JBoss
- 具有内置Servlet容器的Web服务器,如:
- Tomcat
- 使用Servlet引擎插件的产品(需要安装Web插件来集成Java运行时环境)
- Apache Web服务器;
- Microsoft IIS
Tomcat:
- 是JSP/Servlet规范的一个实现;
- 是WebServer的一种,它又称为Servlet引擎,web包容器。
- 运行只需要JRE(本身是使用java 开发的)
JSP
JSP本质:Servlet (每个JSP都会转译为Servlet)
(JSP执行过程)
第二次请求:不存在转译与编译的过程直接访问上次生成的class类,速度变快
JSP页面构成
Jsp页面=java+DHTML(html+css+js)
//jsp转换为的Servlet:
public final class 名_jsp extends ...{
... // 使用<%! xxx %> 声明的方法或变量
public void _jspService(request,response){
... // 使用<% xxx %> 编写的Java Scriptlet
out.print(...) // 使用<%=xxx> 编写的Expression
...
}
}
JSP内置对象
内置对象:又称隐式对象,由Web容器创建的一组类的实例(可直接在JSP页面中使用的对象)
4类9个:
输入输出
out
:向客户端输出数据(eg:out.print("<br/>")
)request
:封装客户端请求- 获取页面提交数据:
String value=request.getParameter(param); //单值 String[] values=request.getParameterValues(param); //多值
- 请求转发 (客户端为建立新的连接,地址栏URL不变,属于一次请求范围)
request.getRequestDispather(url).forward(request, response);
- 获取页面提交数据:
response
:响应客户端请求(向客户端输出信息)- 向客户端打印数据
PrintWriter out = response.getWriter(); out.println("<br/>");
- 重定向(客户端URL地址改变,属于二次请求范围)
reponse.sendRedirect(url);
- 向客户端打印数据
- 请求转发与重定向对比:
请求转发 重定向 是一次请求,只有一个request对象(所以数据不会丢失) 多次请求,生成多个request对象(所以数据会丢失) 处理完,地址栏url路径不变 处理完,地址栏为最后一个url路径 只能在一个web应用程序内部转发 可以到其他应用程序 前后调用的方法类别相同 会变为get请求,与之前的没关系
作用域通讯级别(4个作用域),由低到高:
- 页面级
pageContext
:刷新时新页面 - 请求级
request
:除转发外新页面 - 会话级
session
:浏览器关闭新会话(HttpSession,跟踪同一浏览器发出的连续请求,一种会话跟踪) - 应用级
application
- 操作:
void setAttribute(String key,Object value); Object getAttribute(String key); void removeAttribute(String key); void invalidate(); //销毁
//使用示例: session.setAttribute("Login_User",loginUser); //以键值方式,在session中存放用户登录信息 User user=(User)session.getAttribute("Login_User"); //获取session中存放的对象 session.removeAttribute("Login_User"); //移除已登录用户 session.invalidate(); //销毁会话
- 页面级
- Servlet对象
page
config
- 错误对象
exception
<!-- 指定页面为错误页面 --> <%@ page ... isErrorPage="true" %>
PS: request中parameter
& attribute
parameter | attribute |
---|---|
客户端传过来的(client请求参数)数据 | Server端放入的数据 |
只读 | 可读写 |
String getParamenter(String) String[] getParameterValues(String) |
Object getAttribute(String) setAttribute(String,Object) removeAttribute(String) |
PS: ServletContext
- 只有一份,单态类
- 启动时创建,停止时消亡
- 获取:
- Jsp中,eg:
request.getServletContext()
- Servlet中,eg:
this.getServletContext()
- Jsp中,eg:
附录:Request其他常用方法
方法 | 说明 |
---|---|
getParameterNames() | 得到客户端所有请求参数名称的集合 |
getContextPath() | 得到web应用程序名称,或者叫虚拟路径 |
getServletPath() | 1,如果请求的是servlet,那么返回的该servlet在web.xml中配置的url-pattern;2,如果是jsp,返回jsp文件名。 |
getRequestURI() | = request.getContextPath() + request.getServletPath();请求的相对路径 |
getRequestURL() | 请求的全部路径,绝对路径 |
getMethod() | 请求类别,默认是get请求,只有使用form表单提交才可以使用post请求 |
getRemotePort() | 客户端请求端口 |
getRemoteAddr() | 得到客户端的IP地址 |
getServerPort() | 得到服务器端口号 |
getServerName() | 得到服务器名称 |
getRealPath("/") | 得到当前Web应用程序的绝对路径 |
getHeader(paramName) | 得到客户端浏览器的信息 |
getHeaderNames() | 得到所有请求头名称的Enumeration |
getRequestDispatcher(url) | 得到请求转发对象 |
附录: Session常用方法
方法名 | 返回值 | 定义 |
---|---|---|
getId() | String | 获得当前Session的SessionID |
isNew() | boolean | 判断Session是不是新创建的 |
getCreationTime() | long | 获得当前Session创建的时间 |
getLastAccessedTime() | long | 获得客户最后一次请求的时间 |
setMaxInactiveInterval(intinterval) | void | 设置Session最大请求间隔时间 |
getMaxInactiveInterval() | int | 获得Session最大请求间隔时间 |
setAttribute(String key,Objectvalue) | void | 向Session空间中存储对象 |
getAttribute(String key) | Object | 从Session中获取对应的对象 |
removeAttribute(String key) | void | 从Session中删除对应的对象 |
getAttributeNames | Enumeration | 返回session存储的key的集合 |
invalidate() | void | 强制Session过期 |
附录:HTTP响应状态码
状态码 | 说明 |
---|---|
1xx | 临时响应 |
2xx | 成功 |
3xx | 已重定向 |
4xx | 请求错误 eg: 401 (未授权) 404 (未找到) |
5xx | 服务器错误 |
会话跟踪
解决同一时刻不同客户端会话私有数据保存问题
有两种实现:
Session
- 服务器分配的保存客户端私有信息的一块内存空间
- sessionId是Session的唯一标识
- 服务器通过sessionId将客户端与Session数据对应起来
- 跟踪实现机制:
- Client第一次请求
- Server端为此Client产生session对象,生成sessionId
- 应答时传递sessionId
- Client保存此sessionId
- 同一Client再次发送请求:
- 将上次得到的sessionId一同发出
- Server端检查sessionId,取得对应session对象
- sessionId保存方式(客户端)
- Cookie (默认,可显示设置)
- url重写 (客户端禁用cookie情况下使用,给每个超链接后增加请求参数,值为sessionId)
- 隐藏表单域(不推荐)
- Client第一次请求
Cookie
两种实现方式比较:
比较内容 | Session | Cookie |
---|---|---|
保存方式 | 数据内容保存在服务器端 | 数据内容保存在客户端 |
安全性 | 比较安全 | 相对不安全 |
生命周期 | 使用内存存放数据,当用户长时间未请求服务器或服务器重启,内容可能丢失 | 保存在客户端的内存或文件中,可以指定Cookie的生存周期 |
资源占用 | 服务器的内存 | 每次请求时发送Cookie内容,占用带宽 |
存放内容 | 可以存放各种数据类型的数据 | 只能存放字符串类型的数据 |
MVC
- MVC演化史:
- MVC框架:Structs1,Struct2,SpringMVC
- 事件驱动型框架:JSF(Java Server Face)