You are on page 1of 113

郝斌 2016年2月25日16:54:54 学完学数据库。

根据ppt上讲的顺序去敲代码,有不懂得可以查帮助文档API

1、java概述

sun公司Green项目 hotjava浏览器

2、java特点

简单易学:没有c和c++的指针,内存申请和释放

安全性高:强类型,垃圾回收机制,禁止非法内存访问。

跨平台:作为一种网络语言,其源代码被编译成一种结构中立的中间文件格式。只
要有Java运行系统的机器都能执行这种中间代码。java源程序被编译成一种与机器无关
的字节码格式,在Java虚拟机上运行。

硬件 操作系统 编译器 高级语言程序 用户

多线程的。即能够使得一个程序同时执行多个任务。

3、java虚拟机 JVM

4、JVM的平台相关性:java源代码与字节码是与机器无关的,故在装有不同操作系统的
机器上,需要有专门为该操作系统开发的JVM,JVM是与机器有关的。

5、java的应用领域

1、J2SE ,主要用来开发桌面应用软件

2、J2ME,嵌入式开发,像手机里的软件,掌上电脑

3、J2EE,属于网络编程,JSP等,

6、怎么学JAVA语言。

《java就业培训》张孝祥,
《java学习笔记》林信良

张孝祥,孙鑫,张利国,马士兵

在eclipse中查看java源码首先在这个网站下载源码或者直接复制此链接就可下载
7、学习目标,了解程序语言及发展历史,掌握语法规则,掌握常用类的使用掌握编程
http://download.java.net/openjdk/jdk7/promoted/b147/openjdk-7-fcs-src-b147-27_jun_2011.zip
逻辑思维能力:会看懂程序,会调试程序,理解并应用面对对象的设计思想。为将来学
下载源码压缩包后,直接拷贝到C:\Program Files\Java\jre1.8.0_251\lib下面
在eclipse中按快捷键F4,随便点击一个类或方法,在主编辑区出现一个页面,找到changed
习J2EE做准备。 attached
source并点击,会弹出对话框,选择external location并按照路径找到刚刚拷贝的压缩包
8、环境变量的设置:2016年2月26日15:03:45

为什么要设置path:1、在dos的任何目录下我们都可以运行系统自带的命令; 2、
我用的是jdk-9;按照右面配置即可。但是!也可以不用先定
义系统变量JAVA_HOME=jdk路径,之后再使用%JAVA_HOME%命
令来获取其路径。不如直接到jdk目录下,然后直接生成其路
径复制过来就可以。各路径之间用英文分号“ ;”隔开。本来
就有的系统变量Path和自建的系统变量CLASSPATH方法同上。
下面的1234步骤是jdk-9下面版本的配置方式。
要想在dos下运行用户自己的程序,则必须进入到改程序的当前目录下方课运行; 3、
如果希望在dos的任何目录下都可以运行自己创建的程序,则需要我们自己手动设置操
作系统自带的环境变量path.

path的设置:操作系统是利用path变量来寻找当前程序所存放的路径,并且以最先
找到的为准。路径与路径之间用分号;分开。

set path = %path%;c:\Intel;

java语言运行

————————————————————————————————

