九·AWT(Abstract Window Toolkit) 事件模型
十一·JFC(Java Foundation Classes)
前言
JAVA特点
1) 简单(Java语法是C++语法的一个“纯净”版本);
2) 可移植性 (一次编译到处运行)
3) 面向对象
4) 分布式(Java把打开套接字连接等繁琐的网络任务变得非常容易)
5) 健壮性(Java编译器会检查出很多其他语言在运行时刻才显示出来的错误;Java采用的指针模型可以消除重写内存和数据崩溃的可能)
6) 多线程(多线程编程的简单性是Java成为流行的服务器端开发语言的主要原因之一)
7)安全(用Java可以构建防病毒和防篡改的系统)
9) 动态(Java可随意增加新的方法以及实例变量,而客户端却不需做任何的更改)
10)体系结构中立(字节码与计算机体系结构无关,只要存在运行时系统,可在多种处理器上执行)
运行原理
先编译 *.java文件――――>*.class文件
运行 *.class ――加载――> JVM(JAVA虚拟机)
JAVA目录
JRE―――――――运行环境
SRC――――――-类库
BIN―――――――应用程序
一·基础知识
配置环境
LINUX系统(修改环境配置文件)
1 打开shell
2 vi .bash_profile
3 JAVA_HOME=JAVA目录路径
4 PATH=$JAVA_HOME/bin:其他路径
5 CLASSPATH=.
6 export JAVA_HOME CLASSPATH
Windows系统
我的电脑属性―――>环境变量
设置环境变量:
JAVA_HOME=路径
PATH = %PATH%;c:j2sdk1.4.2_05in;
CLASSPATH = .;
Java中基本概念
1) 源文件
在最顶层只包括一个public类型的类/接口,文件名与类/接口名同并以.java作为文件后缀。
2) 包(package ,在源文件中this identify只能放在第一行,且最多只能是一行)
一个将类和接口组织在一块的实体,在文件系统中以目录/文件夹型式呈现。
二·定义,关键字和类型
注释的三种形式
// 单行注释
/* 一或多行注释 */
/** 文档注释 */
Java代码中的“;”、“{}”、“ ”
Java语句以分号分隔;
Java代码块包含在大括号内;
忽略空格。
标识符
1) 用以命名类、方法和变量、以及包;
遵守JAVA的命名规范
类以每个单词都以大写字母开头。
方法和变量第一个字母不大写,其他依旧
2) 以字符、“_”或“$”开头;
3) 无长度限制。
数据类型
1) 整型
byte 1B 8位 -128到127
short 2B 16位 -2^15到2^15-1
int 4B 32位 -2^31到2^31-1
long 8B 64位 -2^63到2^63-1
2) 浮点类型
float 4B 32位
double 8B 64位
3) 字符类型
char 2B 16位
4) 布尔型
boolean false/true
注:1) char是无符号的16位整数,字面值必须用单引号括起来; ‘a’
2) String 是类,非原始数据类型;
3) 长整型数字有一个后缀为“L”或“l”,八进制前缀为“0”,十六进制前缀为“0x”;
4) 黙认浮点类型为double;
5) float数据类型有一个后缀为“f”或“F”,Double数据类型后可跟后缀“D”或“d“
命名规则
1) 类/接口名首字母大写;
2) 方法、变量名第一个字母小写,其余首字母大写;
3) 常量名称全部大写;
4) 包名全部小写。
三·表达式和控制流
变量和作用域
1) 局部变量
定义在方法内部,其作用域为所在代码块,也称为临时变量、栈变量。
存在于栈中。
2) 实例变量
定义在类内部方法之外,其作用域为整个类。如未定义初值,系统会自动为其赋黙认值。存在于堆中
默认数值
类型 黙认值
byte 0
short 0
int 0
long 0L
float 0.0f
double 0.0d
char 'u0000' 空格
boolean false
*All reference types null
操作符
System.out.println(3/2) 按整型计算 得1
1) >> 前面是零补零,前面是一补一;
2) >>> 无符号右移;
3) && 短路与,前面为假,表达式为假,后面不须计算;
4) || 短路或,前面为真,表达式为真,后面不计算;
例:
if(a<3&(b=a)==0) b赋值
if(a<3&&(b=a)==0) b不赋值
数字类型之间的转换
1) byte ——→ short ——→ int ——→ long
2) char ——→ int - - - → float
3) float ——→ double
4) long - - - → float
5) long - - - → double
6) int ——→ double
注:1)实箭头表示无信息损失的转换,虚箭头表示转换可能引起损失;
2)int和float同为32位,但float要有几位表示幂的位数,在精度位数上要比int要小,所以有可能会有损失。long转为double同理;
3)char和short同为16位,但char属无符号数,其范围为0~2^16, short的范围为-2^15~2^15-1 , 所以char和short不能相互转换;
4)byte、short、char属child型,在计算时会自动转为int型,然后转为更大范围类型(long、short、double)。
强制类型转换
1) 语法:圆括号中给出要转换的目标类型,随后是待转换的变量名。例:doublc x=9.997;int nx = (int)x;
2) 如果试图强制转换的类型超出了目标类型的范围,结果是一个被截取的不同的数值;
3) 不能在布尔值和任何数字类型间强制类型转换,如确要转换,可使用条件表达式,例:b?1:0。
转换的二种类型
1) 赋值
double a = 1.0f
int = ‘j’;
2) 方法调用
double converValue(int value){
return value;
}
3) 数值计算转换 -9.232e20+1;
控制流
if()
if()….else
if()…..else if()….else
switch(){
case variable:……..
case variable:……..
default:
…………
}
注解:switch()内数据类型为child类型 byte short char 自动转换为int
case块中不加break时顺序执行下面的语句。
循环语句
for(int i=0;i<n;i++){}
while(){}
do{} while();-----------à加分号
例子:
loop:for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(3==j){
break loop;//---------------àloop为标签 只能用在循环语句中,循环//嵌套中用于跳到外层循环
}
}
}
辨析:
int x,a=6,b=7;
x=a++ + b++; //----------a=7,b=8,x=13
int x=6;x=~x;//---------------- 6的二进制0110 取反得11001 再转成补码(取反加一)10111 = -7
四·数组
声明数组
1) 一组相同类型(可以是类)数据的集合;
2) 一个数组是一个对象;
3) 声明一个数组没有创建一个对象;
4) 数组能以下列形式声明:
int[] i 或 int i[]
Car[] c 或 Car c[]
*C++中只能 Car c[]
*JAVA中推荐用 Car[] c;
创建数组
1) 创建基本数据类型数组 int[] i = new int[2];
2) 创建引用数据类型数组 Car[] c = new Car[100];
3) 数组创建后有初始值。
数字类型为0 布尔类型为false 引用类型为null
初始化数组
1) 初始化、创建、和声明分开
int[] i;
i = new int[2];
i[0] = 0;
i[1] = 1;
2) 初始化、创建、和声明在同一时间
int[] i = {0,1};
Car[] c = {new Car(),new Car()};
多维数组
1) 有效
int[][] i1 = new int[2][3];
int[][] i2 = new int[2][];
i2[0] = new int[2],i2[1] = new int[3];
*C++中 int[][] =new int[][3];有效
2) 无效
int[][] i1 = new int[][3];
3) 数组长度 ------------à数组的属性length
int[] i = new int[5];
int len = i.length;//len = 5;
Student[][] st = new Student[4][6];
len = st.length;//len = 4;
len = st[0].length;//len = 6;
请问以下哪段代码哪个可正确执行?(a,c)
1. a char[] i = {'a','b'}; i = new char[]{'b','c'};
b char[] i = {'a','b'}; i = {'b','c'};
c char[] i = new char[2]; i = new char[]{'b','c'};
d char[] i = new char[2]; i = {'b','c'};
数组拷贝
System.arrayCopy(Object src, int srcPos, Object dest, int destPos, int length);
拷贝一个数组到另一个数组。
五·对象和类
面向对象与面向过程
为什么要使用面向对象:
首先,面向对象符合人类看待事物的一般规律。
对象的方法的实现细节是屏蔽的,只有对象方法的实现者了解细节。
方法的定义非常重要。方法有参数,也可能有返回值。
注意区分:对象(本身)、对象的实现者、对象的调用者。
分析对象主要从方法开始。
我们通过类来看待对象,类是对象的抽象。
其次,采用面向对象方法可以使系统各部分各司其职、各尽所能。
对象之间的耦合性一定要低(比如不同硬盘和不同主板之间的关系)。这样才能使每个对象本身做成最好的。
对于对象的要求:高内聚、低耦合,这样容易拼装成为一个系统。
实现高内聚就是要最大限度低提高复用性(复用性好是因为高内聚)。
可复用性是OOP的基础。
比较面向过程的思想和面向对象的思想:
面向过程的思想:由过程、步骤、函数组成,以过程为核心;
面向对象的思想:以对象为中心,先开发类,得到对象,通过对象之间相互通信实现功能。
面向过程是先有算法,后有数据结构。
面向对象是先有数据结构,然后再有算法。
在用面向对象思想开发的过程中,可以复用对象就进行复用,如无法进行复用则开发新的对象。
开发过程是用对个简单的对象的多个简单的方法,来实现复杂的功能 。
从语法上来看,一个类是一个新的数据类型。
在面向对象编程中,除了简单数据类型,就是对象类型。
定义类的格式:
class Student{
代码
}
注意类名中单词的首字母大写。
对象的概念
什么是对象:EVERYTHING IS OBJECT(万物皆对象)
所有的事物都有两个方面:
1.有什么(属性):用来描述对象。
2.能够做什么(方法):告诉外界对象有那些功能。后者以前者为基础。
*一个对象的属性也可以是一个的对象。这是一种对象的关联(associate)
public class Student{
private String name;---------对象
private int age;---------------基本类型
private gender;
public void study(){}---------方法
}
成员变量和局部变量
1.实例变量:定义在类中但在任何方法之外。(New出来的均有初值)
实例变量中对象默认值为null。
实例变量的作用域在本类中完全有效,当被其他的类调用的时候也可能有效。
2.局部变量:定义在方法之中的变量。
局部变量要先赋值,再进行运算,而实例变量均已经赋初值。这是局部变量和实例变量的一大区别。
局部变量不允许范围内定义两个同名变量。实例变量和局部变量允许命名冲突,但在局部变量的作用域内局部变量优先,访问实例变量是使用this.variableName。
成员方法
方法定义
1) 格式 <modifiers><return_type><name>([argument_list>])[throws <exception>]{<block>}
例如:public String getName(){ return name; }
2) 当没有返回值时,返回类型必须被定义为void。
3) 构造方法没有返回类型。
4) 返回类型必须与方法相邻,其他修饰符号可以调换位置。
参数传递
Java语言总是使用传值调用。这意味着方法得到的只是所有参数值的拷贝。因此,方法不能修改传递给它的任何参数变量的内容。对于对象类型的参数传递的也是该对象的引用值,方法中并不能改变对象变量,但能通过该变量调用对象的方法或修改对象的成员。
This关键字
1) this是个隐式参数,代表被构造的对象;
publie class Person{
private String name;
public void setName(String name){
this.name=name;-------------àthis.name为成员变量
}
}
2) 如果构造器的第一个语句具有形式this(...),那么这个构造器将调用同一类中的其他构造器。
3)在构造器中this()必须放在方法的第一行。
*Super关键字也是个隐形参数,代表被构造对象的父类。
同样也必须在构造方法的第一行
访问控制符
权限高
public 全部可见
protected 本类可见,同包可见,子类可见
default 本类可见,同包可见
private 本类可见
权限低
构造方法
构造方法是在生成对象的过程中调用的方法,但构造方法并不能创建对象。
其特点为:
1.构造方法没有返回值。
2.构造方法的方法名与类名相同。
格式为:public ClassName(){}
构造方法也可以是其他的限制符――private protected default private 一般用在singleton模式中。
在一个对象的生成周期中构造方法只用一次,一旦这个对象生成,那么这个构造方法失效。
*接口不能创建实例,因为没有构造方法
可以构造多个构造方法,但多个构造方法的参数表一定不同,参数顺序不同即属于不同的构造方法:-----------------------à构造方法的重载
public student(string name,int a){
}
public student(int a,string name){
}
为两个不同的构造方法。
如果我们未给系统提供一个构造方法,那么系统会自动提供一个为空的构造方法。
如果我们提供了有参的构造方法,那么系统不会再提供无参的构造方法了。这样当被子类继承时,如果子类构造方法不人为调用父类的有参构造方法就会出现异常。
数据的隐藏―――封装
public class Person{
private String name;----------------------à数据的隐藏
private int age;
public String getName(){-------------------à方法尽量公开
return name;
}
public int getAge(){
return age;
}
}
方法的重载
Overloading在一个类中可以定义多个同名方法,各个方法的参数表一定不同。但修饰词可能相同,返回值也可能相同。
在程序的编译过程中根据变量类型来找相应的方法。Overloading被认为是编译时的多态。Overloading 只是为方便程序员编程的解决方法,编译之后的方法名实际加上了各参数类型成为唯一的名字。
普通方法
public void aa(int a,double b) throws IOException{}
private int aa(double a,int b){}
protected double aa(String a,String b){}
构造方法也可以实现overloading。
例:
public void teach(){};
public void teach(int a){};
public void teach(String a){}为三种不同的方法。
Overloading方法对于不匹配的参数是从低向高转换的。
Byte—short—float—int—long—double。
六·高级语言特性
封装 (encapsulation)
a. 把数据和行为结合在一个包中,并对对象的使用者隐藏数据的实现过程。对象中的数据称为对象的实例字段(instance field), 操作数据的函数和过程称为对象的方法(method)。一个特定对象就是类在实例字段上有着特定值的某个实例。
b. 实现封装的关键在于绝不让方法直接访问其他类的实例字段。
继承 (inherit)
JAVA继承特点
1. 在现有类的基础上构建新的类。当继承一个现有类时,就重用(继承)了那个类的方法和字段,同时,还可以向新类中增添新的方法和字段。
2. 在通过扩展超类来定义子类时,只需要说明子类和超类的区别就行。所以在设计类时,把最通用的方法放在超类中,把更专门的方法放在子类中。
3. JAVA中不允许多继承,一个类有且只有一个父类。
所以JAVA的数据结构为树型结构,而非网状。(JAVA通过接口实现多继承)
父类(SuperClass)和 子类(SubClass)的关系
父类的非私有化属性(不同包的子类无法访问default修饰符)和方法可以默认继承到子类。
Class Son extends Father{
}
而如果父类中的私有方法被子类调用的话,则编译报错。
父类的构造方法子类不可以继承,更不存在覆盖的问题。
所以子类构造方法默认调用父类的无参构造方法。(所以养成写无参构造的习惯)
如果子类访问父类的有参构造方法,必须在子类构造方法第一行使用super(参数)
当构造一个对象的时候,系统先构造父类对象,再构造子类对象。
系统构造一个对象的顺序
1先为最里层类成员属性赋初值;
2再构造该类对象;
3返回外层,重复1(上一层类)、2步骤直到完成最外层类的构造。
多态(polymorphism)
多态:一个对象变量可以指向多种实际类型的现象。
方法的覆盖(overridding)
当子类从父类继承一个无参方法,而又定义了一个同样的无参方法,则子类新写的方法覆盖父类的方法,称为覆盖。(注意返回值类型也必须相同,否则编译出错。)
如果方法参数表不同,则成重载。
特点:
1.对于方法的访问限制修饰词,子类方法要比父类的访问权限更高。
父类为public,那么子类为private则出现错误。
2.子类抛出的异常应该是父类抛出的异常或其子类。
多态的分类
多态分两种:
1编译时多态:编译时动态重载;
2运行时多态:指一个对象可以具有多个类型。猫,小鸟,狗 都是动物,都可以安上动物的标签。
Interface Animal{}
Class Car implements Animal{}
Class Bird implements Animal{}
Class Dog implements Animal{}
方法中
Animal a = new Car();
Animal b = new Bird();
Animal c = new Dog();
运行时多态的三原则
1.对象不变;(改变的是主观认识)
2.对于对象的调用只能限于编译时类型的方法,如调用运行时类型方法报错。
在上面的例子中:Animal a=new Dog();对象a的编译时类型为Animal,运行时类型为dog。
注意:编译时类型一定要为运行时类型的父类或者同类型。
对于语句:Dog d=(Dog)a。将d强制声明为a类型,此时d为Dog(),此时d就可以调用运行时类型。注意:a和d指向同一对象。
3.动态类型判定实际调用的方法。即它调用覆盖后的方法。
关系运算符:instanceof
instanceof Animal;(这个式子的结果是一个布尔表达式)
上面语句是判定a是否可以贴Animal标签。如果可以贴则返回true,否则返回false。
在上面的题目中: a instanceof Animal返回 True,
a instanceof Dog也返回 True,
用于判定前面的对象是否是后边的类或者子类。
Animal a = new Car();
If(a instanceof Dog){
Dog b =(Dog)a;
}
else if(a instanceof Car){
Car c =(Car)a
}
不会错。
静态变量,方法和类
静态变量
Static int data语句说明data为类变量,为一个类的共享变量,属于整个类。
例:
Class M{
static int data;
}
M m1=new M(); M m2=new M();
m1.data=0;
m1.data++的结果为1,此时m2.data的结果也为1。
Static定义的是一块为整个类共有的一块存储区域。
其变量可以通过类名去访问:类名.变量名。与通过对象引用访问变量是等价的。
2) 静态方法
Public static void printData(){}
表明此类方法为类方法(静态方法)
静态方法不需要有对象,可以使用类名调用。
静态方法中不允许访问类的非静态成员,包括成员的变量和方法,因为此时是通过类调用的,没有对象的概念。方法中this.data和super.data是不可用的。
原因:从根本来说,是静态变量不管类是否实例化都会存在,而实例变量只有类实例化了才存在。直接调用静态方法时并不确定实例变量是否存在。
一般情况下,主方法是静态方法,所以JVM可以直接调用它,主方法为静态方法是因为它是整个软件系统的入口,而进入入口时系统中没有任何对象,只能使用类调用。
猜想:JVM在代码中有这样的语句:
ClassName.main(arg); ClassName 通过命令行的”java 类名”取得,所以类名不用加.class 扩展名
*覆盖不适用于静态方法。
静态方法不可被覆盖。
如果子类中有和父类重名的静态方法,虽然编译通过,但它并不能实现多态,所以不能称作覆盖。
public class Test {
public static void main(String[] arg) {
Super s = new Sub();
s.show();
}
}
class Super
{
static public void show(){System.out.println("in Super");}
}
class Sub extends Super
{
static public void show(){System.out.println("in Sub");}
}
执行结果是: in Super
3) 静态内部类----à只能是成员内部类
class Out{
public static class Inner{}
}
4) 初始化块
1. 只被执行一次;
2. 初始化块在类被加载后首先被运行,不管类是否实例化
3.一般用来初始化静态变量
Singleton模式
Static通常用于Singleton模式开发:
Singleton是一种设计模式,高于语法,可以保证一个类在整个系统中仅有一个对象。
实现1
public class ConnectionFactory{
private static Connection conn;
private Connection(){
if(conn==null)
conn = new Connction();
}
public Connection getInstance(){
return conn;
}
}
实现2
public class ConnectionFactory{
private static Connection conn;
static{
conn = new Connection();
}
public static Connection getInstance(){
return conn;
}
}
final关键字
final变量不能被改变;
当利用final修饰一个属性(变量)的时候,此时的属性成为常量。
注意JAVA命名规范中常量全部字母大写:
Final int AGE=10;
常量的地址不可改变,但在地址中保存的值(即对象的属性)是可以改变的。
在JAVA中利用public static final的组合方式对常量进行标识(固定格式)。
Final变量是在整个类被创建时候被赋值,之后就不能改变了。
对于final变量,如果在声明的时候和构造的时候均不进行赋值,编译出错。
对于利用构造方法对final变量进行赋值的时候,此时在构造之前系统设置的默认值被覆盖。
常量(这里的常量指的是实例常量:即成员变量)赋值:
①在初始化的时候通过显式声明赋值。Final int x=3;
②在构造的时候赋值。
final方法不能被改写;
利用final定义方法:这样的方法为一个不可覆盖的方法。
Public final void print(){};
为了保证方法的一致性(即不被改变),可将方法用final定义。
如果在父类中有final定义的方法,那么在子类中继承同一个方法。
如果一个方法前有修饰词private或static,则系统会自动在前面加上final。即private和static方法默认均为final方法。
注:final并不涉及继承,继承取决于类的修饰符是否为private、default、protected还是public。也就是说,是否继承取决于这个类对于子类是否可见。
Final和abstract永远不会同时出现。
final类不能被继承;
final修饰类的时候,此类不可被继承,即final类没有子类。这样可以用final保证用户调用时动作的一致性,可以防止子类覆盖情况的发生。
String 类数据final类 ,目的是提供效率保证安全。
抽象类
1) Abstract(抽象)可以修饰类、方法
如果将一个类声明为abstract,此类不能生成对象,只能被继承使用。
Abstract类的设计是将子类的共性最大限度的抽取出来,以提高程序的统一性。
2) 一个类中包含有抽象方法必须声明为抽象类;
如果一个类中有一个抽象方法,那么这个类一定为一个抽象类。
反之,如果一个类为抽象类,那么其中可能有非抽象的方法。
3) 抽象类不能实例化,但仍可以声明;
Abstract类可以作为编译时类型,但不能作为运行时类型。
4) 子类继承抽象类必须实现其中抽象方法
当abstract用于修饰方法时,此时该方法为抽象方法,此时方法不需要实现,实现留给子类覆盖,子类覆盖该方法之后方法才能够生效。
注意比较:
private void print(){};此语句表示方法的空实现。
Abstract void print(); 此语句表示方法的抽象,无实现
接口
1) 接口是抽象类的另外一种形式(没有实例变量的抽象类);
2) 在一个接口中所有方法都是抽象方法;
3) 接口中所有变量都必须被定义为final static;
4) 接口可以继承多个接口。
注:1) 接口中的方法自动被置为public, 因经,在接口中声明方法并不需要提供public关键字。但在实现接口时,必须把方法声明为public。
Object 类
JAVA中有一个特殊的类: Object。它是JAVA体系中所有类的父类(直接父类或者间接父类)。
此类中的方法可以使所的类均继承。
以下介绍的三种属于Object的方法:
(1)finalize方法:当一个对象被垃圾回收的时候调用的方法。
(2)toString():是利用字符串来表示对象。
当我们直接打印定义的对象的时候,隐含的是打印toString()的返回值。
可以通过子类作为一个toString()来覆盖父类的toString()。
以取得我们想得到的表现形式,即当我们想利用一个自定义的方式描述对象的时候,我们应该覆盖toString()。
(3)equal
首先试比较下例:
String A=new String(“hello”);
String B=new String(“hello”);
A==B(此时程序返回为FALSE)
因为此时AB中存的是不同的对象引用。
附加知识:
字符串类为JAVA中的特殊类,String中为final类,一个字符串的值不可重复。因此在JAVA VM(虚拟机)中有一个字符串池,专门用来存储字符串。如果遇到String a=”hello”时(注意没有NEW,不是创建新串),系统在字符串池中寻找是否有”hello”,此时字符串池中没有”hello”,那么系统将此字符串存到字符串池中,然后将”hello”在字符串池中的地址返回a。如果系统再遇到String b=”hello”,此时系统可以在字符串池中找到 “hello”。则会把地址返回b,此时a与b为相同。
String a=”hello”;
System.out.println(a==”hello”);
系统的返回值为true。
故如果要比较两个字符串是否相同(而不是他们的地址是否相同)。可以对a调用equal:
System.out.println(a.equal(b));
equal用来比较两个对象中字符串的顺序。
a.equal(b)是a与b的值的比较。
注意下面程序:
student a=new student(“LUCY”,20);
student b=new student(“LUCY”,20);
System.out.println(a==b);
System.out.println(a.equal(b));
此时返回的结果均为false。
因为Student继承的是Object的equals()方法,此时toString()等于==
为了实现对象的比较需要覆盖equals(加上这个定义,返回ture或false)
以下为实现标准equals的流程:
public boolean equals(Object o){
if (this==o) return trun; //此时两者相同
if (o==null) return false;
if (! o instanceof strudent) return false; //不同类
studeng s=(student)o; //强制转换
if (s.name.equals(this.name)&&s.age==this.age) return true;
else return false;
}
封装类
JAVA为每一个简单数据类型提供了一个封装类,使每个简单数据类型可以被Object来装载。
除了int和char,其余类型首字母大写即成封装类。
注:
“==”在任何时候都是比较地址,这种比较永远不会被覆盖。
程序员自己编写的类和JDK类是一种合作关系。(因为多态的存在,可能存在我们调用JDK类的情况,也可能存在JDK自动调用我们的类的情况。)
注意:类型转换中doubleintergerstring之间的转换最多。
封装类·字符串·基本类型
Interger--------------------(Double(a.toString))------------>Double
String -----------------(Integer.valueOf() )------------------>Integer
Integer-----------------(String.valueOf() )-------------------> String
Int----------------------(100+””)------------------------------->String
String------------------(Integer.parseInt() )----------------->int
Integer-----------------(Integer.intValue() )----------------->int
内部类
(注:所有使用内部类的地方都可以不用内部类,但使用内部类可以使程序更加的简洁,便于命名规范和划分层次结构)。
内部类是指在一个外部类的内部再定义一个类。
*内部类可为静态,可用PROTECTED和PRIVATE修饰。(而外部类不可以:顶级类只能使用PUBLIC和DEFAULT)。
*JAVA文件中没有publie class 可以类名和文件不同名。
内部类的分类
成员内部类、
局部内部类、
静态内部类、
匿名内部类(图形是要用到,必须掌握)。
成员内部类
作为外部类的一个成员存在,与外部类的属性、方法并列。
内部类和外部类的实例变量可以共存。
在内部类中访问实例变量:this.属性
在内部类访问外部类的实例变量:外部类名.this.属性。
在外部类的外部访问内部类,使用out.inner.
成员内部类的特点:
1.内部类作为外部类的成员,可以访问外部类的私有成员或属性。(即使将外部类声明为PRIVATE,但是对于处于其内部的内部类还是可见的。)
2.用内部类定义在外部类中不可访问的属性。这样就在外部类中实现了比外部类的private还要小的访问权限。
注意:内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两类。
对于一个名为outer的外部类和其内部定义的名为inner的内部类。编译完成后出现outer.class和outer$inner.class两类。
3.成员内部类不能有静态属性
建立内部类对象时应注意:
在外部类的内部可以直接使用inner s=new inner();(因为外部类知道inner是哪个类,所以可以生成对象。)
而在外部类的外部,要生成(new)一个内部类对象,需要首先建立一个外部类对象(外部类可用),然后在生成一个内部类对象。
Outer.Inner in=Outer.new.Inner()。
局部内部类
在方法中定义的内部类称为局部内部类。
与局部变量类似,在局部内部类前不加修饰符public和private,其范围为定义它的代码块。
注意:
局部内部类不仅可以访问外部类实例变量,还可以访问外部类的局部常量
在类外不可直接访问局部内部类(保证局部内部类对外是不可见的)。
在方法中才能调用其局部内部类。
静态内部类
(注意:前三种内部类与变量类似,所以可以对照参考变量)
静态内部类定义在类中,任何方法外,用static定义。
静态内部类只能访问外部类的静态成员。
生成(new)一个静态内部类不需要外部类成员:这是静态内部类和成员内部类的区别。静态内部类的对象可以直接生成:
Outer.Inner in=new Outer.Inner();
而不需要通过生成外部类对象来生成。这样实际上使静态内部类成为了一个顶级类。静态内部类不可用private来进行定义。
注意:当类与接口(或者是接口与接口)发生方法命名冲突的时候,此时必须使用内部类来实现。
用接口不能完全地实现多继承,用接口配合内部类才能实现真正的多继承。
例子:
对于两个类,拥有相同的方法:
class People
{
run();
}
interface Machine{
run();
}
此时有一个robot类:
class Robot extends People implement Machine.
此时run()不可直接实现。
interface Machine
{
void run();
}
class Person
{
void run(){System.out.println("run");}
}
class Robot extends Person
{
private class MachineHeart implements Machine
{
public void run(){System.out.println("heart run");}
}
public void run(){System.out.println("Robot run");}
Machine getMachine(){return new MachineHeart();}
}
class Test
{
public static void main(String[] args)
{
Robot robot=new Robot();
Machine m=robot.getMachine();
m.run();
robot.run();
}
}
匿名内部类
匿名内部类是一种特殊的局部内部类,它是通过匿名类实现接口。
IA被定义为接口。
IA I=new IA(){};
注:一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类,没有类名,根据多态,我们使用其父类名。
因其为局部内部类,那么局部内部类的所有限制都对其生效。
匿名内部类是唯一一种无构造方法类。
匿名内部类在编译的时候由系统自动起名Out$1.class。
如果一个对象编译时的类型是接口,那么其运行的类型为实现这个接口的类。
因匿名内部类无构造方法,所以其使用范围非常的有限。
集合
集合是指一个对象可以容纳了多个对象(不是引用),这个集合对象主要用来管理维护一系列相似的对象。
集合接口类层次
位于package java.util.*;
Collection
↑
|ˉˉˉˉˉˉ|
Set List Map
↑ ↑
| |
SortedSet SortedMap
1) Set: 集合类中不允许有重复对象;
2) SortedSet: 和Set接口同,但元素按升序排列;
3) List: 元素加载和移出时按照顺序,可以保存重复对象。
4) Map: (key-value对)存储了唯一关键字辨识和对应的值。
5) SortedMap: 和Map类同,但对象按他们关键字的升序排列。
集合类层次 (注:JAVA1.5对JAVA1.4的最大改进就是增加了对范型的支持)
Collection
↑
|ˉˉˉˉˉˉ|
HashSet LinkedList Hashtable
(Set) Vector, ArrayList Hashmap
(List) (Map)
↑ ↑
| |
TreeSet TreeMap
(SortedSet) (SortedMap)
Collection接口的方法:
add(Object o)
addAll(Collection c)
contains(Object o)
containsAll(Collection c)
remove(Object o)
removeAll(Collection c)
clear()
equals(Object o)
isEmpty()
iterator()
size()
toArray()
toArray(Object[] o)
五个最常用的集合类之间的区别和联系
1.ArrayList: 元素单个,效率高,多用于查询
2.Vector: 元素单个,线程安全,多用于查询
3.LinkedList:元素单个,多用于插入和删除
4.HashMap: 元素成对,元素可为空
5.HashTable: 元素成对,线程安全,元素不可为空
ArrayList
底层是Object数组,所以ArrayList具有数组的查询速度快的优点以及增删速度慢的缺点。
而在LinkedList的底层是一种双向循环链表。在此链表上每一个数据节点都由三部分组成:前指针(指向前面的节点的位置),数据,后指针(指向后面的节点的位置)。最后一个节点的后指针指向第一个节点的前指针,形成一个循环。
双向循环链表的查询效率低但是增删效率高。
ArrayList和LinkedList在用法上没有区别,但是在功能上还是有区别的。
LinkedList
经常用在增删操作较多而查询操作很少的情况下:队列和堆栈。
队列:先进先出的数据结构。
栈:后进先出的数据结构。
注意:使用栈的时候一定不能提供方法让不是最后一个元素的元素获得出栈的机会。
Vector
(与ArrayList相似,区别是Vector是重量级的组件,使用使消耗的资源比较多。)
结论:在考虑并发的情况下用Vector(保证线程的安全)。
在不考虑并发的情况下用ArrayList(不能保证线程的安全)。
面试经验(知识点):
java.util.stack(stack即为堆栈)的父类为Vector。可是stack的父类是最不应该为Vector的。因为Vector的底层是数组,且Vector有get方法(意味着它可能访问到并不属于最后一个位置元素的其他元素,很不安全)。
对于堆栈和队列只能用push类和get类。
Stack类以后不要轻易使用。
实现栈一定要用LinkedList。
(在JAVA1.5中,collection有queue来实现队列。)
Set-HashSet实现类:
遍历一个Set的方法只有一个:迭代器(interator)。
HashSet中元素是无序的(这个无序指的是数据的添加顺序和后来的排列顺序不同),而且元素不可重复。
在Object中除了有finalize(),toString(),equals(),还有hashCode()。
HashSet底层用的也是数组。
当向数组中利用add(Object o)添加对象的时候,系统先找对象的hashCode:
int hc=o.hashCode(); 返回的hashCode为整数值。
Int I=hc%n;(n为数组的长度),取得余数后,利用余数向数组中相应的位置添加数据,以n为6为例,如果I=0则放在数组a[0]位置,如果I=1,则放在数组a[1]位置。如果equals()返回的值为true,则说明数据重复。如果equals()返回的值为false,则再找其他的位置进行比较。这样的机制就导致两个相同的对象有可能重复地添加到数组中,因为他们的hashCode不同。
如果我们能够使两个相同的对象具有相同hashcode,才能在equals()返回为真。
在实例中,定义student对象时覆盖它的hashcode。
因为String类是自动覆盖的,所以当比较String类的对象的时候,就不会出现有两个相同的string对象的情况。
现在,在大部分的JDK中,都已经要求覆盖了hashCode。
结论:如将自定义类用hashSet来添加对象,一定要覆盖hashcode()和equals(),覆盖的原则是保证当两个对象hashcode返回相同的整数,而且equals()返回值为True。
如果偷懒,没有设定equals(),就会造成返回hashCode虽然结果相同,但在程序执行的过程中会多次地调用equals(),从而影响程序执行的效率。
我们要保证相同对象的返回的hashCode一定相同,也要保证不相同的对象的hashCode尽可能不同(因为数组的边界性,hashCode还是可能相同的)。
例子:
public int hashCode(){
return name.hashcode()+age;
}
这个例子保证了相同姓名和年龄的记录返回的hashCode是相同的。
使用hashSet的优点:
hashSet的底层是数组,其查询效率非常高。而且在增加和删除的时候由于运用的hashCode的比较开确定添加元素的位置,所以不存在元素的偏移,所以效率也非常高。因为hashSet查询和删除和增加元素的效率都非常高。
但是hashSet增删的高效率是通过花费大量的空间换来的:因为空间越大,取余数相同的情况就越小。HashSet这种算法会建立许多无用的空间。
使用hashSet类时要注意,如果发生冲突,就会出现遍历整个数组的情况,这样就使得效率非常的低。
比较
Collections类(工具类―――全是static 方法)
Public static int binarySearch(List list,Object key)
Public static voidSort(List list,Comparator com)
Comparator接口
Int compare(Object a,Object b)
Boolean equals(Object o)
例子:
import java.util.*;
public class Test {
public static void main(String[] arg) {
ArrayList al = new ArrayList();
Person p1 = new Person("dudi");
Person p2 = new Person("cony");
Person p3 = new Person("aihao");
al.add(p1);
al.add(p2);
al.add(p3);
Collections.sort(al,p1);
for(Iterator it = al.iterator();it.hasNext();){
Person p = (Person)it.next();
System.out.println(p.name);
}
}
}
class Person implements java.util.Comparator
{
public String name;
public Person(String name){
this.name = name;
}
public int compare(Object a,Object b){
if(a instanceof Person&&b instanceof Person){
Person pa = (Person)a;
Person pb = (Person)b;
return pa.name.compareTo(pb.name);
}
return 0;
}
public boolean equals(Object a){return true;}
}
集合的最大缺点是无法进行类型判定(这个缺点在JAVA1.5中已经解决),这样就可能出现因为类型不同而出现类型错误。
解决的方法是添加类型的判断。
反射
1) 确定一个对象的类;
2) 获得一个类的修改符、变量、方法、构器函数、和父类的相类信息;
3) 找出哪些常量和方法是从一个接口声明的;
4) 创建一个在运行时才知道名称的类;
5) 调用对象的方法;
七·异常
异常的基本概念
1) 异常事件改变程序流程;
2) 当一个异常事件发生时,一个异常被抛出;
3) 响应处理异常的代码被称为exception handler;
4) exception handler捕获异常;
5) 异常处理能让你集中精力在一个地方解决问题,然后将处理错误的代码分开来放在另一个地方。
捕获异常
1) 设置一个try/catch的代码块;
2) 如果try块内的任何代码抛出了由catch子句指定的异常,则
a. 程序跳过try块中的其他代码;
b. 程序执行catch从句中的处理器代码。
3) 如try块内没有抛出异常,直接跳过catch从句内容。
4) 如try块内抛出的异常没有在catch从句中指定, 则该方法会立即退出。
处理异常
1.如何控制try的范围:根据操作的连动性和相关性,如果前面的程序代码块抛出的错误影响了后面程序代码的运行,那么这个我们就说这两个程序代码存在关联,应该放在同一个try中。
对已经查出来的例外,有throw(消极)和try catch(积极)两种处理方法。
对于try catch放在能够很好地处理例外的位置(即放在具备对例外进行处理的能力的位置)。如果没有处理能力就继续上抛。
当我们自己定义一个例外类的时候必须使其继承excepiton或者RuntimeException。
3) 对子类方法抛出的异常不能超出父类方法throws指令的范围。如父类方法不抛出任何异常,在子类方法中必须捕捉每一个“已检查异常”。
捕捉多个异常
1) 每个异常类型使用一个catch从句;
2) 如前面catch从句捕获异常,将直接跳过后面catch从句内容;
3) 建议按异常类型的子类->超类的顺序排列catch从句的先后顺序。
finally 声明
无论是否捕获异常,都会执行finally从句中的代码;
例子:
finally{ con.close();}
异常调用栈
1. 哪个方法调用的代码发生异常,返回到调用方法的地方;
2. main方法调用的代码发生异常,返回到虚拟机。
异常层次
答:1) 起源于Error的类代表不常用的环境(通常是硬件层面);
2) 应用程序不能够从Error中恢复正常;
3) 所有的Java异常都起源于Exception;
4) RuntimeExcepiton也称为未检查异常;
5) 未检查异常无须捕获;
6) 其它异常,也称为检查异常,必须处理
Object
↑
Throwable
↑
|ˉˉˉˉˉˉˉˉ|
Error Exception
| ↑
| |ˉˉˉˉˉˉˉ|
RuntimeException
一些未检查的异常
答:1) java.lang.ArithmeticException 如:除0;
2) java.lang.NullPointerException 如:没初始化一个References便使用;
3) java.lang.ArrayIndexoutofBoundsException 如:调用一个有十个元素的Array的第十一个元素的内容;
4) java.lang.NumberFORMatException 如:Integer.parseInt("a");
写你自己的异常
答:1) 要做的仅仅是从Exception继承或者是从Exception的一个子类衍生出自己需要的类就可;
2) 习惯为每一个异常类提供一个默认的构造器以及一个包含详细信息的构造器。
抛出你自己的异常
答:1) 在方法的定义中增加一个throws修饰符,以通知调用者可能会抛出一个异常;
2) 在方法中构造一个该类的实例,然后抛出该实例。
八·图形用户接口
九·AWT(Abstract Window Toolkit) 事件模型
十·The AWT Component Library
十一·JFC(Java Foundation Classes)
十二·Applets
十三·线程Thread
线程原理
进程是数据独占的
线程是数据共享的(所以需要处理数据并发)
并发原理:宏观并行,微观串行
OS将一段时间分为多个时间片,每个时间片CPU只能运行一个任务。
线程实现的两种形式
继承java.lang.Thread:
class MyThread extends Thread{
public void run(){
需要进行执行的代码,如循环。
}
}
启动线程
public class TestThread{
public static void main(){
Thread t1=new Mythread();
T1.start();
}
}
实现java.lang.Runnable接口:
Class MyThread implements Runnable{
Public void run(){
}
}
这种实现可以再继承其他类。
启动线程时不同前者
public static void main(){
Runnable myThread = new MyThread();
Thread t = new Thread(myThread);
t.start();
}
线程的生命周期
下面为线程中的7中非常重要的状态:(有的书上也只有认为前五种状态:而将“锁池”和“等待池”都看成是“阻塞”状态的特殊情况:这种认识也是正确的,但是将“锁池”和“等待池”单独分离出来有利于对程序的理解)
① ⑴
② ⑵
③ ⑶ run()结束
Start()
OS分配CPU
CPU时间片结束
yield() o.wait()
等待锁标记
notify()
注意:图中标记依次为
①输入完毕;②wake up③t1退出
⑴如等待输入(输入设备进行处理,而CUP不处理),则放入阻塞,直到输入完毕。
⑵线程休眠sleep()
⑶t1.join()指停止main(),然后在某段时间内将t1加入运行队列,直到t1退出,main()才加入可运行队列。
特别注意:①②③与⑴⑵⑶是一一对应的。
Thread的方法
public static void sleep(long millis)
throws InterruptedException
括号中以毫秒为单位, 使线程停止一段时间,间隔期满后,线程不一定立即恢复执行。
当main()运行完毕,即使在结束时时间片还没有用完,CPU也放弃此时间片,继续运行其他程序。
Try{Thread.sleep(1000);}
Catch(InterruptedException e){e.printStackTrace(e);}
Public final void join() throws InterruptedException
表示其他运行线程放弃执行权,进入阻塞状态,直到调用线程结束。
实际上是把并发的线程变为串行运行。
线程的优先级:1-10,越大优先级越高,优先级越高被OS选中的可能性就越大。(不建议使用,因为不同操作系统的优先级并不相同,使得程序不具备跨平台性,这种优先级只是粗略地划分)。
注:程序的跨平台性:除了能够运行,还必须保证运行的结果。
Public static void field()
使当前线程马上交出执行权,回到可运行状态,等待OS的再次调用。
Public final Boolean isActive()
验证当前线程是否是活动的,不管它是否正在运行。
共享数据的并发处理
两个线程修改共享资源时会出现数据的不一致,为避免这种现象采用对访问的线程做限制的方法。利用每个对象都有一个monitor(锁标记),当线程拥有这个锁标记时才能访问这个资源,没有锁标记便进入锁池。
1.Synchronized修饰代码块
public void push(char c){
synchronized(this){
...
}
}
对括号内的对象加锁,只有拿到锁标记的对象才能执行该代码块
2.Synchronized修饰方法
public synchronized void push(char c) {
...
}
对当前对象的加锁,只有拿到锁标记的对象才能执行该方法
注:方法的Synchronized特性本身不会被继承,只能覆盖。
线程因为未拿到锁标记而发生阻塞进入锁池(lock pool)。每个对象都有自己的一个锁池的空间,用于放置等待运行的线程。由系统决定哪个线程拿到锁标记并运行。
使用互斥锁的注意事项
锁标记如果过多,就会出现线程等待其他线程释放锁标记,而又都不释放自己的锁标记供其他线程运行的状况。就是死锁。
死锁的两种处理方法
统一排列锁顺序(解决不同方法中对多个共享资源的访问)
对象1的方法
synchronized(a)
synchronized(b)
对象2的方法
synchronized(a)
synchronized(b)
2.线程间通信(也就是线程间的相互协调)
线程间通信使用的空间称之为对象的等待池(wait pool),该队列也是属于对象的空间的。
进入等待池
使用Object类中wait()的方法,在运行状态中,线程调用wait(),此时表示线程将释放自己所有的锁标记和CPU的占用,同时进入这个对象的等待池。等待池的状态也是阻塞状态,只不过线程释放自己的锁标记。
退出等待池进入锁池
notify():将从对象的等待池中移走一个任意的线程,并放到锁池中,那里的对象一直在等待,直到可以获得对象的锁标记。
notifyAll(): 将从等待池中移走所有等待那个对象的线程并放到锁池中,只有锁池中的线程能获取对象的锁标记,锁标记允许线程从上次因调用wait()而中断的地方开始继续运行
注意:只能对加锁的资源进行wait()和notify()。
1) wait():交出锁和CPU的占用;
2) notify():将从对象的等待池中移走一个任意的线程,并放到锁池中,那里的对象一直在等待,直到可以获得对象的锁标记。
3) notifyAll(): 将从等待池中移走所有等待那个对象的线程并放到锁池中,只有锁池中的线程能获取对象的锁标记,锁标记允许线程从上次因调用wait()而中断的地方开始继续运行
注:在java.io包中Vector 和 HashTable 之所以是线程安全的,是因为每个方法都有synchronized修饰。
十四·标准I/O流与文件
I/O流基础
Input/Output:指跨越出了JVM的边界,与外界进行数据交换。
输出
输入
注意:输入/输出是针对JVM而言。
流的分类
1) 从数据类型分:字节流和字符流
2) 从数据方向分:输入流和输出流
3) 从流的功能分:节点流和过滤流
对文件的操作
1.File类(java.io.File)可表示文件或者目录(在JAVA中文件和目录都属于这个类中,而且区分不是非常的明显)。
File下的方法是对磁盘上的文件进行磁盘操作,但是无法读取文件的内容。
注意:创建一个文件对象和创建一个文件在JAVA中是两个不同的概念。前者是在虚拟机中创建了一个文件,但却并没有将它真正地创建到OS的文件系统中,随着虚拟机的关闭,这个创建的对象也就消失了。而创建一个文件才是在系统中真正地建立一个文件。
例如:File f=new File(“11.txt”);//创建一个名为11.txt的文件对象
f.CreateNewFile(); //真正地创建文件
2.File的方法
Boolean createNewFile() //创建文件
Boolean mkdir() //创建目录
Boolean mkdirs() //创建多个目录
Boolean delete() //删除文件
Boolean deleteOnExit(); //在进程退出的时候删除文件,这样的操作通常用在临时文件的删除。
String[] List():返回当前File对象下所以显文件和目录名(相对路径)
File[] ListFiles():返回当前File对象所有Files对象,可以用getName()来访问到文件名。
isDirectory()和isFile()来判断究竟是目录还是文件。
String getParent() 得到父类文件名
File getParentFile() 。。。
String getPath() 。。。路径
处理跨平台性
对于命令:File f2=new file(“d:\abc\789\1.txt”)
这个命令不具备跨平台性,因为不同的OS的文件系统的分隔符是不相同。
使用file类的separtor属性,返回当前平台文件分隔符。
File newD = new File("aa"+File.separator+"bb"+File.separator+"cc");
File newF = new File(newD,"mudi.txt");
try{
newD.mkdirs();
newF.createNewFile();
}catch(Exception e){}
I/O输入输出
1. InputStream类
所以字节输入流的父类,如:FileInputStream,ObjectInputStream,PipedInputStrean
1) 三个基本的read()方法
a. int read(): 从流里读出的一个字节或者-1; (实际读了多长)
b. int read(byte[]):将数据读入到字节数组中,并返回所读的字节数; (期望读了多长)
c. int read(byte[], int , int):两个int参数指定了所要填入的数组的子范围。
2) 其它方法
a. void close(): 关闭流,如使用过滤器流,关闭栈顶部的流,会关闭其余的流。
b. int available(): 返回可从流中读取的字节数。
c. skip(long): 丢弃了流中指定数目的字符。
d. boolean markSupported()
e. void mark(int)
f. void rese()
2. OutputStream方法
答:1) 三个基本的read()方法
a. void write():
b. void write(byte[]):
c. void write(byte[], int , int):
写输出流。
2) 其它方法
a. void close(): 关闭流,如使用过滤器流,关闭栈顶部的流,会关闭其余的流。
b. void flush(): 允许你强制执行写操作。
注:在流中close()方法由程序员控制。因为输入输出流已经超越了JVM的边界,所以有时可能无法回收资源。
原则:凡是跨出虚拟机边界的资源都要求程序员自己关闭,不要指望垃圾回收。
3. FileInputStream和FileOutputStream
答:1) 结点流,使用磁盘文件。
2) 要构造一个FileInputStream, 所关联的文件必须存在而且是可读的。
3) 要构造一个FileOutputStream而输出文件已经存在,则它将被覆盖。 FileInputStream infile = new FileInputStream("myfile.dat");
FIleOutputStream outfile = new FileOutputStream("results.dat");
FileOutputStream outfile = new FileOutputStream(“results.dat”,true);
参数为true时输出为添加,为false时为覆盖。
FileOutputStream类代码:(为什么能建文件)
Public FileOutputStream(String name){
This(name!=null?new File(String):null,false);
}
4.DataInputStream和DataOutputStream
通过流来读写Java基本类,注意DataInputStream和DataOutputStream的方法 是成对的。
过滤流。输出输入各种数据类型。
writeBoolean(boolean b) ------以1 byte数据传送
writeByte(int) ------以1 byte数据传送
writeBytes(String s) --------以byte序列数据传送
writeChar(int v) ――――――以 2 byte
writeChars(String s)-------------以 2 byte序列
writeDouble(double d) -------以 8 byte
writeInt(int v)
writeLong(long l)
writeShort(short s)
writeUTF(String)-----------能输出中文!
5.ObjectInputStream和ObjectOutputStream
过滤流。处理对象的持久化
Object o = new Object();
FileOutputStream fos=new FileOutputStream("Object.txt");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(o);
oos.close();
FileInputStream fis =new FileInputStream(“Object.txt”);
ObjectInputStream ois =new ObjectInputStream(fis);
Object o = (Object)Ois.readObject();
ois.close();
6. BufferInputStream和BufferOutputStream
过滤流,可以提高I/O操作的效率
用于给节点流增加一个缓冲的功能。
在VM的内部建立一个缓冲区,数据先写入缓冲区,等到缓冲区的数据满了之后再一次性写出,效率很高。
使用带缓冲区的输入输出流的速度会大幅提高,缓冲区越大,效率越高。(这是典型的牺牲空间换时间)
切记:使用带缓冲区的流,如果数据数据输入完毕,使用flush方法将缓冲区中的内容一次性写入到外部数据源。用close()也可以达到相同的效果,因为每次close都会使用flush。一定要注意关闭外部的过滤流。
7. PipedInputStream和PipedOutputStream
用来在线程间通信.
PipedOutputStream pos=new PipedOutputStream();
PipedInputStream pis=new PipedInputStream();
try
{
pos.connect(pis);
new Producer(pos).start();
new Consumer(pis).start();
}
catch(Exception e)
{
e.printStackTrace();
}
8. Reader和Writer
1) Java技术使用Unicode来表示字符串和字符,而且提供16位版本的流,以便用类似的方法处理字符。
2) InputStreamReader和OutputStreamWriter作为字节流与字符流中的接口。
3) 如果构造了一个连接到流的Reader和Writer,转换规则会在使用缺省平台所定义的字节编码和Unicode之间切换。
4) 字节流与字符流的区别:
编码是把字符转换成数字存储到计算机中。把数字转换成相应的字符的过程称为解码。
编码方式的分类:
ASCII(数字、英文):1个字符占一个字节(所有的编码集都兼容ASCII)
ISO8859-1(欧洲):1个字符占一个字节
GB-2312/GBK:1个字符占两个字节
Unicode: 1个字符占两个字节(网络传输速度慢)
UTF-8:变长字节,对于英文一个字节,对于汉字两个或三个字节。
9.BufferedReader 和 PrinterWriter 两种好用的字符I/O类
都是过滤流。
BufferedReader的方法:readLine():String
PrintWriter的方法:println(….String,Object等等)和write()