public class MyApp{

public static void main(String[] args){

System.out.println("我能学好Java语言!");

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

c语言编译后exe运行:
9、classpath的设置。

操作系统是利用classpath变量来寻找当前后缀为class的字节码文件所存放的路
径,并且以最先找到的为准。

10、常见dos命令。

cd \ 表示进入当前根目录下 单击Tab键可自动补齐目录名字和目录下的文件名

cd A\B\C 表示当前目录下的A文件夹下的B文件夹下的C文件夹下面

E: 进入E盘根目录

dir 查看文件夹下文件信息

cls 清屏 javac命令编译生成与平
台无关的字节码程序,
javac name.java 编译java文件 名字为类名,扩展名
为.class
java name 运行java文件,即打开.class文件,
只能是name不能是name.class哎!
编译时写文件名,运行时写文件中的类名。故文件的名字和类名最好一样。

11、Java语言的基本要素——标识符

程序员对程序中的各个元素加以命名时使用的命名记号称为标识符(identifier)
包括:类名、变量名、常量名、方法名、……

标识符是以字母,下划线(_)美元符($)开头的一个字符序列,后面可以跟字
母,下划线,美元符,数字。

关键字:abstract default if private

12、数据类型

基本数据类型:
使用8421码辅助记忆
数值型(整数类型(int,byte,short,long)
,浮点类型(float,double)

字符型(char)

布尔型(boolean)

引用数据类型:
(类(class)
,接口(interface)
,数组)

13、输出数据的格式控制:

%d(int, long int, short, byte)

%f(float,double)

%c(char)
%x,%X,%#X,%#x (int long int )

%s (string)

14、常量

(1)整型常量:十进制,十六进制,八进制

一个常量整数默认是int类型,如果数字过大,则必须在末尾加L,否则会报错。
例:long i = 5678678956789;//error

long i = 5678678956789L;//ok

(2)浮点型:一个实数默认是double类型。如果希望一个实数是float类型,可以在
数字后面加f(F)
。将一个double类型数值付给float类型变量,编译时会报错。

例 :float x = 2.2;//error float x = 2.2f;//ok

(3)字符常量:

必须用单引号括起来;Java中字符和字符串都用Unicode编码表示;在Unicode
编码中一个字符占两个字节。‘a'‘\n' '\uxxxx' '\u4e2d'代表Unicode编码下的字符{中}

特殊字符的常量表示法

'\\' '\n'

(4)布尔类型:用boolean表示,不能写成bool;布尔型数据只有两个值true和false,
且他们不对应于任何整数值。

定义:boolean b = true

只能参与逻辑关系运算:&& || == != !

————————————————————————————
class BiJiao
{
public static void main(String[] args)
{
float a = 12.45f;
double b = 45.5;
boolean e;
boolean d;

System.out.printf("e = %b\n", e = a <= b);


System.out.printf("d = %b\n", d = 12.45 <= 45.5);
}
}
结果: e = true
d = true
+++++++++++++++++++++++++++++++++++++++++++++++

15、不同类型变量的字节。
( 一个字节byte 八位bit )

16、不同类型的变量相互转换:一般低字节可以转换成高字节。高字节转换成低字节必须强制转换。

byte b = 10;//1个字节
int i = 6;//4个字节
i = b;//ok b = i;//error 会丢失数据
b = (byte)i;//ok 强制类型转化
//b = i;//本语句错误,上面的(byte)i并没有改变i本身的数据类型。

17、算术运算符:

(1)“+”可以表示数值的相加,字符串的联接,也能把非字符转换成字符串。

System.out.println('a'+1);与System.out.println(""+'a'+1);

结果 98 a1

(2)除法运算符(/)

跟C语言一样。

(3)取余运算符(%)
java中允许取余取余运算符的被除数和除数是实数,
(与C/C++不同)。但所得余数
的正负只和被除数相同。

(4)逻辑与,逻辑或。

跟C语言一样。

(5)位运算符:
(0表示正数,1表示负数)

&(按位与)把两个数字所有的二进制位相与:1&1=1,1&0=0,0&0=0;0&1=0

|(按位或)把两个数字所有的二进制位相与:1|1=1,1|0=1,0|0=0;0|1=1

~(按位取反)把一个数字的所有二进制位取反。~1=0 ~0=1

^(按位异或)把两个数字的所有二进制位异或1^1=0,1^0=1,0^0=0;0^1=1

————————————————————利用异或实现换值————————
import java.util.Scanner;
public class VariableExchange
{
public static void main(String[] args)
{
Scanner scan = new Scanner(System.in);
System.out.println("请输入A的值");
long A = scan.nextLong();
System.out.println("请输入B的值");
long B = scan.nextLong();
System.out.println("A="+A+"\tB="+B); 运行结果:
请输入A的值
System.out.println("执行变量互换..."); 3
A = A ^ B; 请输入B的值
5
B = B ^ A; A=3 B=5
A = A ^ B; 执行变量互换...
System.out.println("A="+A+"\tB="+B); A=5 B=3
}
}
—————————————————————————————————
import java.util.Scanner;//是util而不是until
public class LeapYear
{
public static void main(String[] args)//后面不应该加分号
{
Scanner scan = new Scanner(System.in);
System.out.println("请输入一个年份:");
long year = scan.nextLong();
if(year % 4 == 0 && year % 100 !=0||year % 4 == 0)
System.out.print(year + "年是闰年!");
else
System.out.print(year + "年是不是闰年!");
运行结果:
} 请输入一个年份:
2020
} 2020年是闰年!
+++++++++++++++++++++++++++++++++++++++++++++++++++++++ 电子书113
>>(右移)与 C/C++不同,对于有符号数,在右移时,符号位将随同移动,当为正数
时,最高位0,最高位补零,而为负数时,最高位为1,最高位补1。移位能让用户实现
整数除以或乘以2的n次方的效果。

>>>(右移)无论最高位是0还是1,左边移空的都补为零。

(6)自增自减

(7)三元运算符 boolean b = 20 <= 45 ? true : false;


18、流程控制
同C语言。

——————————————————————
class Triangle //启动类 模拟出事物
{
int a, b, c;

int zhouchang()
{
return a + b + c;
}

double area()
{
double p = 1.0*(a+b+c) / 2;
return Math.sqrt(p * (p-a) * (p-b) * (p-c));
}
}

class TestTriangle
{
public static void main(String[] args)
{
Triangle t = new Triangle();

t.a = 3;
t.b = 4;
t.c = 5;

System.out.printf("%d %f\n", t.zhouchang(), t.area());


}
运行结果:
} 12 6.000000
+++++++++++++++++++++++++++ 2016年2月26日18:32:12 9

19、面向过程设计思想

优点: 1、分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现。
2、以算法为核心,
3、自顶向下设计,要求一开始必须对问题有很深的了解
4、将大问题转化为若干小问题来求解
5、表现形式:用函数来作为划分程序的基本单位
6、直接面向问题。
缺点:数据和操作分离开,对数据与操作的修改变得很困难;数据的安全性得不到保证;
程序架构的依赖关系不合理。

20、面向对象的设计思想

1、确定该问题由哪些问题组成!先用类模拟出该事物
2、通过类间接解决问题。

21、类:把一类事物静态属性和动态可以执行的操作组合在一起所得的这个概念就是类
类是抽象的,用来模拟一类事物,是一个概念,一旦定义了,类的概念就永远存在

22、对象:类的一个个体;具体的实实在在存在的事物;生命周期是短暂的。

左图:内部类N可以使用所有的
访问控制符,外部类M只能使用
访问控制符public或者什么都不
加。类A这种写法是错误的。

对象

类的定义:
class Person
{
int age;
void shout()
{
System.out.println("oh,my god!"+ age);
}
}
对象名.属性(成员变量)// 访问对象的属性
age是类的属性,也叫类的成员变量,也叫字段,也叫域 对象名.成员方法名()// 访问对象的方法
aa.i和aa.j叫做属性引用。
shout是类的方法,也叫类的成员函数 aa.f()叫做方法引用
访问对象的属性和方法就叫做引用。
shout方法可以直接访问同一个类中的age变量。 只有对象引用特殊点
A aa = new A();其中aa叫做类A引用类型的
变量,存放在栈空间,用来指向new出来的A
23、内存分配 对象的对象引用。

——————————类对象实例 1——————————————————
class A
{
int i;
int j;
}
class TestMemo
{
C++中new的用法格式为:
public static void main(String[] args) int* P = new int();
{ 和C中malloc对比着看:
A aa = new A();//(A *)malloc(sizeof(A))int* P = (int *)malloc(sizeof(int));
//new A();在堆中动态分配一块区域,被当作了A对象
//aa本身的内存是在栈中分配的
//堆中内存的地址付给了aa
//aa指向堆中的内存,aa代表了堆中的内存。
//aa.i代表aa静态指针变量所指向的动态内存中的A对象这个i成员
//aa.j代表aa静态指针变量所指向的动态内存中的A对象这个j成员
对象名.属性(成员变量)// 访问对象的属性
对象名.成员方法名()// 访问对象的方法
aa.i和aa.j叫做属性引用。
aa.f()叫做方法引用
访问对象的属性和方法就叫做引用。
只有对象引用特殊点
A aa = new A();其中aa叫做类A引用类型的变量,存放在栈空间,用来指向new出来的对象。
aa.i = 10;
aa.j = 20;
System.out.printf("%d, %d\n",aa.i, aa.j);
}
运行结果:
} 10, 20
+++++++++++++++++++++++++++++2016年2月26日23:47:30 11未完
栈(静态分配)中指针变量aa本身占四个字节,aa是指针变量,只能存放对应类型变量的地址。堆(动态分
配)中分配的一块内存区域被当做A对象,并把这块内存区域的首地址送到指针变量aa中,则aa指向堆中这块
的内存,则表示aa指向A对象,在堆中动态分配的内存块并没有名字,aa指向这块内存,则aa就是他的名字。
aa.j中aa本身的栈内存不含有j这个数据成员,aa是指针变量,只能存放对应类型变量的地址。则aa.j表示,
aa指针变量所指向的堆里面的那块内存A对象的成员j。JAVA中的aa.j等于C语言中的(*aa).j也即aa.j==
(*aa).j。JAVA把C语言指针的*砍掉了。
若是完全等同于C语言应该是这样的A *aa = (A*)malloc(sizeof(A));
JAVA中为A aa = new A;

data segment:是static静态变量和字符串常量的存储区
在创建类的时候,类的属性和方法若是static静态的,其属于类本身。类的属性若是static修饰的,则会在
data segment分配存储空间(对象之间共用),没通过类创建对象时,不是private私有的情况下外部直接就
可以通过'类名.属性'访问使用,不加static则只有在通过类创建对象时才会在堆中为对象分配存储空间,
每个对象之间的属性是不一样的不共用。
类的方法若是加上static修饰放在data segment区(对象之间共用),没创建类的对象时,不是private私有
的情况下外部可以直接通过'类名.方法'访问使用。否则不加static则只有在通过类创建对象时,为类的方
法在code segment区为其分配存储空间(对象之间共用)之后才能被访问。this指向当前调用此方法的对象
static静态方法不能访问非静态成员,非静态方法可以访问static静态成员,上述适用于:类的属性和方法
的内部相互访问和外部访问。因为非静态的要有对象才能被访问,所以静态方法不能访问非静态成员。

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

———————类对象实例 2————————2016年2月27日21:39:59
class A
{
int i;
int j;
}
class TestMemo

{ 堆
public static void main(String[] args)
{
new A
A aa1 = new A();
A aa2 = new A();
new A

aa.i = 50;
aa.j = 80;
System.out.printf("%d, %d\n",aa1.i, aa2.i);
}
}
结论:一个new A()生成的对象是不一样的。
++++++++++++ 结果50 80 +++++++++++++++++++++++++++++++++
——————— 类对象实例 3 ——————————————————
class A
{
int i;
int j;
}
class TestMemo
{
public static void main(String[] args)
{
A aa1 = new A();
aa1中保存new出来的A对象的首地
A aa2 = aa1;//最后aa1和aa2同时指向堆中的一个对象。址,A aa2 = aa1;则aa1中保存的
地址值赋值给了aa2,则aa1和aa2
aa1.j = 50; 内部都保存了A对象的首地址,则
System.out.printf("%d\n",aa2.j);// 其都指向堆中的A对象
} 运行结果:
} 50

++++++++++++ 结果50 +++++++++++++++++++++++++++++++++


—————— 访问控制符 ——————————————————
类的访问控制符有四种:public protected default private
default(默认):如 int i, j, k;前面不加任何访问控制符就是默认default。
在一个类的内部,所有的成员可以相互访问,访问控制符是透明的,访问控制符在自
己本身类内部失效没用;访问控制符是针对外部访问而言的。
在一个类的外部:通过 类对象名.私有成员名 的方式是无法访问该对象中的私
有成员的,这样写编译时会出错。

访问控制符确切的应该叫外部直接访问控制符,类的访问控制符在内部失效,内部透明
private私有的不能被外部直接访问,public公有的可被外部直接访问
class Triangle
{
private int a, b, c;//如果不加访问控制符private,将默认为default,可以被
任何一个对象去使用。

int zhouchang()
{
return a + b + c;
}

void set (int i, int j, int k)


{
a = i;
b = j;
c = k;
}

double area()
{
double p = 1.0*(a+b+c) / 2;
return Math.sqrt(p * (p-a) * (p-b) * (p-c));
}
}

class TestTriangle_2
{
public static void main(String[] args)
{
Triangle t = new Triangle();
t.set(3, 4, 5);
System.out.printf("%d %f\n", t.zhouchang(), t.area());
}
运行结果:
} 12 6.000000
++++++++++++++++++++++++++++++++++++++++++++++++++++++++

——————类的构造函数——————————————————14

class A 不要叫构造函数
要叫构造方法
{
private int i;
private int j;

public A()
{
//System.out.printf("嘿嘿!\n\n");

A aa = new A();//在对象被创建时看到A()没,这个
就是一个函数的形式,这就是一种方法,不过他比较
特殊叫构造方法。
}

//构造函数
public A(int a, int b)
{
i = a;
j = b;
System.out.printf("无参构造函数被调用了!\n");
}

public void show()


{
System.out.printf("i = %d, j = %d\n", i, j);
}
}

class TestConst_2
{
public static void main(String[] args)
{
创建对象aa时,他会调用类A的有参数
A aa = new A(1, 2); 构造函数public A(int a, int b)
//A aa2 = new A(1,2,3); //error
A aa3 = new A(); 创建对象aa3时,他会调用类A的无参数构造函数public A()
若是类A中没有无参数的构造函数,系统会默认自动创建一个无参数的
// 构造函数public A(),但前提是类A中没自己手动创建别的构造函数。
// //System.out.printf("%d %d\n", aa.i, aa.j);
// aa.show(); 任何一个类的对象被生成时,一定会调用其中的某个构造方法
}
}
————————————————————2016年2月27日23:16:04
管理与技术

————————————————————————2016年2月29日14:47:00
class B
{
public int i = 10;

public void show()


{
System.out.printf("%d\n", i);
}
}

class M
{
public static void main(String[] args)
{
B bb1 = new B();
B bb2 = new B();
bb1.i = 20;
bb2.show();//结果为10

//bb1 = bb2;//把bb2的地址赋值给了bb1
//bb1.i = 20;
//bb2.show();//结果为20.看下面的图。
}
}

+++++++++++++++++++++++++++++++++++++++++++++++++++++++
函数的重载:同名的函数通过不同的形参做类似的事情,就叫做函数的重载。
函数重载要求:函数的形参个数,形参顺序,形参数据类型,三个至少有一个是不一样
的 函数的重载:重要的事情说三遍,形参形参形参是形参的个数或顺序或数据类型不同才行
如果两个函数只是函数的返回值不一样,其他的一样,就不构成重载,编译时会出错。

class TestOver
{
static int add(int a, int b)
{
return a + b;
}

static double add(double x, double y)//如果改为static double add(int x,


int y)将会报错
{
return x + y;
}

static int add(int a, int b, int c)


{
return a + b + c;
}

public static void main(String[] args)


{
System.out.printf("%d\n",add(1,2));
System.out.printf("%f\n",add(1.3,2.2));
System.out.printf("%d\n",add(1,2,3));
}
}
++++++++++++++结果:3 3.500000 6++++++++++++++++++++++++++++++++
——————————构造函数例子2———————————————————

class A
这个i是类的属性,如果不初始化,Boolean类型默认是
{ false,数值型默认为0.当其在函数内部属于局部变量,局
public int i = 2; 部变量不初始化不能被使用,会报错
public boolean flag = true;

public A(int j, boolean f)//public:构造方法修饰符,A构造方法的名称


{
i = j;
flag = f;
}

public void show()


{
int k;
System.out.printf("%d\n", k);//会报错,局部变量编译器是不会自动进行初始化的
System.out.printf("%b\n", flag);//boolean用B%或b%输出
}
}

class Testconst_2
{
public static void main(String[] args)
{
A aa = new A(88, false);
aa.show();
}
} 运行时报错:The local variable k may not have been initialized
结论:系统会先执行定义时赋的初值,然后再执行构造函数中赋的初值。如果在定义的
时候不初始化,则他的值是系统自动分配好的默认值。
+++++++++++++++++++++++++ 结果 88 false +++++++++++++++++++

—————— this 关键字 — 视频19 电子书191--2016年3月5日07:43:39

this 是一个系统隐含的指针被自动附加在非静态的成员函数参数列表中。

当前时刻,哪个对象调用该函数,那么this就指向当前调用该函数的对象,系统会
自动在该函数的参数列表中添加一个隐藏的指针,并且把调用该函数的对象的地址赋给
this指针,这样一来,在函数的内部通过this就可以访问当前正在调用该函数的对象的
成员
方法区是共享的又叫非堆,感觉还是描述
成存储在代码段code segment比较准确

右图运行结果
左边这图只是 i = 10
图解指针过程 i = 20
静态函数内部,没有this指针。因为类的静态属性和方法属于类本身,非静态属性和
方法属于类的对象。

关键字this代表当前正在调用该方法的那个对象,具体可分为两种情况:1、在普
通方法中,this代表方法的调用者,即本次调用了该方法的对象。2、在构造方法中,
关键字this代表了该方法本次运行所创建的那个新对象。

——————————————————————————————
class A
{
private int i;
public A(int i)
{
this.i = i;//将形参 i 赋给该构造方法本次运行所创建的那个新对象的i数据成员

}
public void show()
{
System.out.println("i = " + this.i); //this表示当前时刻正在调用show方法的对象
//this可以省略
}
}

public class TestThis


{
public static void main(String[] args)
{
A aa1 = new A(100);
aa1.show();

A aa2 = new A(200);


aa2.show();
}
}
i = 100 i = 200
++++++++++++++++++++++++++++++++++++++++++++++++++++++++

———————— static ————— 20————2016年3月5日09:34:17


static属性i是属于类本身,或者讲:没有创建对象时,该类内部的static属性可以直
接通过“类名.属性”的方式被外部访问。在没有创建对象时,类内部的static方法也能
直接通过“类名.方法”的方式被外部访问。类中非静态的属性和方法,必须先创建对象
才能被外部访问。类自己内部的static属性和static方法,自己内部相互调用时static特性仍然成立
static属性和方法虽然属于类本身,虽然可以通过类名的方式访问,但static属
性和方法很明显也属于类本身,当然也可以通过类对象来访问。
只有非private的static成员才可以通过类名的方式访问。 因为类的静态属性和方法属于类本身,非静
态属性和方法属于类的对象,只有对象被创
建时才会分配内存。假如类的属性是静态
的,那么你通过此类创建十个对象,这十个
对象的静态属性是共用一个属性,只有这一
个属性,不是十个属性。如果类的属性是非
静态的,那么通过此类创建十个对象,就会
有十个属性。每个对象都各有自己的属性。
static只是表明了该成员具有了可以通过类名访问的潜在特征,但是否能通过类名
访问,还必须满足一个条件,就是该类名是非private成员。

——————————————————————————
/*
2016年3月5日10:01:20
本程序证明了:
静态方法不能访问非静态成员 适用于:类的属性和方法的
非静态方法可以访问静态成员 内部相互访问和外部访问。
因为非静态的要有对象才能
*/ 被访问,所以静态方法不能
访问非静态成员

class A
{
private static int i = 10;
public int j = 99;

public static void f()


{
//g(); //error 静态方法不能访问非静态成员
//j = 22; //error
System.out.printf("FFFF\n");
}

public void g()


{
//f(); //OK 说明非静态方法可以访问静态成员
System.out.printf("GGGG\n");
System.out.printf("%d\n", i);
}
}

class TestStatic_5
{
public static void main(String[] args)
{
A aa = new A();
//aa.g();
aa.f();

//A.g(); //error,无法从静态上下文中引用非静态方法
}
} 运行结果:FFFF
data segment:是static静态变量和字符串常量的存储区
++++++++++++++++++++++++++++++++++++++++++++++++++
在创建类的时候,类的属性和方法若是static静态的,其属于类本身。类的属性若是static修饰的,则会在
data segment分配存储空间(对象之间共用),没通过类创建对象时,不是private私有的情况下外部直接就
可以通过'类名.属性'访问使用,不加static则只有在通过类创建对象时才会在堆中为对象分配存储空间,
每个对象之间的属性是不一样的不共用。
类的方法若是加上static修饰放在data segment区(对象之间共用),没创建类的对象时,不是private私有
的情况下外部可以直接通过'类名.方法'访问使用。否则不加static则只有在通过类创建对象时,为类的方
法在code segment区为其分配存储空间(对象之间共用)之后才能被访问。
static静态方法不能访问非静态成员,非静态方法可以访问static静态成员,上述适用于:类的属性和方法
的内部相互访问和外部访问。因为非静态的要有对象才能被访问,所以静态方法不能访问非静态成员。
因为类的静态属性和方法属于类本身,非静
态属性和方法属于类的对象,只有对象被创
建时才会分配内存。假如类的属性是静态
的,那么你通过此类创建十个对象,这十个
对象的静态属性是共用一个属性,只有这一
个属性,不是十个属性。如果类的属性是非
静态的,那么通过此类创建十个对象,就会
有十个属性。每个对象都各有自己的属性。

因为类的静态属性和方法属于类本身,非静
态属性和方法属于类的对象,只有对象被创
建时才会分配内存。假如类的属性是静态
的,那么你通过此类创建十个对象,这十个
对象的静态属性是共用一个属性,只有这一
个属性,不是十个属性。如果类的属性是非
静态的,那么通过此类创建十个对象,就会
有十个属性。每个对象都各有自己的属性。
通过类只能生成一个对象的方法

类,对象,方法,属性,

++++++++++++++++++++++++++++++++

继承extends:利用继承可以较好的模拟出现实世界事物之间的关系。为多态创造条件。

——————————————————————
/*
2009年5月31日14:59:26
本程序证明了:
1、 子类内部可以访问父类非私有的成员
私有成员无法被子类方法访问
2、 通过子类对象名只能访问从父类继承过来的非私有成员
总结:
私有不能被继承
私有物理上已经被继承过来,只不过逻辑上程序员不能去访问它
因此继承必须慎重,否则会浪费内存
*/
class A{
public int i;
protected int j;
private int k;

public void g(){}


private void s(){}
protected void b(){}
}

子类B继承父类A class B extends A{


父类是animal private void m(){
子类是dog/cat
i = 10;
j = 20;
//k = 30; //error 私有属性不能被继承
g();
b();
//s(); // error 私有方法不能被继承
}

public void f(){


i = 10;
j = 20;
//k = 30; //error 私有属性不能被继承

g();
b();
//s(); // error 私有方法不能被继承
}
}

class M{
public static void main(String[] args){
B bb = new B();
bb.i = 20;
bb.j = 30;
//bb.k = 40;//error k可以在B中访问private
bb.b();
bb.g();
//B.g();//无法从静态上下文中引用非静态 方法

//bb.s(); //error
//bb.k = 22;
}
}
+++++++++++++++++++++++++++++++++

java只支持单继承,不允许多重继承。单继承就是一个类只能有一个父类。多继承就是
一个类可以有多个父类。可以有多层继承,即一个类可以继承某一个类的子类,如类B
继承了类A,类C继承类B,那么类C也间接继承了类A。
非私有
子类可以继承父类所有的成员变量和成员方法,但子类永远无法继承父类的构造方法,
在子类的构造方法中可使用语句super(参数列表)调用父类的构造方法。

++++++++++++++++++++++++2016年3月5日11:54:49 28

《head first java》

————————————————2016年3月5日 14:21:47
子类访问父类成员的三种方式

在子类内部访问,通过子类对象名访问,通过子类的类名访问。经验证子类无法通过以
上三种方式访问父类的私有成员,所以,得出结论,私有成员无法被子类继承。

————

super: 在子类的构造方法中可使用语句super(参数列表)调用父类的构造方法。super(参数列表)必
须在构造方法的第一行.
—————————
—class A
{
A()
{
System.out.println("AAAA");
}
A(int i){}
}
class B extends A
{
B()
{
super(2); //如果把该语句注释掉的化,则编译器默认的是自动隐含调用super();
//但如果父类没有无参的构造函数,则会报错
//一个子类的构造函数中只能出现一个 super(....)
System.out.println("BBBB");
}
}
class C extends B
{
C()
{
//int k = 10; //如果该语句生效 则会出错,因为会导致super()语句不是构造函数的
//第一条语句

super(); //35行 每个子类构造方法的第一条语句,都是隐含地调用


//super(),如果父类没有无参的构造函数,那么在编译的时候就会报错。
//super();语句可以写,也可以不写,不写的化,系统会自动调用的

//如果显示写出来的话,编译器要求该语句前面不能加任何语句,也就是说该
//语句必须保证是第一条语句
// super()也可以改为super(2); 但前提是父类必须有带一个参数的构造函数,
//否则也会报错
//如果把35行改为 super(2); 编译时就会报错!

System.out.println("CCCC");
}
}

class TestSuper_1
{
public static void main(String[] args)
{
C cc = new C();

//System.out.println("Hello World!");
}
}
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
总结:

1、每个子类构造方法的第一条语句,都是隐含地调用super(),如果父类没有这种
形式的构造函数,那么在编译的时候就会报错。
2、如果显示的写出super();语句,则必须保证该语句是第一条语句,否则会出错
3、super();如果不写,则编译器会自动添加,所以此时如果父类没有无参的构造函
数就会出错
4 、 既 可 以 显 示 写 super(); 前 提 是 父 类 必 须 有 无 参 的 构 造 函 数 也 可 以 显 示 写
super(实参); 前提是父类必须有带参的构造函数
5、调用父类的构造函数的语句必须借助于super,不能直接写父类的类名,这与C++
不同
6、一个子类的构造函数中只能出现一个 super(....)
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
重写父类方法:是指在子类中重新定义父类中已有的方法

重写方法必须和被重写方法具有相同的方法名词、参数列表和返回值类型
子类中不允许出现与父类同名同参但不同返回类型的方法,如果出现,编译时会报错
覆盖方法时,不能使用比父类中被覆盖的方法更严格的访问权限。重写方法不能低于被重写的方法的访问权限
————————————————————————- super也能调用父类的其他方法和属性
class A{ 如super.f()
super.i
//public void f(){
// System.out.printf("AAAA\n");
//}

public void f(int i){


System.out.printf("哈哈\n");
}
}

class B extends A{
public void f(){
super也能调用父类的其他方法和属性
//super.f(); //调用从父类继承过来的f方法 如super.f()
f(10); super.i

System.out.printf("BBBB\n");
}
}

public class TestOver_2{


public static void main(String[] zhangsan){
B bb = new B();
bb.f();
}
} 运行结果:
哈哈
++++++++++++++++++++++++++++++++++++2016年3月5日16:15:10
BBBB 31

———————————————————2016年3月7日06:23:02
class Human{
private String name;
private int age;

public Human()//不用加分号
{}

public Human(String name, int age){


this.name = name;
this.age = age;
}

public void SetName(String name){


this.name = name;
}

public void Setage(int age){


this.age = age;
}

public String getInfor()//是String类型而不是void类型


{
String strInf = name + " : " + age;
return strInf;
}
}

class Student extends Human{


public String school;
public Student(){}

public Student(String name, int age, String school){


super(name, age);
// this .name = name; //error,共有不能访问私有;
// this.age = age;
this.school = school;
}

public void setSchool(String school){


this.school = school;
}

public String getInfor(){


// String strInf = name + " : " + age + " " + school; //error,共有不能
访问私有;
String strInf = super.getInfor() + " : "+ school;
return strInf;
}
}

public class TestStudent{


public static void main(String[] args){
Student st1 = new Student("校长", 22, "一中");
System.out.printf("%s\n", st1.getInfor());//是st1不是st“L”
}
}
运行结果:
++++++++++++++++++++++++++++++++++++++2016年3月7日07:23:16
校长 : 22 : 一中
**** 多态

定义:同一代码可以随上下文的不同而执行不同的操作,俗称多态。

子类对象可以直接赋给父类引用,但父类对象在任何情况下都不可以直接赋给子类引
用。 父类是animal,子类是dog/cat
dog是animal,animal却不一定就是dog A aa = new B(); 父类引用aa,子类对象new B()
通过父类的引用只能访问子类对象从父类继承过来的成员

通过父类引用不能访问子类对象所特有的成员

父类引用永远不可能直接赋给子类引用(只有在父类引用本身指向的就是一个子类对象
时,才可以把父类引用强制转化为子类引用,其他情况下不允许把父类引用强制转化为
子类引用) A aa = new B();//步1:父类引用aa,子类对象new B(),父类引用指向子类对象
B bb = (B)aa;//步2:经过上面步1后才能将父类引用强制转化为子类对象
(B)aa == new B();
同样的语句,不一样的执行结果
结果为:AAAA
BBBB
多态和指针

虽然父类aa指向子类bb,但编译器任然认为aa是父类
通过父类引用不能访问子类对象所特有的成员

子类对象可以直接赋给父类引用,
但父类对象在任何情况下都不可以直
接赋给子类引用。
————————————————————————
/*
2009年2月11日17:27:54
父类引用永远不可能直接赋给子类引用
注意:
只有在父类引用本身指向的就是一个子类对象时,才可以把父类引用强制转化
为子类引用
其他情况下不允许把父类引用强制转化为子类引用
*/

class A
{
}

class B extends A
{
}

public class TestPoly_3


{
public static void main(String[] args)
{
A aa = new A(); 父类是animal,子类是dog/cat
dog是animal,animal却不一定就是dog
B bb = new B();
//bb = aa; //error 永远不可以把父类引用直接赋给子类引用
//bb = (B)aa; //24行 编译没有错误,但运行时出错! 因为aa指向的是
父类对象

A aa2 = new B();


//bb = aa2; //error 永远不可以把父类引用直接赋给子类引用
bb = (B)aa2; //OK 因为aa2 本身指向的就是一个B类对象 所以可以进行
强制转化,注意与24行的区别 如果父类引用指向的是个子类对象,则可以通过强制
类型转化把父类引用强制转化为子类引用,注意必须强制转化,在Java中无论如何绝对
不可能直接把父类引用赋给子类引用的
}
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

抽象类:通常用来作为一个类族的最顶层的父亲,用最底层的类来表示具体的事物。
最顶层的父类

abstract 抽象类不一定有抽象方法,有抽象方法一定有抽象类。

————————————————————————
//有抽象方法的类一定是抽象类
abstract class A
{ public void f();这样也叫抽象方法,没有方法体
abstract public void f(); //没有方法体的方法叫做抽象方法, 抽象方
法要求末尾必须得加分号,前面必须得加abstract
}

//抽象类不一定有抽象方法
abstract class B
{
public void g()
{
}
}

public class TestAbstract_1


{
public static void main(String[] args)
{
}
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++
abstract class A
{
abstract public void f();
}

class B extends A
{
public void f()
{
System.out.printf("BBBB\n");
}
}

public class TestAbsPoly_1


{
public static void main(String[] args)
{
//A aa = new A(); //error 18行 不能通过抽象类直接创建对象

B bb = new B(); //OK


bb.f(); //OK

A aa; //23行 OK 可以定义一个抽象类的引用,但是不可以定义一个抽象

抽象类不能直接new出一个对象,但是可以创建一个指向对象
的一个抽象类的引用类型的变量aa,A aa;
多态

类的对象,所以18行error, 本行OK
aa = bb;
aa.f();
}
}
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

final关键字可以修饰:整个类,类中的若干个属性,类中的若干个方法。
如果认为一个类已经很完美了不需要子类再去继承的时候可以在前面加final。
修饰类中的若干个属性时,表示该属性必须被赋值,并且只能被赋一次值。 常量只读
修饰方法时,表示该方法可以被子类继承,但不可以被重写。

————————————————————————
class A
{
final public int i = 10; //常变量 和C语言中的const功能一样,值不可变,为只读

public A()
{
i = 10;
}

public void f()


{
//i = 22;
}
}

class TestFinal_1
{
public static void main(String[] args)
{

}
}
+++++++++++++++++++++++++++++++++++++++++
class A
{
final public void f() //如果在public前面加final,则编译时就会报错
{
System.out.println("AAAA");
}
}
创建接口时,接口中的方法不允许有方法体

class B extends A
{
// public void f()
// {
// System.out.println("BBBB");
// }
}

public class TestFinal_2


{
public static void main(String[] args)
{
}
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++
接口

定义:就是抽象方法和常量值的集合。从本质上看,接口就是一种特殊的抽象类
格式:[public] interface interface name [extends]

接口中定义的属性必须是public static final的,而接口定义的方法则必须是public


abstract 的,因此这些修饰符可以省略或部分省略。
public static final int i;
创建接口时,接口中的非静态方法不允许有方
public abstract void f(); 法体。static静态方法可以有方法体

接口的实现 implements (类可以继承类,类不可以继承接口,但类可以实现接口)


abstract class A implements It2{} 实现(implements)就是包含的意思It2接口
内的属性和方法被包含到类A内。
接口可以继承接口,而且可以继承多个接口,即允许多继承。用extends
interface It1 extends It2,It3{}

一个类可以在继承一个父类的同时实现一个或多个接口,但extends关键字必须用在
implements之前。
class T extends A implements It3,It4{}

接口可以实现不相关类的相同行为,可以实现多继承,从一定程度上弥补了类只能单继
承的缺陷。

————————————————————————
class A
{
int i;

public void show()


{
System.out.printf("show-> %d\n", i);//i是属性i,此时的i等价于this.i
}

public void f()


{
int i; //这里的i和属性i没有冲突 。可以只定义不使用。使用之前得初始

//System.out.printf("f-> %d\n", i); //error 因为i是局部变量,Java
要求局部变量在使用之前必须得初始化
}

public void g(int i) //i是形参i 形参i也是局部变量


{
this.i = i; //ok
System.out.printf("f-> %d\n", i);
}
}

public class E
{
public static void main(String[] args)
{
A aa = new A();
//aa.g(99);
aa.show();
}
}
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

面向对象大纲:

封装

访问控制符:只有private的不能在外部访问,类内部访问控制符是透明的
构造方法:定义:名字和类名一样,无返回值。
注意事项:一个类对象只能调用一个构造方法,一个类至少有一个构造方
法,如果自己定义了构造方法,编译器将不在提供默认的构造方
法。

this:一个对象只含有属性的空间,n个对象共用一份方法的拷贝。

static:

继承:java只允许单继承,私有成员无法被继承。
重写:方法名和参数列表和返回值必须一样。访问权限不能过低。

多态

++++++++++++++++++++++++++++++++++++++++2016年3月7日11:23:21 39

包:javac -d . 文件名.java

package zhangsan.lisi.TestPackage先检测当前目录下是否有zhangsan/lisi这个
包(文件夹)
,如果有,在监测该包下是否有zhangsan.lisi.TestPackage这个类,如果
没有,编译器将再去classpath设置的路径中依次寻找。如果都查找失败,则运行时出
错。

set classpath = F:\java;

同包和不同包类的相互访问。
在同一个包中,只有私有的不能被另一个类访问,也只有私有的不能被访问
在不同包没有任何关系的两个类,只有public类的public成员才可以被另一个包中
的类访问。
在不同包中有继承关系的两个类,只有public类的public成员和public类的protected
成员可以被另一个包中的子类在内部使用,但在子类的外部,通过子类对象名只能访问
父类的public成员。

——————————————————————2016年3月8日06:28:18

——————
package zhangsan.lisi;

public class A
{
public void g()
{
System.out.printf("Hello World!\n");
}
protected void b()
{
System.out.printf("Hello JAVA!");
}
}
不同包的类
访问的三种方法

========================================
package com.ruide;
import zhangsan.lisi.*;

class B extends A{
public void f(){
g();//ok
b();//ok, 在 子 类 内 部 可 以 访 问 从 另 一 个 包 继 承 过 来 的 父 类 的 public 和
protected成员。
}
}

class M{
public static void main(String[] args) {
B bb = new B();
bb.f();//ok
bb.g();//ok
//bb.b();//error,不能访问protected。
}
}
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 45

归档工具jar包的生成

作用:发布和使用类库,便于资源的组合和管理
DOS下的命 格式:jar cvf 要生成的包名.jar *
令格式
生成后打开用解压软件打开。

如何使用jar包中的类

import zhangsan.lisi.A;//注意路径后面的分号;

public class Test


{
public static void main (String[] args)
{
A aa = new A();
aa.g();
}
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

**** 异常(Exception)是程序运行过程中发生的事件,该事件可以中断程序指
令的正常执行流程。 出现了异常且没有异常处理语句则整个程
序只会执行到异常那一句,其他语句都不
会被执行。如果有异常处理语句,执行完
————————————————————
异常处理语句会接着执行后面的语句。
class A{
int divide(int a, int b) {
int m;
//System.out.printf("22222\n");
m = a / b;
//System.out.printf("11111\n");
return m;
}
}

public class TestExcep{


public static void main(String[] args){
A aa = new A();

//aa.divide(6, 0);

try{
aa.divide(6,0);//23行 写在try中异常才能被捕获到
异常处理语句
} ArithmeticException是异常类
型的一种,要是不知道是什么
catch (ArithmeticException e) //e用来接收23行抛出的异常对象 类型的异常就用Exception
{
e.printStackTrace(); //可以简单理解为 输出该异常的具体信息
System.out.printf("除零错误, 你的程序出错啦! 除数不能为零!\n");
}

System.out.printf("道可道非常道!\n");
}
}
++++++++++++++++++++++++++++++++++++++++++++++++ 49 2016年3月8日10:45:33

Java API 的使用

++++++++++++++++++++++++++++++++++++++++++2016年3月9日08:00:50 51
运行结果:
java.lang.ArithmeticException: / by zero
at helloworld.A.divide(helloworlda.java:5)
at helloworld.helloworlda.main(helloworlda.java:23)
除零错误, 你的程序出错啦! 除数不能为零!
道可道非常道!
printStackTrace方法的介绍

异常的分类:

error是系统的错误,程序员无法处理这些异常
ArithmeticException是异
常类型的一种,要是不知 Exception是程序员可以捕获并处理的异常。
道是什么类型的异常就用
Exception RuntimeException的子类异常,是可以处理也可以不处理的异常
凡是继承自Exception但又不是RuntimeException子类的异常必须捕捉并处理。

异常的处理步骤:

Finally的作用

无论try所指定的程序块中是否抛出异常,也无论catch语句的异常类型是否与所
抛弃的异常的类型一致,finally中的代码一定会得到执行
finally语句为异常处理提供一个统一的出口,使得在控制流程转到程序的其他部
分以前,能够对程序的状态作统一的管理
通常在finally语句中可以进行资源的清除工作。如关闭打开的文件,删除临时文件
int div(int a, int b) throws Exception
{
自定义异常
}
public static void main(String[] args){
exa aa = new exa();
throw try
表示方法内抛出某 {
用来抛出异常
种异常对象(只能是 aa.div(6, 0);
一个) }catch(Exception e)
格式:throw new 异常名(参数)
{
e.printStackTrace();
}}
throws 抛出异常类
throws在方法后边声明异常,其实就是自己不想对异常
void f() throws A 做出任何的处理,告诉别人自己可能出现的异常,交给
{ 别人处理;
……
注意:方法名后面跟上 throws Exception 证明这个方
} 法里的语句可能会发生异常,注意是可能!在别处如果
调用这个方法时,就必须也抛出异常或者用try catch
处理。 throws是可以单独使用的。
throws可以单独使用,throw不可以单独使用,必须搭
配try catch,或者throws。若程序执行到throw
exception 语句,则后面的语句不会再执行。
package hello;
class exa
{
int div(int a, int b) throws Exception//声明方法可能抛出异常类Exception
{ 异常类进行处理
建议:对throws出的所有异常进行处理
if(0 == b)
{ 如果一个方法内部已经对throws进行处理了,就不用再处理。
throw new Exception("chu shu bu neng wei ling");//29行throw方法内抛出异常对象
} ————————————————————————————————
int sumclass
= a/b;
DivisorIsZeroException extends Exception
return {sum;
} public DivisorIsZeroException(String errorMessage)
} {
public class throwa super(errorMessage);
{ }
public static
} void main(String[] args)
{
exa aa = new exa();
class A
try {
{ int divide(int a, int b) throws DivisorIsZeroException//throws+类
aa.div(6, 0);//48行
{
}catch(Exception e)
{ // try
e.printStackTrace();
// {
}
} // if (0 == b)
} // throw new DivisorIsZeroException("除数不能为零!");
result:hello.Exception:
// } chu shu bu neng wei ling
at hello.exa.div(throwa.java:29)
// catch (DivisorIsZeroException e)
at hello.throwa.main(throwa.java:48)
// {
// e.printStackTrace();
class exa // }
{
int div(int a, int b)
{ if (0 == b)
int sum = 0; throw new DivisorIsZeroException("除数不能为零!");//throw new +对象

try int m = a / b;
{ return m;
if(0 }== b)
{
} throw new Exception("chu shu bu neng wei ling");
}
}catch(Exception
public classe)
TestA
{ {
e.printStackTrace();
} public static void main(String[] args)
sum = a/b; {
A aa = new A();
return sum;
} //aa.divide(6, 2);
} }
}
public class throwa
{ +++++++++++++++++++++++++++++++++++++++++++++++
public static void main(String[] args)
{
exa aa = new exa();
aa.div(6, 0);
}
}
class A
{
public void f()
{
System.out.println("AAAA");
}
}

public class Test


{
public static void main(String[] args)
{
A aa1 = null; //aa1必须被赋值,否则会导致21行出错,因为编译器认为16行并不一定会成功给aa1赋 值
//不过令人遗憾的是:编译器在编译时却检测不出空指针异常错误,以本程序为例,因
//为16行并不一定可以成功给aa1赋值,所以理论上讲21行的aa1仍然可能是null,但是令人
//遗憾的是编译器编译时却检测不出这种错误,它只会在运行时抛出
//java.lang.NullPointerException异常
try
{
aa1 = new A(); //16行
}
catch (Exception e)
{
}
//aa1 = new A();

aa1.f(); //21行 编译器在编译时检测不出空指针异常错误,以本程序为例,因为16行并不一定可以


//成功给aa1赋值,所以理论上讲本行中的aa1仍然可能是null,但是令人遗憾的是编译器编译
//时却检测不出这种错误,它只会在运行时抛出java.lang.NullPointerException异常

// /*
// * 下面两行代码验证了:
// 编译器在编译时检测不出空指针异常错误,它只会在运行时抛出
// java.lang.NullPointerException异常
// */
// A aa2 = null;
// aa2.f(); //编译器编译时检测不会出错,只会在运行时抛出 java.lang.NullPointerException异常
}

}
异常的注意问题:

所有catch只有一个被执行
有可能所有的catch都没有执行
先catch子类异常再catch父类异常(若先catch父类,再子类,会报错)
catch与catch之间不能有其他代码

异常的范围

重写方法抛出的范围不能大于被重写方法排除的异常范围。

——————————————————————————————— 57
/*
2016年3月9日10:16:40
子类覆盖了基类方法时,
子类方法抛出异常的范围不能大于基类方法抛出的异常范围
子类方法可以不抛出异常,也可以只抛出基类方法的部分异常
但不可以抛出基类方法以外的异常
*/

//自定义异常A、B、C
class A extends Exception
{}
class B extends Exception
{}
class C extends Exception
{}

class M
{
void f() throws A, B
{ }
}

class N extends M
{
void f() throws A,B //可以throws A或B,也可以throws A,B 也可以不throws
或者throws C 即"子类覆盖了基类方法时,子类方法抛出异常的范围不能大于基类方
法抛出的异常范围"
{ }
}

class Test
{
public void k(M mm)
{
try
{
mm.f();
}
catch (A aa)
{}
catch (B bb)
{}
}
}

class TestExtendExce
{
public static void main(String[] args)
{
M m = new M();
N n = new N();

//System.out.println("1111");
}
}
//结果: 1111
+++++++++++++++++++++++++++++++++++++++++++++++++++++

异常的优缺点:

没有错误处理的程序:openTheFile;determine its size;allocate that much memory;


read-file ;close-file;程序可读性差,出错返回信息量少,无法确切的了解错误状况
或原因。

用异常的形式处理错误。利用 try{} catch{} {dosomething}

优点:强制程序员考虑程序的安全性和健壮性,增强了程序员对程序的可控性,有利于
代码的调试,把错误处理代码从常规代码给分离出来。

缺点:并不一定能够使程序的逻辑更清晰,并不能解决所有的问题。

+++++++++++++++++++++++++++++++++++++++++++++++++++++++

你学的任何知识要能够用自己的语言说出来。

+++++++++++++++++++++++++++++++++++++++++++++++++++

ToString()方法介绍
所有的类都默认自动继承了Object类
Object类中的toString方法返回的是类的名字和该对象哈希码组成的一个字符串
System.out.println(类对象名)

实际输出的是该对象的toString()方法所返回的字符串 toString()可用来打印对象
的信息。
为了实际需要,建议子类重写父类Object继承的toString方法。

————————————————
class Dian extends Object
{
public int x, y;

public Dian(int x, int y)


{

this.x = x;
this.y = y;
}

public String toString()


{
return "[" + x + ", " + y + "]"; //[3, 5]
}
}

public class TestPoint


{
public static void main(String[] args)
{
Dian d = new Dian(3, 5);
//System.out.printf("%s", d);
System.out.println(d);
}
} 运行结果:[3, 5]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
重写父类的equals()方法
胸肌撕裂者教程

+++++++++++++++++++++++++++++++++++++ 2016年3月9日12:58:48 60

equals ------------------------------ 2016年3月10日10:44:57

+++++++++++++++++++++++++++++++++++++++++++++++++

String类

F fu = new B();//步1:父类引用fu,子类对象new B(),父类引用指向子类对象


B bb = (B)fu;////步2:经过上面步1后才能将父类引用强制转化为子类对象(B)fu == new B();
Object是父类,父类引用obj形参指向子类对象aa2,这样父类引用obj就可以强制转化为子类对象(A)obj
(A)obj == new A();
java.lang.String类对象表示不可修改的Unicode编码字符串。

在java中双引号括起来的字符串也被当作String对象
System.out.println("abc".length());//输出3
System.out.println("abc".equals("abc"));//输出ture

————————————————————————

public class TestString


{
public static void main(String[] args)
{
// String str1 = new String("China");
// String str2 = new String("China");
// System.out.println(str1.equals(str2));//用来判断str1指向的对象的内
容和str2指向的对象的内容是否相等
//
// if(str1 == str2)//是判断其内容相等?还是其指向的对象的内容相等?前
者。
// System.out.println("str1 == str2");
// else
// System.out.println("str1 != str2");//输出str1 != str2

String str3 = "china";


String str4 = "china";
if(str3 == str4)
System.out.println("str3 == str4");
else
System.out.println("str3 != str4");//输出str3 == str4
}
}
data segment数据区:是static静态变量和字符串常量的存储区
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
String类常用方法 String类中有些方法的参数可以是“正则表达式”。
查看JDK_API_1.6_zh_CN.CHM手册
public char charAt(int index) 返回字符串中第index个字符。
public int s.length() 返回字符串的长度
public int indexof(String str) 返回字符串中出现str的第一个位置
public int indexof(String str,int fromIndex)
返回从fromIndex开始出现str的第一个位置
public boolean equalsIgnoreCase(String another)
比较字符串与another是否一样(忽略大小写)
public String replace(char oldchar,chat newchat)
在字符串中用newchar字符替换oldchar字符
…… Character类
判断字母大小写等
静态重载方法 Character.isUpperCase(char ch)//ch是否大写

public static String valueOf()//可将基本类型数据转换为字符串

public static String valueOf(int i)

——————————————————————
public class TestString_1{

public static void main(String[] args)


{
int i = 123;
String str = "456";

//str = i;//error
//i = str;//error
str = String.valueOf(i);//valueOf中的o要大写。
System.out.println(str);
i = Integer.parseInt(str);
System.out.printf("i = %d\n", i);
}
} 运行结果:
123
++++++++++++++++++++++++++++++++++++++++++++++++++++
i = 123
printf和println的区别:

String和StringBuffer 结果:1的值+2的值 是 3;
1的值+2的值 是3;
String类对象一旦创建就不可更改 只读 println黄色的+号是连接符,不会显示

若经常对字符串内容进行修改,则使用StringBuffer。因其里面有很多修改方法

StringBuffer类的构造函数
String为final内容
不能改变为只读 public StringBuffer()创建一个空的没有任何字符的StringBuffer对象
StringBuffer内容
是可更改的 ————————————————————————————
public class TestStringBuffer{
public static void main(String[] args){
StringBuffer sb = new StringBuffer();
sb.append("abc");
append追加
sb.append("123");
System.out.println("sb = " + sb); //sb = abc123
sb.insert(3, "--");
System.out.println("sb = " + sb); //sb = abc--123
sb.delete(2,6); //把下标从2开始到6-1结束的字符删除
System.out.println("sb = " + sb); //sb = ab23
sb.reverse();
System.out.println("sb = " + sb); //sb = 32ba
String str = sb.toString();
System.out.printf("str = " + str); //str = 32ba
}
}
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
public StringBuffer(int capacity)创建一个不带字符,但具有制定初始容量
的字符串缓冲区。
public StringBuffer(String str)创建一个StringBuffer对象,包含与str对象
相同的字符序列。

——————————————————————————————
public class TestStringBuffer_2{
public static void main(String[] args){
StringBuffer sb = new StringBuffer("zhangsan");
//StringBuffer sb2 = "nzhangsan";//error
System.out.println(sb);
}
运行结果:zhangsan
}
++++++++++++++++++++++++++++++++++++++++++++ 2016年3月10日15:02:17 68

——————————————————————— 2016年3月10日15:30:43 69

数组

为什么需要数组:解决大量同类型数据的存储和使用问题,模拟显示世界。

一维数组

——————————————————————
public class TestArray_1
{
public static void main(String[] args)
{
//方式一
int[] arr1; 方法一运行结果:
arr1 = new int[3]; 0
1
arr1[0] = 0; 2
arr1[1] = 1; ************************
arr1[2] = 2;
showArr(arr1);
System.out.println("************************");

//方式二
int[] arr2 = new int[]{0,1,2};//常用形式
showArr(arr2);
System.out.println("************************");
// System.out.println(arr1); //error 一维数组的内容是不能通过
System.out.println()直接输出的,即便该数组的内容是引用且已经重写了toString方
法也不行
方法二运行结果:
// 0
// int[3] arr3 = new int[]{0,1,2}; // error 1
// int[] arr4 = new int[3]{0,1,2}; //error 2
// int[3] arr5 = new int[3]{0,1,2}; //error
************************
//
//方式三
int[] arr6 = {0,1,2}; //25行
showArr(arr6);
System.out.println("************************");
arr6 = new int[]{5,4,3,2,1}; //arr6本来是指向25行的{1,2,3}, 但是也
可以改变arr6的值,使其指向{5,4,3,2,1} 方法三运行结果: 0
showArr(arr6); 1
2
************************
5
4
3
2
1
}

public static void showArr(int[] arr)


{
for (int i = 0; i<arr.length; ++i)
System.out.println(arr[i]);
}
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++

创建并使用基本类型数组

创建并使用引用类型数组

多维数组

数组的拷贝

————————————————————————————
class TestArrayCopy {
public static void main(String[] args) {
int[] a = {1, 2, 3, 4, 5};
int[] b = {-1,-2,-3,-4,-5};

System.arraycopy(b, 0, a, 1, 2);//1 -1 -2 4 5 arr2是源数组 arr1是


目的数组!
System.arraycopy(a, 0, b, 1, 2);//-1 1 2 -4 -5 arr1是源数组 arr2是
目的数组!

System.out.println("a = ");
for (int i=0; i<a.length; ++i){
System.out.println(a[i]); 运行结果:
a =
} 1
-1
System.out.println("b = "); -2
4
for (int i=0; i<b.length; ++i){ 5
System.out.println(b[i]); b =
} -1
1
-1
System.out.println("Hello World!"); -4
} -5
} Hello World!
//将arr1所指向的数组中下标从pos1开始的总共length个元素覆盖掉arr2所指向的数
组中从pos2开始的length个元素
+++++++++++++++++++++++++++++++++++++++++++++++++++++
数组的排序 Arrays

——————————————————————
import java.util.*;

public class TestArraysSort_1{


public static void main(String[] args){
int[] data = {1,3,5,7,2,4,6,8,10,9};
System.out.println("排序前数组data中的内容是:");
showArray(data);

Arrays.sort(data);//排序

System.out.println("排序后数组data中的内容是:");
showArray(data);
}

public static void showArray(int[] data){


for (int e : data)//增强for循环。数组data第一个元素赋给e,循环,然后
第二个元素赋给e
System.out.printf("%d\t", e);
System.out.println("");
} 运行结果: 排序前数组data中的内容是:
} 1 3 5 7 2 4 6 8 10 9
排序后数组data中的内容是:
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 2 3 4 5 6 7 8 9 10
程序: 是一个严格有序的指令集合。程序规定了完成某一任务时,计算机所需做的各
种操作,以及这些操作的执行。

单道程序设计环境:计算机中除了操作系统之外,只存在一个用户程序,即用户
程序独占整个计算机资源。特点:资源的独占性,执行的顺序性,结果的再
现性。
多道程序设计环境:计算机中除了操作系统之外,存在多个用户程序,这些程序
同时运行。特点:间断性,失去封闭性,不可再现性。

进程的由来:为了不破坏“程序”这个词原有的含义,而又能刻画多个程序共同
运行是呈现出的新特征,所以引入了进程的概念。

进程:是一个动态的实体,是程序在某个数据集上的执行。它有自己的生命体,
它因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因
完成任务而被撤销

线程 定义:是一个程序的不同执行路径。
多线程就是在单个程序内部在同一时刻进行多种运算

创建线程方法一:
创建:1、创建一个继承Thread的类(假定类名为A)
,并重写Thread中的run方法。
2、构建一个A类对象,假定对象名为aa。3、调用aa的start方法。
1、 定义一个实现Runnable接口的类, 假定为A; 2、 创建A类对象aa, 代码如下 A aa = new A();3、 利用
创建线程方法二: aa构造一个Thread对象tt, Thread tt = new Thread(aa);4、 调用tt中的start方法tt.start()启动线程;会
自动调用run方法
class A implements Runnable。此时仅仅是类A实现了Runnable接口,并不像方法一一样继承了Thread类,就可以使用Thread类的start()
方法。用实现了Runnable接口的类A去创造一个对象aa即A aa = new A()(创造了实现了Runnable的对象aa)。再使用Thread的构造方法
Thread(Runnable target)去构造一个Thread类的对象tt即Thread tt = new Thread(aa),其中Thread(aa)会自动调用aa中的Runnable。
然后Thread类的对象tt就可以调用Thread的start()方法启动线程,会自动调用run方法;
推荐使用Runnable来实现线程,因为定义一个实现Runnable接口的类A,A aa = new A();创建A类对
象aa,则通过一个aa对象可以构造多个Thread类的对象,Thread ta = new Thread(aa); Thread tb
= new Thread(aa);则引用变量ta和tb即线程ta和tb都指向通过aa创建的对象,对象aa在堆中分配的
是同一块内存,则在synchronized同步时,就不要用static来修饰类A内属性和对象了。

Java 开发中,我们实现多线程,有两种方式, 一种是继承 Thread 类,一种是

实现 Runnable 接口。但是,我们真的理解 Runnable?Runnable 和 Thread

一样吗?都是开启新的线程吗? 为何明显在子线程使用 Handler 的

post(Runnable),最终还是在主线程中执行呢?...带着这些疑问,我们来开始

今天的博文。本文的例子是基于 Android Studio。

PS:本文是讲解 Android 开发中 Runnable 接口。

一、首先通过例子实现这两种方式。
1、继承 Thread 类。

Thread 类是在 java.lang 包中定义的。一个类只要继承了 Thread 类同

时覆写了本类中的 run()方法就可以实现多线程操作了。

首先新建一个 MyThread 类继承自 Thread 类,重写 run()方法,在控制输入


传递的文本,
[java] view plain copy

1. public class MyThread extends Thread {


2.
3. private String name;
4.
5. public MyThread(String name) {
6. this.name = name;
7. }
8.
9. @Override
10. public void run() {
11. System.out.println("MyThread is " + name);
12. }
13. }
接着创建该类,启动该线程(Thread 类的 start()方法),并输出线程的 id,
[java] view plain copy

1. public class Test1 {


2.
3. public static void main(String[] args){
4. MyThread myThread1=new MyThread("线程 1");
5. MyThread myThread2=new MyThread("线程 2");
6. MyThread myThread3=new MyThread("线程 3");
7.
8. myThread1.start();
9. myThread2.start();
10. myThread3.start();
11.
12. System.out.println("myThread1 id ="+myThread1.getId());
13. System.out.println("myThread1 id ="+myThread2.getId());
14. System.out.println("myThread1 id ="+myThread3.getId());
15.
16.
17. }
18.
19. }
控制台输出截图如下,

开启了三个线程。
PS:如果你也是使用 Android Studio,控制台中文输出可能是乱码,那么可

以参考这篇文章去解决,Android Studio 中 Java 控制台中文输出乱码

2、实现 Runnable 接口。

Runnable 只是一个接口,它里面只有一个 run()方法,没有 start()方法,


[java] view plain copy

1. public interface Runnable{


2. public void run();
3. }
所以,即使实现了 Runnable 接口,那也无法启动线程,必须依托其他类。

而 Thread 类,有一个构造方法,参数是 Runnable 对象,也就是说可以

通过 Thread 类来启动 Runnable 实现的多线程。


[java] view plain copy

1. public Thread(Runnable target) {


2. init(null, target, "Thread-" + nextThreadNum(), 0);
3. }
所以,实现 Runnable 接口后,需要使用 Thread 类来启动。
下面还是上案例说明,
创建一个类 MyRunnable,实现 Runnable 接口,
[java] view plain copy

1. public class MyRunnable implements Runnable {


2.
3. private String name;
4.
5. public MyRunnable(String name) {
6. this.name = name;
7. }
8. @Override
9. public void run() {
10. System.out.println("MyRunnable is " + name);
11. }
12. }
和继承 Thread 类的实现方法基本一样,其实 Thread 类也是实现了 Runnable
接口,

下面是调用以及启动线程并打印线程的 id,启动线程还是调用 Thread 类的

start()方法,
[java] view plain copy

1. public class Test1 {


2.
3. public static void main(String[] args){
4.
5. MyRunnable myRunnable1=new MyRunnable("Runnable1");
6. MyRunnable myRunnable2=new MyRunnable("Runnable2");
7. MyRunnable myRunnable3=new MyRunnable("Runnable3");
8.
9. Thread myThread1=new Thread(myRunnable1);
10. myThread1.start();
11. System.out.println("myThread1 id ="+myThread1.getId());
12. Thread myThread2=new Thread(myRunnable2);
13. myThread2.start();
14. System.out.println("myThread1 id ="+myThread2.getId());
15. Thread myThread3=new Thread(myRunnable3);
16. myThread3.start();
17. System.out.println("myThread1 id ="+myThread3.getId());
18. }
19.
20. }
控制台输出截图如下,

可以看到,启动了三个不同的线程。
小结:通过上面的两个小例子程序,我们可以得知,只是实现 Runnable 接口,

并不能启动或者说实现一个线程。Runnable 接口,并不能代表一个线程。

实现 Runnable 接口的类,可以做很多事情,可以传递给任何参数是 Runnable


类的方法,而不仅仅是只能被用于实现一个线程。
二、为何明显使用 Handler 的 post(Runnable),最终还是在主线程中执行呢?

1.我们都知道使用 Handler 更新 UI,有时候会调用 Handler.post()方法更新

UI, Handler.post()方法的源码如下,
[java] view plain copy

1. public final boolean post(Runnable r)


2. {
3. return sendMessageDelayed(getPostMessage(r), 0);
4. }

getPostMessage()方法是创建一个 Message 对象,并且将参数的 Runnable

对象赋值给了该 Message 对象的 callback 属性,


[java] view plain copy

1. private static Message getPostMessage(Runnable r) {


2. Message m = Message.obtain();
3. m.callback = r;
4. return m;
5. }
sendMessageDelayed()方法内部会继续调用其他方法(此处不再详说),而这

一系列的方法最终的功能是,将创建的 Message 对象加入到消息队列中。详情

请看 Android 源码解析 Handler 处理机制(二)。

2.执行 Looper.loop()方法,该方法将会从消息循环中循环取出消息,取出消息
后,会执行下面的代码,
[java] view plain copy

1. public void dispatchMessage(Message msg) {


2. if (msg.callback != null) {
3. handleCallback(msg);
4. } else {
5. if (mCallback != null) {
6. if (mCallback.handleMessage(msg)) {
7. return;
8. }
9. }
10. handleMessage(msg);
11. }
12. }
参数是消息队列取出的消息,如果该消息的 callback 属性(Runnable 对象)等于

‘null’,则会执行 handleMessage()方法,否则,将执行 handleCallback()方

法,我们重点看看 handleCallback()方法,有关 handleMessage()详情请看

Android 源码解析 Handler 处理机制(二)。


[java] view plain copy
1. private static void handleCallback(Message message) {
2. message.callback.run();
3. }

通过上面的分析,这一块是不是更加清晰、明白了 !
[java] view plain copy

1. message.callback.run();

很显然该方法仅仅是执行了消息的 callback 属性(Runnable 对象)的 run()方

法,并没有开启子线程,它其实还是运行在 Handler 所在的线程即主线程中。

小结:使用 Handler.post()方法更新 UI,只是将消息加入到消息队列,并且设

置消息的 callback 属性为参数 Runnable 对象的值;从消息循环中取出消息时,

将执行消息对象的 callback 属性(Runnable 对象)run()方法,还是在 Handler


所在的主线程中运行的,并没有开启新的子线程。
总结:读过本篇文章后,相信读者对 Handler.post()方法更新 UI 理解会更清晰、

完整、透彻,并且对 Runnable 接口会有新的不一样的认识。

PS:在使用 Runnable 时,可能会内存泄露。Runnable 是一个匿名内部类,

因此它对当前 Activity 有一个隐式引用。如果 Activity 在销毁之前,任务还未

完成, 那么将导致 Activity 的内存资源无法回收,造成内存泄漏。那么该怎么


解决这种问题呢?代码如下,
[java] view plain copy

1. static class MyRunnable implements Runnable {


2. @Override
3. public void run() {
4. //执行任务
5. }
6. }
使用 静态内部类,避免了 Activity 的内存资源泄漏。
创建线程第
创建:1、定义一个实现Runnable接口的类,假定为A;2、创建A类对象aa,代码
二种方法:
如下 A aa = new A();3、利用aa构造一个Thread对象tt,Thread tt = new
Thread(aa);4、调用tt中的start方法tt.start();

多线程的优势:编程简单,效率高,适合开发服务程序。

注意问题:

thread中的start()方法就是创建一个新的线程,并自动调用run()方法.
直接调用run()方法是不会创建一个新的线程的。

执行一个线程实际就是执行该线程run方法中的代码

执行完aa.start();后并不表示aa所对应的线程就一定会立即得到了执
行,aa.start();执行完后后只是表示aa线程具有了可以立即被CPU执行的
资格,但由于想抢占CPU执行的线程很多,CPU并不一定会立即去执行aa所
对应的线程。

一个Thread对象能且只能代表一个线程,一个Thread对象不能调用两次
start()方法,否则会抛出异常。

————————————————————— 创建线程方法一
class A extends Thread{
public void run(){
while (true){
System.out.println("AAAA");
}
}
}

public class TestThread_1{


public static void main(String[] args){
A aa = new A();
//aa.run();
aa.start();//会自动调用run方法。start会自动开启线程。
//aa.start();//error,不能调用两次start()方法,
while(true){
System.out.println("BBBB");
}
}
}//按ctrl+c结束循环
————————————————————— 创建线程方法二
class A implements Runnable{
public void run(){
while (true){
System.out.println("AAAA");
}
}
}

class B{}

public class TestThread_2{


public static void main(String[] args){
A aa = new A();
//aa.start();//error
Thread t = new Thread(aa);//public Thread(Runnable target)
//B bb = new B(); 参数:target 称为被创建线程的目标对象。创建
目标对象target的类A负责实现 Runnable接口,
//Thread t = new Thread(bb)//error 给出该接口中run()方法的方法体。
t.start(); 利用构造方法创建新线程对象后,进入线程的新
建状态。
while(true){
System.out.println("BBBB");
}
}
}
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

常用方法:

public final void setName(String name)设置当前线程的名字


public static Thread currentThread()返回对当前正在执行的线程对象的引用
public Thread.getName();返回当前线程的名字。

——————————————————————
class A extends Thread{
public void run()
{
System.out.println("AAAA");
System.out.printf("%s在执行!\n",Thread.currentThread().getName());
}
}

public class TestThread_3{


public static void main(String[] args){
A aa1 = new A();
aa1.setName("三");
aa1.start();

A aa2 = new A();


aa2.setName("四");
aa2.start();
A aa3 = new A();
aa3.setName("五");
aa3.start();

//System.out.println("BBBB");
System.out.printf("%s在执行!\n", Thread.currentThread().getName());
}
}
++++++++++++++++++++++++++++++++++++++++++++++++++++

控制:优先级控制,线程的休眠与让步,挂起和恢复,线程的串行化,生命周期

线程的优先级:java提供了一个线程调度器来监控程序中启动后进入就绪状态的
所有线程。线程的优先级用数字表示,从1到10,一个线程的缺省优先级是5

——————————————————————
public class TestPriority {
public static void main(String[] args)
{
Thread t1 = new Thread(new T1());
Thread t2 = new Thread(new T2());
//t1.setPriority(Thread.NORM_PRIORITY + 3); //考虑把本语句注释掉后
会怎样
t1.start();
t2.start();
}
}

class T1 implements Runnable {


public void run() {
for(int i=0; i<100; i++) {
System.out.println("T1: " + i);
}
}
}

class T2 implements Runnable {


public void run() {
for(int i=0; i<100; i++) {
System.out.println("------T2: " + i);
}
}
}
+++++++++++++++++++++++++++++++++++++++++++ 2016年3月10日18:32:27 74
休眠:暂停执行当前运行中的线程,使之进入阻塞状态,待经过制定的“延迟时
间后”再醒来并转入就绪状态。

无论是继承Thread类的run方法还是实现Runnable接口的run方法,都不能抛
出任何异常

——————————————————————— 2016年3月11日09:18:48 74
public class TestSleep_3{
public static void main(String[] args){
A aa = new A();
Thread tt = new Thread(aa);
tt.start();
子类抛出的异常范围不能超过父类,
} 父类的run方法根本没有抛出异常,
} 子类也当然不能抛出异常。

class A implements Runnable{


public void run() //throws Exception{ //9行
for (int i=0; i<2; ++i){
System.out.println(Thread.currentThread().getName() + " " + i);
try{ //必须对其进行捕获或声明以便抛出
Thread.sleep(1000);
}catch (Exception e)
{ }
System.out.println("FFFFFFF");
//Thread.sleep(1000);
}
}
}
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

线程的让步:

————————————————————
public class TestYield
{
public static void main(String[] args)
{
MyThread mt = new MyThread();
Thread t1 = new Thread(mt);
Thread t2 = new Thread(mt);

t1.setName("线程A");
t2.setName("线程B");

t1.start();
t2.start();
}
}
class MyThread implements Runnable
{
public void run()
{
for(int i=1;i<=20;i++)
{
System.out.println(Thread.currentThread().getName()+": "+i);
if(0 == i%10)
{
Thread.yield();
}
}
}
}
++++++++++++++++++++++++++++++++++++++++++++++++++++
显示Eclipse中Console的
全部内容: 线程的串行化 TestJoin
preference->run/debug->
console设置limit
console output 复选框勾 生命周期控制 TestShutThread
掉方便调试时,查看全部
console。
线程的同步

———买票————————————————————————————
class A implements Runnable{
publicprivate
staticint
inttickets = 100;
tickets = 100;
public void run(){
买票程序的运行结果如下
图:这俩线程访问同一块 while(true){
资源,线程不断切换,导
致卖票重复,线程在执行 if(tickets > 0){
到哪句代码切换,由系统
决定。有可能某一线程在 System.out.printf("%s线程正在卖出第%d张票\n",
--tickets这句话刚要执 Thread.currentThread().getName(),tickets);
行时,票没减一,线程切
换了,那么另外一个线程 --tickets;
会重复卖票。
}
else{
break;
}
}
}
}

public class TestTickets{


public static void main(String[] args){
A aa1 = new A();
Thread t1 = new Thread(aa1);
t1.start();
卖票问题:ABC三个地方同时卖票(相当于三个线程),假设只剩了一张
票,A经过if判断票大于零,把这张票卖了,但此时票数还没减一,线程
就切换到B,B经过if判断票也是大于零的,则会把票卖给B点的人,此时
票还没减一,线程又切换到C,C经过if判断票大于零,又会卖票给C点的
人。则一张票会同时卖给三个人。
解决此问题:由于线程可在任一行代码时进行切换,创造一种机制,就是
线程必须在执行完1-5行代码后,才能切换到下一个线程。
就是同步即互斥:线程中最核心的代码,必须只能有一个线程去访问,其
他线程只有在正在访问的线程退出来后才能去访问。使用synchronized
A aa2 = new A();
Thread t2 = new Thread(aa2);
t2.start();
}
}
++++++++++++++++++++++++++++++++++++++++++++++++++

Synchronized关键字 可以用来修饰一个方法,或者一个方法内部的代码块。
格式:
Synchronized(类对象名aa)
{ 类似于数电状态机
同步代码块//3行
}
static

synchronized(类对象名aa) 结果就是:一个程序正在操作某资源的时候,将不允许其他线程操作该资源,即
一次只允许一个线程处理该资源。

Synchronized修饰一个方法时,实际霸占的是该方法的this指针所指向的对象,
即霸占的是正在调用该方法的对象。注:霸占的专业术语叫锁定,霸占住的
那个对象专业术语叫做监听器。
static静态属性和方法属于类本身,非静态属性和方法属于对象,只有在对象创建时
才会分配内存。此处用了static说明tickets和str是属于类本身,并不是在创建对象
时才创建。str是为了让那么多线程只锁定一个str。若是str不是static的,则str属
于对象不属于类本身,在对象创建时才创建,若是创建对象aa1和aa2,则会创建两个
—————————————————— str。就不能实现互斥访问同一资源(同一资源即str)。同理tckets若不是static
的,则创建对象时aa1和aa2会各有一个tickets即各有100张票,就不是同一资源
class A implements Runnable{ tickets,就不能实现互斥访问同一个tickets即仅仅只有100张票,而是各有100张票
private static int tickets = 100;//static不能省
static String str = new String("AA");//让各个线程锁定一个str。其static也不能省
thread中的start()方法
就是创建一个新的线程, 上面一句改为static String str = "AA";更好
并自动调用run()方法,
哪个线程start(),哪 public synchronized void run()//若把synchronized放在 void 前则会造成只
个线程就会锁定run方 //有一个线程运行
法。导致只能有一个线程 这两个static都不能省,因为如果省略任
运行。其他线程无法开启 { 意一个就会导致不是同一资源,如省略
tickets的static会导致各有100张票,则
while(true){ 会各卖100张票。省略str的static会导致
// synchronized(str)//线程霸占 某些票被卖多次,因为还没执行票减一的
时候,线程就切换了,则下一个线程又会
// { 接着卖此票,导致票卖多次。
if(tickets > 0){
System.out.printf("%s线程正在卖出第%d张票\n",
Thread.currentThread().getName(),tickets);
--tickets; 因为类的静态属性和方法属于类本身,非静态
属性和方法属于类的对象,只有对象被创建时
} 才会分配内存。假如类的属性是静态的,那么
你通过此类创建十个对象,这十个对象的静态
属性是共用一个属性,只有这一个属性,不是
十个属性。如果类的属性是非静态的,那么通
过此类创建十个对象,就会有十个属性。每个
对象都各有自己的属性。
else{
break;
}
// }
}
}
}

public class TestTickets{


public static void main(String[] args){
A aa1 = new A();
Thread t1 = new Thread(aa1);
t1.start(); aa1和aa2是两个对象,则private static int tickets = 100;
和static String str = new String("AA")中的static不能省
略,说明tickets和str属于类本身,并不是在创建aa1和aa2对象
Thread t2 = new Thread(aa1); 时才创建。说明对象aa1的tickets和str分别和对象aa2的tickets
和str是同一个,就可以使用synchronized互斥访问同一个资源
t2.start(); (tickets和str)。

A aa2 = new A();


Thread t3 = new Thread(aa2);
t3.start();
}
}
下面这个是正常使用的卖票程序
————————————————————————————
class A implements Runnable{
public int tickets = 100;
String str = new String("哈哈");

public void run() {


String str = "哈哈"; //数据区存放

while (true){
synchronized (str) {
if (tickets > 0){
System.out.printf("%s线程正在卖出第%d张票\n",
Thread.currentThread().getName(), tickets);
--tickets;
}
else{
break;
}
}
} 不同线程使用run方法时的string_a是同一个,因为string
string_a = "synchronized_test"是放在同一个数据区存放,如
} 果是String string_a = new String("synchronized_test")就
不行因为new是在堆中分配,是不一样的。
}
推荐使用Runnable来实现线程,因为定义一个实现Runnable接口的类
A,A aa = new A();创建A类对象aa,则通过一个aa对象可以构造多个
Thread类的对象,Thread ta = new Thread(aa); Thread tb = new
Thread(aa);则Thread类的引用变量ta和tb即线程ta和tb都指向通过aa
创建的Thread类对象,对象aa在堆中分配的是同一块内存,则在
synchronized同步时,就不要用static来修饰类A内属性和对象了。

public class TestTickets_9{


public static void main(String[] args){
A aa = new A();
Thread t1 = new Thread(aa);
t1.start(); 同一个aa对象,则public static int tickets = 100和
String str = new String("哈哈")中的static可以省
略;因为是同一个对象,则tickets和str是一样的。则
可以实现互斥访问同一资源tickets和str
Thread t2 = new Thread(aa);
t2.start();
}
}
+++++++++++++++++++++++++++++++++++ 记忆曲线。两三天复习一遍。

————2016年3月11日12:26:31 一本好书,能用自己的话说出来,通俗易懂。

——-——2016年3月11日14:21:09 80 “生产消费”

同步概念:通常,一些同时运行的线程需要共享数据。

notify和wait方法

this.notify()

功能:不是叫醒正在执行this.notify()的当前线程,而是叫醒其他线程。
如果当前时刻有其他线程因为执行了aa.wait()而陷入阻塞状态,则叫醒其
中的一个,所谓叫醒就是令某个线程因为wait而陷入阻塞的状态转入就绪状
态。

aa.wait();将执行aa.wait()的当前线程转入阻塞状态,让出cpu的控制权。并释
放对aa的锁定。

aa.notifyAll():叫醒其他所有因为aa.wait()而陷入阻塞状态的线程。

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

awt

——————————————
import java.awt.*;

public class TestCom


{
public static void main(String[] args)
{
Frame f = new Frame();
f.setSize(400,400);
f.setBackground(Color.GREEN);
f.setVisible(true);
}
}
+++++++++++++++++++++++++++++++++++++++++++++++++++
生产和消费:

SynStack
同步栈

正常情况下index++被执行index=3,但index++没
被执行,线程就切换了,index仍然为2

index - - 语句被执行,index由2变为1即
index=1

/*
2009 年 6 月 24 日 15:24:35
生产和消费
*/

class SynStack
{
private char[] data = new char[6];
private int cnt = 0; //表示数组有效元素的个数

public synchronized void push(char ch)//给push方法加synchronized同步,为了在写入数据后cnt还没


//加 1,线程就切换到pop方法去取数据,导致逻辑混乱错误。
若把synchronized放在void前则会造成只有一个线程运行

若把synchronized放
在 void 前则会造成
只有一个线程运行,则
就是ta生产线程或tb
消费线程只有一个在
运行,线程在执行
push或pop方法时,因
为synchronized则线
程互斥,线程不会进
行切换。

若把synchronized放在void前则会造成只有一个线程运行

一个对象只声明不赋值,则只会在内存的栈中创建
引用,堆中并无此引用的指向。
而null对象在堆中会被java的垃圾回收机制回收。
java中对象引用放在栈中,对象的实例放于堆中,
如果为null,说明只在栈中。
声明了一个对象的引用,jvm并没有开辟内存放入
一个对象。
互斥访问同一资源即对象sya

result:
GUI:图形化用户界面(了解一下就好。swing html才是专门的)

组件component:是图形用户界面的基本组成元素。凡是能够以图形化方式显示在
屏幕上并能够与用户进行交互的对象均为组件,如菜单,标签,文本框等。

分类:java.awt.Component

java.awt.MenuComponent

容器:组件通常不能独立地显示出来,必须将组件放在一定的容器中才可以显示
出来。而有一类特殊的组件是专门用来包含其他组件的,这类组件就叫作容
器。java.awt.Container是所有容器的父类,java.awt.Container继承自
java.awt.Component。容器类对象本身也是一个组件,具有组件的所有性质,
但反过来组件却不一定是组件。

Frame常用方法:
——————————————————————————
import java.awt.*;

public class TestFrame_3 {


public static void main(String args[]) {
MyFrame f1 =
new MyFrame(100,100,200,200,Color.BLUE); //一个类A中定义的属性
的类型可以就是类A本身
MyFrame f2 =
new MyFrame(300,100,200,200,Color.YELLOW);
MyFrame f3 =
new MyFrame(100,300,200,200,Color.GREEN);
MyFrame f4 =
new MyFrame(300,300,200,200,Color.MAGENTA);
}
}

class MyFrame extends Frame{


public static int id = 0;

MyFrame(int x,int y,int w,int h,Color color){


super("MyFrame " + (++id));//调用父类的构造方法
setBackground(color);
setLayout(null);
setBounds(x,y,w,h);
setVisible(true);
}
}
+++++++++++++++++++++++++++++++++++++++++++++++++++++

panel

是容纳其他组件的组件。是容器。不能单独存在,必须得被添加到其他容器中。

————————————————
import java.awt.*;

public class TestPanel_1


{
public static void main(String args[])
{
Frame f = new Frame("Java Frame with Panel");
Panel p = new Panel();
f.setLayout(null);
f.setBounds(300,300,500,500); //Frame的设置是相对于整个屏幕而言的
f.setBackground(new Color(100,100,102)); //三个颜色分量都分别使用
一个字节表示
p.setBounds(300/2, 300/2, 500/2, 500/2); //Panel的设置是相对于
Panel所在的父窗口而言的
p.setBackground(new Color(204,204,255));
f.add(p); // 这是java.awt.Container类中的一个方法, 把当前Panel对
象添加到当前Frame对象中
f.setVisible(true);
}
}
+++++++++++++++++++++++++++++++++++++++++++++++++++++++ 83

布局管理器:容器对其中所包含组件的排列方式,包括组件的位置和大小设定,被称为
容器的布局(Layout)

GUI默认布局管理器:

BorderLayout Frame类默认布局管理器用 EAST,SOUTH,CENTER等布局

————————————————————————————
import java.awt.*;
public class TestBorderLayout
{
public static void main(String args[])
{
Frame f;
f = new Frame("Border Layout");
Button bn = new Button("BN");
Button bs = new Button("BS");
Button bw = new Button("BW");
Button be = new Button("BE");
Button bc = new Button("BC");

f.add(bn, "North");
f.add(bs, "South");
f.add(bw, "West");
f.add(be, "East");
f.add(bc, "Center");

// 也可使用下述语句
/*
f.add(bn, BorderLayout.NORTH);
f.add(bs, BorderLayout.SOUTH);
f.add(bw, BorderLayout.WEST);
f.add(be, BorderLayout.EAST);
f.add(bc, BorderLayout.CENTER);
*/

f.setSize(200,200);
f.setVisible(true);
}
}
+++++++++++++++++++++++++++++++++++++++++++++++++++++

FlowLayout panel类默认布局管理器

————————————————————————————————
import java.awt.*;

public class TestFlowLayout {


public static void main(String args[]) {
Frame f = new Frame("Flow Layout");
Button button1 = new Button("Ok");
Button button2 = new Button("Open");
Button button3 = new Button("Close");
f.setLayout(new FlowLayout(FlowLayout.CENTER)); //设置f是
FlowLayout布局,并且是右对齐
f.add(button1);
f.add(button2);
f.add(button3);
f.setSize(300,400);
f.setVisible(true);
}
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++

GridLayout 将空间划分成规则的矩形网络。每个单元格区域大小相等。

————————————————————————
/*
2008年10月18日16:27:16
GridLayout的构造函数
public GriLayout(int, int);
的用法
*/

import java.awt.*;
public class TestGridLayout
{
public static void main(String args[])
{
Frame f = new Frame("GridLayout Example");

Button b1 = new Button("b1");


Button b2 = new Button("b2");
Button b3 = new Button("b3");
Button b4 = new Button("b4");
Button b5 = new Button("b5");
//Button b6 = new Button("b6");
f.setLayout (new GridLayout(2,10)); //第一个参数是多少,则输出的就是
多少行,列数是编译器根据行数自动计算出来的
f.add(b1);
f.add(b2);
f.add(b3);
f.add(b4);
f.add(b5);
//f.add(b6);
f.pack(); //如果把该语句注释掉,则输出结果就变成一个只有标题栏的小
窗口,摘自API:“pack() - 类 java.awt.Window 中的方法, 调整此窗口
的大小,以适合其子组件的首选大小和布局” ,郝斌:也就是把子组件包括
起来,会根据它所容纳的子组件的大小自动调整自身的大小 ,把子组件包括
起来
f.setVisible(true);
}
}
++++++++++++++++++++++++++++ 2016年3月11日17:24:08 简单不代表会用。
—————————————— 2016年3月11日19:46:08

事件处理:

事件Event:用户对组件的一个操作,称之为一个事件。

事件源EventSourse:能够产生事件的GUI组件对象,如按钮、文本框等。

事件处理方法EventHandler:能够接收、解析和处理事件类对象,实现与用户交
互 功能的方法。

事件监听器EventListener:可以处理事件的一个类。

事件处理步骤:

假设事件为XXXX

1、向事件源注册某种事件监听器对象 addXXXXListener(……)

2、设计好可以处理这种事件的事件监听器

class 类名 implements XXXXListener


{
重写XXXXListener接口中的方法
}
说明

事件处理相关概念:

默认情况下事件源不会自动自动产生任何事物,程序员需要做两件事:1、告诉事
件源可以自动产生哪类事件,设计好可以处理这种事件的事件监听器。

一旦完成这两步操作,当用户对事件源进行操作时,事件源就会自动产生事件,
事件源就会自动把产生的时间封装成一个事件对象,事件源就会自动把封装好的事件对
象传递给事件监听器。时间监听器收到事件源发送过来的事件时,事件监听器就会自动
调用相应的事件处理方法来对该事件进行相应的处理。

java.awt.event包中含有所有的事件,常用的事件有:
ActionEvent:激活组件时发生的事件;
KeyEvent操作键盘时发生
MouseEvent操作鼠标时发生
WindowEvent:操作窗口时发生的事件,如最大化或最小化某一窗口。

————————————————————————
import java.awt.*;
import java.awt.event.*;

public class TestButton


{
public static void main(String[] args)
{
Frame f = new Frame();
Button bn = new Button("ok");
f.add(bn);
A aa = new A();

bn.addActionListener(aa);//aa监听bn,来对用户的单击操作进行处理

f.pack();
f.setVisible(true);
}
}
class A implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
System.out.println("perfect");
//System.exit(-1);//单击终止程序
}
}
+++++++++++++++++++++++++++++++++++++++++++

TestFiled监听器:

将整数转化为string的方法 toString

——————————————————————
public class TestInt
{
public static void main(String[] args)
{
int i = 345;
String str;

//第一种方法
// str = i + "";
// System.out.println("str = " + str);

// //第二种
// Integer it = new Integer(i);
// str = it.toString();
// System.out.println("str = " + str);

//第三种
// str = Integer.toString(i);
// System.out.println("str = " + str);

// //第四种:
// str = String.valueOf(i);
// System.out.println("str = " + str);

//第五种: 本方法不对
// str = i; //error 不兼容
// System.out.println("str = " + str);
}
}
+++++++++++++++++++++++++++++++++++++++++++++

三个文本框的相加运算(没听懂)88,89

————————————————
import java.awt.*;
import java.awt.event.*;

public class TestCaculator_1


{

public static void main(String[] args)


{
new.TF().launch();
}
}

class TF
{
public static TextField tf1, tf2, tf3;

public void launch()


{
Frame f = new Frame("Caculator");
tf1 = new TextField(10);
tf2 = new TextField(10);
tf3 = new TextField(10);
Label Lb = new Label("+");
Button bn = new Button("=");
f.setLayout(new FlowLayout());//设置布局

//MyMonitor mm = new MyMonitor();


MyMonitor mm = new MyMonitor(this);
bn.addActionListener(mm);//bn.addActionListener(new MyMonitor()); 括
号里面不能只写MyMonitor。
f.addWindowListener(mm);

f.add(tf1);
f.add(Lb);
f.add(tf2);
f.add(bn);
f.add(tf3);

f.pack();
f.setVisible(true);
}
}

class MyMonitor extends WindowAdapter implements ActionListener //extends在


前,implements在后
{
private TF tf = null;//定义一个属性指向TF,调用里面的tf

public MyMonitor(TF tf)


{
this.tf = tf;
}

@Override
public void actionPerformed(ActionEvent e)//方法的首字母小写,以后的每
个单词首字母大写
{
//int num1 = Integer.parseInt( TestCaculator.tf1.getText() );

String str1 = tf.tf1.getText();


String str2 = tf.tf2.getText();
int num1 = Integer.parseInt(str1);//转换成整型后赋值
int num2 = Integer.parseInt(str2);
int num3 = num1 + num2;

// Integer it = new Integer(num3);


// String str3 = it.toString();

TestCaculator_1.tf3.setText(num3+"");
}

@Override
public void windowClosing(WindowEvent e)//是WindowEvent不是ActionEvent
{
System.exit(-1);
}
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++
利用内部类。
import java.awt.*;
import java.awt.event.*;

public class TestTextField_3


{
public static void main(String[] argsw)
{
new TF().launch();
}
}

class TF
{
private TextField tf1, tf2, tf3;

public void launch()


{
Frame f = new Frame();
tf1 = new TextField(10);
tf2 = new TextField(10);
tf3 = new TextField(10);
Label Lb = new Label("+");
Button bn = new Button("=");
f.setLayout(new FlowLayout());

f.add(tf1);
f.add(Lb);
f.add(tf2);
f.add(bn);
f.add(tf3);

bn.addActionListener(new MyMonitor());

f.pack();
f.setVisible(true);
}
class MyMonitor implements ActionListener //内部类直接访问外部类成员
{
@Override
public void actionPerformed(ActionEvent e)
{
int num1 = Integer.parseInt( tf1.getText() );
int num2 = Integer.parseInt( tf2.getText() );
int num3 = num1 + num2;
tf3.setText(num3+"");
}
}
}
//升级版:TestCalculator.java
++++++++++++++++++++++++++++++++++++++++++
内部类

定义:在A类的内部但是所有方法的外部定义了一个B类,则B类就是A类的内部类,
A是B的外部类。

访问原则:内部类的方法可以访问外部类的所有成员。外部类的方法不可以直接
访问内部类的成员。TestInnerClass_1.java

内部类之间可以相互访问,只要在类中定义一个对象便可。

优点:可以让一个类方便的访问另一个类中的所有成员,增加程序的安全性,有
效的避免其它不想关类对该类的访问。

匿名类:

定义:是一种特殊的内部类

可以访问外部类的所有成员。

创建方式:1、继承父类

2、实现接口

3、实现抽象类

————————————————————————
import java.awt.*;
import java.awt.event.*;

public class TestWindow


{
public static void main(String[] args)
{
final Frame f = new Frame();//非final类型的局部变量无法被匿名类访问
f.setSize(300, 300);

f.addWindowListener( //11行
new WindowAdapter()//对象new完可以加花括号,再加方法//不规范

{
@Override
public void windowClosing(WindowEvent e)
{
f.setVisible(false);
System.exit(-1);
}
}
); //19行

//f.addWindowListener(new MyMonitor()); //本行代码从功能上讲等价于


11行到19行的代码

f.setVisible(true);
}
}

class MyMonitor extends WindowAdapter


{
@Override
public void windowClosing(WindowEvent e)
{
System.exit(-1);
}
}
++++++++++++++++++++++++++++++++++++++++++++++++++++

可运行jar包的生成。
流就是数据传输的管道
———————--------—2016年3月12日19:39:48
新建一个流引用变量就是建立一 2016年3月13日09:59:04 95
个管道,让程序和设备之间连接
起来用于数据传输的管道。
也可以说是数据传输的线缆,如
海底光缆,区分处理流和节点流
流的分类

流的定义

定义:当前设备与其他设备用于数据传输的管道。流一定是类,但类不一定是流
byte 8bit 处理文本

原始流的功能弱,所以需要包裹
流,但是包裹流是在原始流的基
础上建立的,数据传输能力更强
Filewriter关闭close之前要flush一次性写到硬盘中。
是处理流,又叫包裹流

BufferedInputStream是处理流,又叫包裹流
FileInputStream是原始流,包裹流需要原始
流做基底,看JDK API1.6参考手册

包裹流的基底是原始流,字节包裹流基底是字节
原始流,字符包裹流的基底是字符原始流。
同理BufferedReader和BufferedWriter
应该定义一个临时的char类型的数组

字节流byte

字符流char
是处理流,又叫包裹流

将一个长整型数据写入字节数组,然后再从字节数组读出来。本质就是包裹流必须要有一个原始
流才能使用包裹流,先创造一个字节数组的原始流,再使用数据字节流包裹住字节数组的原始流
ByteArrayOutputStream流中toByteArray()方法的含义,摘自API“创建一个新分配的 byte 数
组。其大小是此输出流的当前大小,并且缓冲区的有效内容已复制到该数组中”
是处理流,又叫包裹流
是处理流,又叫包裹流

将System系统默认输出到显示器改为输
出的txt文件中,可以查卡错误日志。
InputStream流中常用的方法:
(语句针对的是byte)

public int read() Throws IOException


读取一个字节并以整数形式返回,如果读取到输入流的末尾则返回-1
pubilc int read(byte[] b);throws IOExcettion
从输入流读取一定数量的字节,并将其存储到缓冲区数组b中,以整数形式返回
实际读取的字节数。
如果b的长度是0,则不读取任何字节并返回0,如果因为流于文件末尾而没有可
用的字节,则返回值-1;
pubilc int read(byte[] b,int off,int len); throws IOExcettion
从输入流中最多读取len个字节的数据并存入byte数组中
b表示读取的数据要存入的数组的名字
off表示第一个读出的字节数
将从输入流所关联到的设备中读取的第一个字节存储在元素b[off]中,下一个
自己存储在b[off+1]中,依次类推,读取的字节数最多等于len
void close() throws IOExcettion
关闭此输入流并释放与该流关联的所有系统资源
long skip(long n) throws IOExcettion
跳过和丢弃此输入流中数据的n个字节。 (不常用)

Reader流的常用方法(与InputStream比没有public,语句针对的是char)

int read() throws IOExcettion


读取一个字符并以整数形式返回(0~255),如果读取到输入流的末尾则返回-1
int read(char[] cbuf) throws IOExcettion
读取一系列字符并存储到一个数组buffer,返回实际读取的字符数。
int read(char[] cbuf, int offset, lnt length) throws IOExcettion
最多读取length个字符,并存储到一个数组buffer,从length位置开始。
void close() throws IOExcettion
关闭流释放内存资源
long skip (long n) throws IOExcettion
跳过n个字符不读,返回实际跳过的字节数。

OutputStream流中常用的方法:

void write(int b) throws IOExcettion


向输出流中写一个字节数据,该字节数据位参数b的低8位。
void write(byte[] b) throws IOExcettion
将一个字节类型的数组中的数据写入输出流
void write(byte[] b,int off, int len) throws IOExcettion
将一个字节类型的数组中的从指定位置off开始的len个字节写入到输出流
void close() throws IOExcettion
关闭流释放内存资源
void flush() throws IOExcettion
将输出流中缓冲的数据全部写出到目的地
Writer流中常用的方法

void write(int c) throws IOExcettion


向输出流中写一个字节数据,该字节数据位参数b的低8位。
void write(char[] cbuf) throws IOExcettion
将一个字节类型的数组中的数据写入输出流
void write(char[] cbuf,int offset, int length) throws IOExcettion
将一个字节类型的数组中的从指定位置off开始的len个字节写入到输出流
void write(String string) throws IOExcettion
将一个字符串中的字符写入到输出流
void write(String string, int offset, int length) throws IOExcettion
将一个字符串从offset开始的length个字符写入到输出流。
void close() throws IOExcettion
关闭流释放内存资源
void flush() throws IOExcettion
将输出流中缓冲的数据全部写出到目的地

文件流:

包括: 字节流:FileInputStream FileOutputSteam

字符流:FileReader FileWriter

FileInputStream的使用:(FileOutputStream 同理)

InputStream是用来读取字节的,是个抽象类,我们通常使用的是该类的子类
FileInputSteam是InputStream的子类,利用FileInputStream可以将一个文件
的内容按字节为单位读取出来。

常用的构造函数:

public FileInputStream(String fileName) Throws FileNotFoundException


利用该构造函数可以实现将输入流连接到某个文件的功能
必须对本构造函数抛出的异常进行捕获
文件路分隔符\\或者/

FileReader的使用:
(FileWriter同理)

Reader是用来读取字符的,是个抽象类,常用的是该类的子类

FileReader是Reader的子类,利用FileReader可以将一个文件的内容以字符为单
位读取出来。

常用的构造函数
public FileReader(String fileName) throws FileNotFoundException

字节流和字符流的区别:
FileInputStream和FileOutputStream可以完成所有格式文件的赋值

FileRead和FileWriter只可以完成文本文件的赋值,无法完成其他格式的复制。

因为字节是不需要解码和编码的,将字节转化为字符才存在这种问题。字节流
可以从所有格式的设备中读写数据,但字符流只能从文本格式的设备中读写数据。

————————————————————————————————————
import java.io.*;

public class TestFileInputStreamOutputStreamCopy


{
public static void main(String[] args)
{
fi就叫流或者流对象。流就是设备和程序之间进行
FileInputStream fi = null; 数据传输的管道。流就是管道,管道名字叫fi。
FileOutputStream fo = null;

try
{
fi = new FileInputStream("F:/java/妹妹来看我.mp3");
fo = new FileOutputStream("F:/Output.txt");
int ch;

while(-1 !=(ch = fi.read()))


{
fo.write(ch);
}
}
catch (FileNotFoundException e)
{
System.out.println("failed to found!");
System.exit(-1);
}
catch (IOException e)
{
System.out.println("failed to read");
System.exit(-1);
}

finally
{
try
{
if(null != fi)
{
fi.close();
fi = null;
}
if(null != fo)
{
fo.close();
fo = null;
}
}
catch (Exception e)
{
e.printStackTrace();
System.exit(-1);
}
}
System.out.println("Copy success!");

}
}
——————————————————————————

import java.io.*;

public class TestIO


{
public static void main(String[] args) throws Exception
{
FileReader fr = new FileReader("D:/share/S5/di-20 流/TestIO.java");
//FileInputStream fr = new FileInputStream("D:\\share\\S5\\di-20 流
\\TestIO.java");//当用字节流读取汉字时,会出现乱码。一般用字符流,这样不会乱
码,而且速度快。
int ch;
int cnt = 0;

ch = fr.read();
while (-1 != ch)
{
++cnt;
System.out.printf("%c", (char)ch);
ch = fr.read();
}
System.out.printf("该文件字符的个数是:%d\n", cnt);
fr.close();
}
}
_________________________________________________

/*
2009年7月3日10:42:43
通过字符流完成文本文件的复制

*/

import java.io.*;

public class TestFileReaderWriterCopy


{
public static void main(String[] args) throws Exception
{
FileReader fr = new FileReader("D:\\share\\S5\\di-20 流
\\TestFileReaderWriterCopy.java");
FileWriter fw = new FileWriter("d:/zhangsan.haha");
int ch;

ch = fr.read();
while (-1 != ch)
{
fw.write(ch);
ch = fr.read();
}
fw.flush();//刷新。有时候不写无法输出。

fr.close();//关闭
fw.close();
}
}
+++++++++++++++++++++++++++++++++++++++++++++要把例子都掌握了。
字符流只能处理文本,不能处理非文本文件,字节流还能处理视频,音乐等,凡是用steam
结尾的都是字节流
——————————————————————
/*
通过字节流可以完成音频格式文件,图像文件的复制,
实际上我们通过字节流可以完成任意格式文件的复制

缺点:没有缓冲区, 处理速度慢
可以参考对比"TestInputStreamOutputStreamCopy_3.java"
*/

import java.io.*;
public class TestInputStreamOutputStreamCopy
{
public static void main(String[] args) throws Exception
{
FileInputStream fr = new FileInputStream("E:\\IBM 教 学
\\java\\lesson_io\\妹妹来看我.mp3");

//FileInputStream fr = new FileInputStream("C:\\1.jpg");

FileOutputStream fw = new FileOutputStream("d:/zhangsan.haha");


int ch;

ch = fr.read();
while (-1 != ch)
{
fw.write(ch);
ch = fr.read();
}
fw.flush();

fr.close();
fw.close();
}
}
——————————————————————
/*
2016年3月12日21:35:49
本程序证明了:
带缓冲区的字节流处理文件的速度要快于不带缓冲区的字节流处理文件的速

可以参考对比"TestInputStreamOutputStreamCopy.java"
*/

import java.io.*;

public class TestInputStreamOutputStreamCopy_3


{
public static void main(String[] args) throws Exception
{
BufferedInputStream bis = new BufferedInputStream( new
FileInputStream("E:\\IBM教学\\java\\lesson_io\\妹妹来看我.mp3"));
BufferedOutputStream bos = new BufferedOutputStream(new
FileOutputStream("d:/zhangsan.haha"));
byte[] buf = new byte[1024];
int len;

len = bis.read(buf);
while (-1 != len)
{
bos.write(buf, 0, len);
len = bis.read(buf);
}
bos.flush();

bos.close();
bis.close();
}
}
++++++++++++++++++++++++++++++++++++++++++
缓冲流就是带有缓冲区的输入输出流

可以显著的减少我们对IO访问的次数,保护我们的硬盘。
缓冲流本身就是处理流(也叫包裹流),缓冲流必须得依附于节点流(也叫原始
流)
。处理流是包裹在原始节点流上的流,相当于包括在管道上的管道。
带缓冲区的字节流处理文件的速度要快于不带缓冲区的字节流处理文件的速度。
常用的构造方法:
BufferedReader(Reader in)
BufferedReader(Reader in,int sz)//sz为缓冲区的大小
BufferedWriter(Writer out)
BufferedWriter(Writer out, int sz)
BufferedInputStream(InputStream in)
BufferedInputStream(InputStream in, int size)
BufferedOutputStream(OutputStream out)
BufferedOutputStream(OutputStream out, int size)

缓冲输入流支持其父类的mark和reset方法。
BufferedReader提供了readLine方法用于读取一行字符串(以\r或\n分割)
BufferedReader提供了newLine用于写入一个行分隔符。
对于输出的缓冲流,写出的数据会先在内存中缓存,使用flus方法使在内存中
的数 据立刻写出。
BufferedOutputStream,BufferedInputStream 依 附 于
OutputStream,InputStream
BufferedOutputStream允许一次向硬盘写入多个字节的数据。
BufferedInputStream允许一次向程序中读入多个字节的数据。
BufferedOutputStream流中有 public int write(byte[] b)方法用来把byte
数组 中的数据输出到当前流所关联到的设备中。
BufferedInputStream流中有 public int read(byte[] b)方法用来把从当前
流所 关联到的设备中存入一个byte数组。
可以定义一个临时的byte类型的数组,用来作为输入流和输出流的中转枢纽。
——————————————————————————————————
import java.io.*;

public class TestBufferedInputStreamOutputStreamCopy


{
public static void main(String[] args)
{
BufferedOutputStream bos = null;
BufferedInputStream bis = null;

try
{
//new FileOutputStream将String型转换为OutputStream型
bos = new BufferedOutputStream(new FileOutputStream("F:/ 来 看
我.txt"));//bos 输出流有个默认的缓冲区,大小为32个字节
bis = new BufferedInputStream(new FileInputStream("F:/java/感恩父
母.flv"));//bis 输入流有个默认的缓冲区,大小为32个字节

byte[] buf = new byte[1024];


int len = bis.read(buf, 0, 1024);//这不是从buf中读数据,而是从bis
所关联到的F:/java/感恩父母.flv文件中读取数据,并将读取的数据写入bis自己的默
认缓冲区中,然后再将缓冲区的内容写入buf数组中,每次最多向buf数组中写入1024个
字节,返回实际写入buf数组的字节个数,
while(-1 != len)
{
bos.write(buf, 0 ,len);//不是写入buf数组,而是将buf数组中下标
从0开始的到len-1为止的所有数据写入bos所关联到的"F:/来看我.txt"文件中
len = bis.read(buf);
}
bos.flush();
bis.close();
bos.close();
}
catch (FileNotFoundException e)
{
System.out.println("没有找到文件!");
System.exit(-1);
}
catch (IOException e)
{
System.out.println("文件读写错误!");
System.exit(-1);
}

System.out.println("文件复制成功!");
}
}
+++++++++++++++++++++++++++++++++++++++

BufferedReader和BufferWriter可以提高读写文本文件内容的速度。

BufferedReader是个常用的流,流中有个readLine是个非常有用的方法。

——————————————————————————————————
import java.io.*;

public class TestBufferedReaderWriterCopy


{
public static void main(String[] args)
{
BufferedReader br = null;
BufferedWriter bw = null;

try
{
br = new BufferedReader(
new FileReader("F:\\java\\TestBufferedReaderWriterCopy.java")
);
bw = new BufferedWriter(
new FileWriter("F:\\Writer.txt")
);
String str = null;

while (null != (str=br.readLine())) //br.readLine()读取一行字符,


但会将读取的换行符自动丢弃,即返回的String对象中并不包括换行符
{
bw.write(str);
bw.newLine(); //写入一个换行符 这行不能省
}
bw.flush();
}
catch (FileNotFoundException e)
{
e.printStackTrace();
System.exit(-1);
}
catch (IOException e)
{
e.printStackTrace();
System.exit(-1);
}
finally
{
try
{
bw.close();
br.close();
}
catch (IOException e)
{
e.printStackTrace();
System.exit(-1);
}
}
}
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

包裹流
数据流:Data//没听懂

DataInputStream能够以一种与机器无关的方式,直接从底层字节输入流读取
java基本类型和string类型的数据。依附于InputStream

常用方法:
public DataInputStream(InputStream in)
public final boolean readBoolean()
public final byte/char/double/…… readByte/readChar/readDouble()
public final String readUTF()

DataOutputStream能够以一种与机器无关的方式,直接将jaca基本类型和
string类型数据写出到其他的字节输出流中。依附于OutputStream
常用方法:
public DataOutputStream(OutputStream in)
public final boolean writeBoolean()
public final byte/char/double/…… writeByte/readChar/readDouble()
public final String writeUTF()

将一个长整型数据写入字节数组然后再从字节数组读出来。

————————————————————————————
import java.io.*;

public class TestData


{
public static void main(String[] args) throws Exception
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();//
DataOutputStream dos = new DataOutputStream(baos);
long m = 1234567;
dos.writeLong(m);

byte[] buf2 = baos.toByteArray();


ByteArrayInputStream bais = new ByteArrayInputStream(buf2);//从buf2

DataInputStream dis = new DataInputStream(bais);
long n;
n = dis.readLong();
//dis.readLong(n); //error

System.out.println("n = " + n);


}
}
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

转换流:

OutputStreamWriter流是把OutputSteam流转换成Writer流

readLine()与回车符的问题

readLine()返回的是“”而不是null,“”表示空字符串,null表示空指针,空
指针就是空地址,空地址就是不指向任何存储单元的意思。

Print流:只有输出,没有输入。

分类:PrintWriter,PrintStream

标准输入输出的重定向
什么叫容器? 容器分为以下三类:
有一个类,这个类里面可以放置其他类的对象, set接口:无序无重复
并可以对存储的这些对象进行排序等操作。 list接口:有序有重复
这个类就叫容器。也可以叫做集合。 map接口:key-value映射对
collection接口 通过这三种接口可实现容器(类)
编程实现将键盘输入的数据输入A文件中,如果输入有误,则把出错信息输出到B
文件中。

e.printStackTrace();默认是把错误信息输出到System.err所关联的设备中

对象的序列化:ObjectOutputSteam,ObjectInputStream.

++++++++++++++++++++++++++++++++++2016年3月13日12:24:36 96 先预习再去看
视频

容器:97 2016年3月13日19:12:03 跟数据结构有点关系

容器里面放着的是对象。 容器里面放的都是某类型的类new出来的对象,如"张三"
String类new出来的对象。66.66是Double类new出来的对象
————————————————————
import java.util.*;

class A{ Object类的toString方法返回的是类的名字和该对象哈
希码组成的一个字符串
public String toString(){ System.out.println(类对象名);实际输出的是该对象
return "哈哈"; 的toString()方法,由于其返回的是该对象哈希码组成
的一个字符串。为了实际需要, 建议子类重写父类
} Object继承的toString方法。
}

public class Test{


public static void main(String[] args) {
ArrayList al = new ArrayList();//a1是个对象
al.add(12345);
"张三"是对象,在java中双引号括起来的字符串也是String类new出来的对象
al.add("张三"); //"张三".length() "张三".conpareTo("李四");
66.66不是double类型是Double类new出来的一个对象
al.add(66.66); //double Double
al.add(new A());
//System.out.println(al[2]); //容器不是数组

System.out.println(al.get(2)); 通过下标获取容器内的对象

Object[] obArr = al.toArray();


System.out.println(obArr[2]); //容器当数组使用,下标。//输出66.66

//System.out.println(al);
}
运行结果:
} 66.66
66.66
++++++++++++++++++++++++++++++++++++++++++++
为什么需要容器:数组存在的缺陷:数组长度难以扩充,数据类型必须相同。

集合就是将若干用途、性质相同或相近的“数据”组合而成一个整体。

集合可分为:Set,List,Map

Collection接口方法介绍:

int size();

Collection接口的子接口

List接口:实现List接口的容器类中的元素是有顺序的,而且可以重复。list:有序有重复

Set接口:容器类中的元素是没有顺序的,不可重复。set:无序无重复

Map接口:

Collection类常用算法:

void sort (List)对List容器内的元素排序 升序

void shuffle (List)对List容器内的对象进行随机排列。

void reverse(List)对List容器内的对象进行逆序排列。

——————————————————
import java.util.*;

public class TestCollections


{
public static void main(String[] args)
{
List lt = new LinkedList();
LinkedList
for (int i=0; i<7; ++i)
{
lt.add("a" + i);
}
System.out.println(lt);
Collections.shuffle(lt); //记住LinkedList中是没有shuffle方法的,因
此需要通过Collections类的相关方法来实现
System.out.println(lt);
Collections.sort(lt); // 默 认 升 序 排 序 , 要 降 序 很 简 单 , 先 调 用
Collections.sort(); 再调用Collections.reverse()
System.out.println(lt);
Collections.reverse(lt); //倒置
System.out.println("倒置之后: " + lt);
System.out.println(Collections.binarySearch(lt, "a5")); //因为lt默认
不是升序排序的,所以调用Collections.binarySearch()方法是不会成功的
Collections.sort(lt);
System.out.println("重新排序之后: " + lt);
System.out.println(Collections.binarySearch(lt, "a5")); //记住,使
[a0, a1, a2, a3, a4, a5, a6]
用binarySearch()方法的前提是该容器已升序排序 [a5, a3, a6, a4, a2, a0, a1]
} 结果 [a0, a1, a2, a3, a4, a5, a6]
倒置之后: [a6, a5, a4, a3, a2, a1, a0]
} -8
重新排序之后: [a0, a1, a2, a3, a4, a5, a6]
++++++++++学完后要问自己,是什么,怎么来,怎么用5 2016年3月14日08:32:09
实现Comparable接口的类,通过实现comparaTo()方法,从而确定
Comparable接口:凡是
排序方式。通过collections,sort()方法来实现自定义对象的自
动排序。
set接口:

————————————————————————————
import java.util.*;

class Student implements Comparable


{
private int id;
private String name;

public Student(int id, String name)


{
this.id = id;
this.name = name;
}

@Override
public String toString() 父类引用变量Object o指向调用compareTo()方
{ 法的子类对象,则此父类引用变量可以强制转
换为子类对象,(Student)o == new Student()
return id + " " + name; //1000张三
//System.out.println();
}

public boolean equals(Object ob)


{
Student st = (Student)ob;
return st.id==this.id && st.name==this.name;
}

运行结果:
[1000 张三, 1001 李四, 1002 王五, 1003 小娟]
list:有序有重复

set接口 set:无序无重复

注意hashCode()方法的大小写

正确的运行结果:不能有重复

不重写equals()和hashCode()
方法的运行结果,有重复,不对
// public int hashCode()
// {
// return id * this.name.hashCode();
// }

// public int compareTo(Object ob)


// {
// Student st = (Student)ob;
// return this.id-st.id;
// }
}

只要以hash字符开头或结尾的类都要重写equals()和hashCode()方法。
public class TestSet
{
public static void main(String[] args)
{
Set S = new TreeSet(); //TreeSet
S.add(new Student(1000, "张三"));
S.add(new Student(1003, "小娟"));
S.add(new Student(1002, "王五"));
S.add(new Student(1001, "李四"));
S.add(new Student(1001, "李四"));
S.add(new Student(1001, "李四"));
S.add(new Student(1001, "李四"));
S.add(new Student(1001, "李四"));
S.add(new Student(1001, "李四"));
System.out.println(S); //[
}
}

为什么要重写hashCode()方法?
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*******equals和hashCode方法的使用
String和Integer这些Java自带的类都重写了hashCode方法,如果String和Integernew
出来的对象的内容是一样的,则这些对象的hashCode返回值也是一样的,虽然这些对
象占用的是不同的内存,不过用户自定义类型则不同,如本程序的A类,即便是两个内
容一模一样的A类对象,它们返回的hashCode值也是不一样的,但是两个内容一模一样
的Integer类对象或者String类对象返回的hashCode值却是一样的,因为系统自带的
String和Integer类都已经重写了Object的hashCode方法嘛!
如果程序员希望自己定义的类对象,占用不同内存空间但内容却是一样的对象调用
hashCode方法返回值是一样的,则程序员就必须自己重写hashCode方法,如本程序的B

如果程序员希望自己定义的类对象,占用不同内存空间但内容却是一样的对象调
用hashCode方法返回值是一样的,则程序员就必须自己重写hashCode方法,如本程序的
B类
——————————————————————————————————
class A
{
private int id;

public A(int id)


{
this.id = id;
}
}

class B
{
private int id;

public B(int id)


{
this.id = id;
}

@Override
public int hashCode()
{
return new Integer(id).hashCode(); 用return (this.id + this.name.hashCode());来取
} 代因为new一个对象占用内存
}

public class TestHashCode_1{


public static void main(String[] args) {
System.out.println("new A(1).hashCode() = " + new A(1).hashCode());
System.out.println("new A(1).hashCode() = " + new A(1).hashCode());
System.out.println();
System.out.println("new Integer(1).hashCode() = " + new
Integer(1).hashCode());
System.out.println("new Integer(1).hashCode() = " + new
Integer(1).hashCode());
System.out.println();
System.out.println("new String(\"haha\").hashCode() = " + new
String("haha").hashCode());
System.out.println("new String(\"haha\").hashCode() = " + new
String("haha").hashCode());
System.out.println();
System.out.println("\"haha\".hashCode() = " + "haha".hashCode());
System.out.println("\"haha\".hashCode() = " + "haha".hashCode());
System.out.println();
System.out.println("new B(1).hashCode() = " + new B(1).hashCode());
System.out.println("new B(1).hashCode() = " + new B(1).hashCode());

Integer it1 = new Integer(1);


Integer it2 = new Integer(1);
System.out.println(it1 == it2);
System.out.println(it1.equals(it2));
System.out.println(it1.hashCode() == it2.hashCode());
}
}
/*
在JDK 1.6中的运行结果是:
--------------------------
new A(1).hashCode() = 14576877 自己定义的类A没有重写hashCode()方法,所以
new A(1).hashCode() = 12677476 hashCode码是不一样的。

new Integer(1).hashCode() = 1 Integer是Sun公司定义的类,其hashCode()方法


new Integer(1).hashCode() = 1 内部被Sun公司重写了,那么hashCode码就相同。

new String("haha").hashCode() = 3194802


new String("haha").hashCode() = 3194802

"haha".hashCode() = 3194802
"haha".hashCode() = 3194802

new B(1).hashCode() = 1 自己定义的类B因为重写hashCode()方法,所以


new B(1).hashCode() = 1 hashCode码是一样的。和自定义的类A比较着看
false
true
true
--------------------------
*/
+++++++++++++++++++++++++++++++++++++++++++++++++++++

Iterator接口

boolean hasNext();是用来判断当前游标后面还是否存在元素,存在则返回真,否则为

Object next();先返回当前游标右边的元素,然后游标后移一个位置,相当于i++

——————————————————————————
import java.util.*;

public class TestIterator_1


{
//可以遍历所有Collection接口的实现类
public static void showCollection(Collection c)
{
Iterator it = c.iterator();//通过c调用iterator返回一个it指针(游标)
while (it.hasNext())//通过指针调用hasNext方法
{
System.out.println(it.next());//it.next()相当于it++,指针下移
}
}

public static void main(String[] args)


{
ArrayList al = new ArrayList();//只有学了C++才知道是怎么造出来的
al.add("one");
al.add(22);
al.add(new Point(1,1));
运行结果:
System.out.println("a1 容器的内容是:"); a1 容器的内容是:
showCollection(al); one
22
[1, 1]
HashSet hs = new HashSet();
hs容器的内容是:
hs.add("one"); 22
hs.add(22); one
hs.add(new Point(1,1)); [1, 1]
System.out.println("hs容器的内容是:");
set接口:无序无重复
showCollection(hs);//输出。 list接口:有序有重复
}
}

class Point
{
private int i, j;

public Point(int i, int j)


{
this.i = i;
this.j = j;
}
public String toString()
{
return "[" + i + ", " + j + "]";
}
}
+++++++++++++++++++++++++++++++++++++++++++++++

TreeSet类:

HashSet类:

Map接口 102

————————————————————————
import java.util.*;

class Student
{
private int id;
private String name;
private int age;

public Student()
{
}

public Student(int id, String name, int age)


{
this.id = id;
this.name = name;
this.age = age;
}

public int hashCode()


{
return this.name.hashCode()*id*age;
}

public boolean equals(Object o)


{
Student s = (Student)o;
return this.name.equals(s.name) && this.id==s.id && this.age==s.age;

public String toString()


{
return id + " " + name + " " + age;
}
}

public class TestHashMap_2


{
public static void main(String[] args){
HashMap hm = new HashMap();
hm.put(1001, new Student(1001, "zhangsan", 20)); //自动封装
hm.put(1003, new Student(1003, "lisi", 30)); //自动封装
hm.put(new Integer(1004), new Student(1004, "wangwu", 10));
hm.put(1002, new Student(1002, "baichi", 20)); //自动封装

//遍历所有的元素
System.out.println("hm容器中所有的元素是:");
Set s = hm.keySet(); //通过keySet方法获取到当前容器键的集合,实际就
是Integer对象的集合
Iterator it = s.iterator();//通过it指针就可以指向s容器里面的每一个关
键字
while (it.hasNext()){//it.hasNext()指针指向下一个
//int Key = (Integer)it.next(); // (Integer) 不能省, 利用了自
动拆分技术
Integer kk = (Integer)it.next();
System.out.println(hm.get(kk));
}

System.out.println("直接查找某一元素");
System.out.println( hm.get(1003) );
System.out.println( hm.get(1005) ); // 如 果 找 不 到 则 直 接 返 回
null,而不是抛出异常
}
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++

感觉最可怕的就是外面瞬息万变,而我一成不变,还不知道外面怎么变。

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
查看手册某些类或方法带有<>尖括号的才
能使用泛型
泛型:是用来限制传入容器、接口的数据类型。 容器里面保存的是某个类的对象

——————————————————————————
import java.util.*;

class Student
{
private int id;
private String name;
private int age;

public Student()
{
}

public Student(int id, String name, int age)


{
this.id = id;
this.name = name;
this.age = age;
}

public int hashCode()


{
return this.name.hashCode()*id*age;
}

public boolean equals(Object o)


{
Student s = (Student)o;
return this.name.equals(s.name) && this.id==s.id && this.age==s.age;

public String toString()


{
return id + " " + name + " " + age;
}
}

public class TestGenerics_2


{
public static void main(String[] args)
{
HashMap<Integer, Student> hm = new HashMap<Integer, Student>();

红色划线的部分
就是泛型,指定
传入容器的类型
//制定当前时刻传入的类型,就不要强制转换成子类型。
hm.put(1001, new Student(1001, "zhangsan", 20));
hm.put(1003, new Student(1003, "lisi", 30));
hm.put(1004, new Student(1004, "wangwu", 10));
hm.put(1002, new Student(1002, "baichi", 20));

//遍历所有的元素
System.out.println("hm容器中所有的元素是:");
Set<Integer> s = hm.keySet();
Iterator<Integer> it = s.iterator();
while (it.hasNext())
{
int Key = it.next(); // it.next() 前面不需要加(Integer) 不能省
//Integer itg = it.next(); //it.next() 前 面 不 需 要 加 (Integer)
59和60行等价
System.out.println(hm.get(Key));
}

System.out.println("直接查找某一元素");
System.out.println( hm.get(1003) );
System.out.println( hm.get(1005) ); //如果找不到 则直接返回null
运行结果:
} hm容器中所有的元素是:
1001 zhangsan 20
} 1002 baichi 20
1003 lisi 30
++++++++++++++++++++++++++++++++++++++++++++
1004 wangwu 10
直接查找某一元素
网络编程(不是重点) 与 jdc 差不多 1003 lisi 30
null
网络程序:能够接受另一台计算机发送过来的数据或向一台计算机发送数据的程

IP:能够在网络中唯一标示一台主机的编号就是IP,IP的地址是一个逻辑地址。32
位,4个字节,常用点分十进制的格式表示。
(从哪发到哪)

端口号:发送到机器的某个程序。

协议:为进行网络中的数据交换(通信)而建立的规则、标准或约定。
(怎么发)
TCP:面向连接的可靠的传输协议。类似于写信
UDP:是无连接的,不可靠的传输协议。类似于写信,发邮件

套接字(socket)

基于UDP的socket编程步骤:

1、定义码头:即定义一个DatagramSocket对象ds
2、定义可以用来接受或发送数据的集装箱:定义:DatagramPacket对象dp

3、在码头上用集装箱接受对方发送过来的数据:ds.receive(dp);

或者在码头上把集装箱中的数据发送给对方:ds.send(dp);

4、关闭码头:ds.close()

—————————————— TestUDPServer — 程序是配套写的。要成对看


import java.net.*;
import java.io.*;

public class TestUDPServer


{
public static void main(String args[]) throws Exception
{
//定义码头
DatagramSocket ds = new DatagramSocket(5678); //5678表示该码头占用
的是5678这个编号,因为一台计算机可以有多个码头接收多个数据,这些码头用不同的
编号来表示,这些编号的专业术语就是端口号

//定义可以用来接受数据的集装箱
byte buf[] = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);

try
{
while(true)
{
//在码头上用集装箱接受对方发送过来的数据
ds.receive(dp); //注意:本语句执行完毕就意味着,dp数据包中就
已经含有了从客户端接收过来的数据

//从集装箱中取出对方发送过来的数据
ByteArrayInputStream bais = new
ByteArrayInputStream(dp.getData()); //1、 ByteArrayInputStream的内核必须是个
字节数组,并且是从该字节数组中读取数据 2、dp.getData()表示把dp集装箱中的数
据转化为一个字节数组并返回该字节数组
DataInputStream dis = new DataInputStream(bais);
System.out.println(dis.readLong());
}
}
catch (Exception e)
{
e.printStackTrace();
ds.close(); //关闭码头
}

}
}
/*
在JDK 1.6中的运行结果是:
----------------------------------
10000
数据源自: 127.0.0.1 : 1464
10000
数据源自: 127.0.0.1 : 1471
10000
数据源自: 127.0.0.1 : 1474
----------------------------------
*/
———————————— TestUDPClient ————————
import java.net.*;
import java.io.*;

public class TestUDPClient


{
public static void main(String args[]) throws Exception
{
//定义码头ds
DatagramSocket ds = new DatagramSocket();

//13行到23行完成的功能是: 定义可以发送数据的集装箱dp
long n = 10000L; //13行
ByteArrayOutputStream baos = new ByteArrayOutputStream(); // 注 意
ByteArrayOutputStream 的 所 有 构 造 函 数 都 没 有 byte[] buf 这 样 的 形 参 , 即 定 义
ByteArrayOutputStream流对象时是不能指定byte数组的,因为这个连接到的byte数组
是 由 ByteArrayOutputStream 自 动 生 成 的 9 行 API:"public
ByteArrayOutputStream(): 创建一个新的 byte 数组输出流。缓冲区的容量最初是 32
字节,如有必要可增加其大小。 "
//9行代码一旦执行完毕,意味着两点: 1、在内
存中生成了一个大小为32个字节的byte数组 2、有一根叫做baos的管道已链接到了该
byte数组中,并且可以通过这个管道向该数组中写入数据
//虽然此时可以通过baos向baos所连接到的在
内存中分配好的byte数组中写入数据,但是ByteArrayOutputStream流并没有提供可以
直接把long类型数据直接写入ByteArrayOutputStream流所连接到的byte数组中的方法,
简单说我们没法通过baos向baos所连接到的byte数组中写入long类型的数据, 查API文
档可以发现: ByteArrayOutputStream流中并没有类似writeLong()这样的方法,但是
DataOutputStream流中却有writeLong() writeFloat()等方法
DataOutputStream dos = new DataOutputStream(baos);
dos.writeLong(n); //把n变量所代表的10000L写入dos所依附的baos管道所
连接到的内存中的大小为32字节的byte数组中

byte[] buf = baos.toByteArray(); //DataOutputStream 流 中 并 没 有


toByteArray()方法,但是ByteArrayOutputStream 流中却有toByteArray()方法, 所以
不可以把baos 改为dos, 否则编译时会出错! ByteArrayOutputStream流中toByteArray()
方法的含义,摘自API“创建一个新分配的 byte 数组。其大小是此输出流的当前大小,
并且缓冲区的有效内容已复制到该数组中”
DatagramPacket dp = new DatagramPacket(buf, buf.length,
new
InetSocketAddress("127.0.0.1", 5678)
); //23行

//在码头上把集装箱中的数据发送给对方
ds.send(dp);

//关闭码头
ds.close();
}
}
++++++++++++++++++++++++++ 看程序首先得知道它运行的结果。
数组数据→集装箱→码头
这样数据就发送出去了。

You might also like