public class HelloWorld{
//static: 通过类找到此main方法并调用
//public: 使得JVM能找到并使用此main方法
//void: JVM不需要返回值
public static void main(String[] args){
//定义一个String类型局部变量 变量名为 s
String s;
s=”HelloWorld”;
//这里调用System.out(输出流)的println()时会自动调用已经覆写了的toString(),所以输出的结果不是对象的地址而是对象的值。
System.out.println(”s said:”+s);
}
}
文字解析HelloWorld
- 1. 五个文件:一个源文件(HelloWorld.java),四个字节码文件(HelloWorld.class , String[].class , String.class , System.class)
- 2. 五个对象:一个字符串数组对象(args),一个常对象(s),三个流对象(System.in , System.out , System.error)
- 3. 三个方法:main() , println() , toString()
- 4. 零个属性:类中没有定义变量
- 5. 两个局部变量:args , s
- 6. 说明:
字符串数组 String[] ,堆中有的是空数组对象,new String[0] 中有对象但是没有值。GC作用于堆。
代码区包括类代码和共享代码,数据区包括静态数据区也叫常量池和缓冲数据区(包括整型 Integer -128~+127 ,字符串对象)
栈中的方法执行空间包括断点调试,临时变量,数据局部变量等等。
主类public是编译器的入口,主方法main是JVM的入口。
String 与 String[] 是两个不同的类
20142023 软件1412段飞霞
面向对象的相关知识
C语言的缺点:内存需要自己去管理,变量不赋值会导致系统崩溃指针太麻烦;优点是效率高。
Java的缺点:Java以牺牲效率为代价来提高安全性,自动释放内存。
Java中一般只有一个主类是公有(public),其他的类都是默认的。
静态的继承编译时知道,动态的继承执行时才知道。
Java支持多平台的原因:
1、Java是先编译再解释。
2、JVM的上层模块相同,下层与系统相接的地方不同。
3、JVM(虚拟机)的个数与系统的个数相同。这就是一次编写到处运行的原因。JVM是相同的,然后将.Class文件解释为相应的机器指令。
在编译与解释中,.java与.class文件都不会变化,JVM是变化的。
Java源代码与字节码与机器无关,JVM与机器有关。
JVM是由硬件和软件模拟的计算机
关于GC(垃圾回收机制)
GC:垃圾回收机制,优点是更好的回收使用内存,缺点是效率不高。
内存泄漏是指内存使用不当导致系统崩溃。
Java的GC机制只能适当的改善问题,不能避免内存泄漏问题。
环境变量配置的原因:
1、Path:……bin,找到java javac等命令
2、Classpath:……lib,找到标准库中的类(不能省略)
3、JAVA_HOME:针对tomcat配置的,确定jdk的位置。
JVM的内存:
方法区:分为代码区和数据区。
堆:堆中一般存放对象,对象都有属性和方法(一般都有四个字节)
栈:栈中主要包括形参,局部变量,断点,堆空间的地址,方法区空间的地址以及临时数据。
实例方法通过对象调用
除了常对象在方法区的常数据区中开辟空间,其他的对象都在堆中开辟空间。
Applet(java应用小程序)
Applet:是不安全的,容易被黑客入侵,程序由浏览器执行
HTML是静态的,但是存储在服务器上然后下载到浏览器上由浏览器执行。
1+1<2 的原理是模块化
一个程序只要有主类 public修饰的类就可以被编译生成.class文件
面向对象:
面向对象的概念:以对象为核心,属性与需求都会有变化,但是对象不会变,从而保证程序的正确性和可维护性。
对象是把事物放在问题领域中抽象出它的方法。
对象与对象之间的关系主要有两种:继承和关联(一对一,一对多,多对多)
普通类默认继承Object类
类与属性之间的绑定通过this关键字
参数签名:参数类型,参数个数,参数顺序
封装:是指用共有方法操作私有属性
构造方法:
构造方法没有返回值类型但是会有参数签名。没有return
构造方法在类中可以有多个,单例的构造方法是私有的
创建一个对象时会调用多个构造方法(this)
如果有父类,则也会调用父类的构造方法(super)
抽象类有构造方法但是接口中没有构造方法
构造方法是由JVM来调用的,不是通过对象调用
构造方法有无数个,但是无参的构造方法有且只有一个。
一般类的访问权限分为两种:public和 default
构造方法的作用:为堆里面的对象的实例全局变量赋初值(初始化)
不能通过this来调用构造方法,但是调用构造方法一定要传this指针
创建对象先默认初始化再调用构造方法初始化:
创建对象的第一步在堆里开辟空间,首先开辟属性空间再开辟方法空间再默认初始化。基本类型值为0,引用类型为null,再进行构造方法的初始化
调用构造方法一定要给对它传对象的地址,构造方法不能是静态的
方法在堆中开辟空间,通过对象调用,开辟多次空间
结构化程序设计就是面向过程的意思。(模块化)
C语言是以函数为单位
架构师分句需求分析建立基本架构,一般是用MVC的三层架构,流程图主要用于详细设计。项目的成败决定于需求的获取。
对象的属性:
通过公有的方法对私有属性进行访问,可以对用户传送的数据进行约束。
属性或者域都是指在类里面定义的变量,一般都用private,外部不能直接访问
属性开辟空间的大小和数据类型有关,方法一般都是四个字节
初始化过程分为两次:调用构造方法时为对象的属性和方法初始化
默认初始化(普通数据默认为0,引用类型的数据为null)
this指针是指对象的地址,对象的值,对象没有名
实例方法存在于方法区的共享代码段,堆中存放的是实例方法的引用或地址
调用构造方法不一定会创建对象,但是创建对象一定会调用构造方法。
抽象类与接口中都可以定义属性
静态方法不能传递this指针。
问题一:创建一个对象时为什么会调用多个构造方法?
因为此类可能有父类,所以会调用父类的构造方法
问题二:this指针是怎么传递的,它的地址是怎么样的?
虚拟机传递过去的
方法分为:实例方法,主方法,构造方法。
只有对象才能调用实例方法,实例方法只能通过对象调用
编译时不能创建对象,只有在执行时虚拟机才有可能创建对象。
在调用构造方法之前就已经有了对象的地址
实例方法不能直接调用,一定要通过对象调用
主类public是编译入口,主方法main是jvm入口(执行)
封装的作用:隐藏类的实现细节,设定权限,保证数据的完整性,有利于修改与增强代码的可维护性。
方法的重写:是指方法名还有参数签名都和父类一致。
方法的重载:是指方法名相同但是参数签名不同
包申明:只有字节码文件放在包指定的目录下才能被使用
包导入:指定字节码文件的具体位置
包导入时 * 与.具体类 的执行效率不同,后者效率高。
包申明只有一个,包导入可以有多个。
当测试类的.class文件与测试类的.java文件不在同一目录下时,执行测试类应加上类的相对路径 例如 javac mypack.Test
在doc命令中带包编译的格式 javac –d . aa.java
this的使用: 当属性名和方法中的变量名重名时,在方法中会优点调用方法中定义的变量,若想调用类中定义的属性应在前面加上this. 表示通过对象调用。
栈中分配空间,调用一个方法会分配一个空间。
信息隐藏:是指把属性定义为私有的,外部不能直接访问。
Java一般通过接口来保证稳定性。
序列化:表示可以存储在硬盘中的数据。(类可以实现序列化接口)
注释分为两种:
说明性注释:(类的作用,作者,版本,日期,接口)
功能性注释:(某属性,代码的功能)
对类的注释:类的作用,作者,版本
对方法的注释:方法的作用,参数,返回值,异常
Javadoc:收集对类的注释,在命令行中:javadoc A.java ,会根据注释生成一系列的文件,以index.Html为入口查看。
Java类的一般只有public和默认类型,接口也是这样。
Javadoc收集的注释是对public类型的方法以及默认的方法的注释。不包括protected和private类型的注释。
Javadoc –private Test.java//收集对所有类型的方法的注释
Javadoc –private –version –author Test.java//收集所有方法的注释以及程序版本和作者
标识符包括:字母,下划线,$,_,以及数字(但是数字不能用于开头)
汉字可以作为标识符: Strirng 你好=null;//是合法的
标识符不能定义为关键字或者标准类库中的类
关键字:
boolean: 1个字节,用于关系判断和逻辑判断
byte:1个字节, -128~127
short:2个字节,-32768~32767
long:8个字节 int:4个字节
float:有效位数为8位 double:有效位数为15位
abstract:可以用来修饰类或方法,可以被继承但是不能自己创建对象
assert:断言,没有经过验证的结果
break:跳出本层循环 continue:跳出本次循环,开始下一次循环
final:定义的类不能被继承 定义的方法不能被重写只能重载 定义属性只能初始化,不能赋值
new关键字的最终目的是返回对象的地址
super:标明父类的特征,通过this去找到父类的对象
throw:抛异常 throws:声明异常
transient:瞬时状态
A :字符型 ‘A’ 十进制 65 十六进制 41(16) Unicode ‘\u0041\’
A:字符型 ‘a 十进制 97 十六进制 61(16) Unicode ‘\u0061\’
回车符 : 字符型 ‘\r ’ 十进制 13 十六进制 d(16) Unicode ‘\u000d\’
换行:字符型 ‘\n’ 十进制 10 十六进制 a(16) Unicode ‘\u000a\’
空格:字符型 ‘ ’ 十进制 32 十六进制 20(16) Unicode ‘\u0020\’
Java只有值传递,没有地址传递。
Java.Lang:包括 throws ,Exception, Math, String, System ,Integer
一个8进制数相当于三个二进制数
一个16进制数相当于四个二进制数
不同数据类型之间的转换原则:
当数据中有double类型时,结果一定为double类型
当整形数据与实型数据进行运算的时候,结果应该为实型数据
Byte char short 数据类型的数据进行运算结果为int型
双目运算会改变运算量的类型,单目运算和复合赋值运算不会改变运算变量的类型,
i+=1//逆波兰式 复合赋值 f_abc//匈牙利变量命名规则用下划线连接
int a=(int)1.567;//造型,向下转型
数值尽量不要用造型容易造成数据丢失
引用类型:一定与对象关联 存放的是对象的地址而不是对象本身
声明时不会创建对象,只会分配引用空间
Javac *.java//一次编译当前包下的所有.java文件
编译时会对所有的语句进行检查,不管是否被调用到
一般不要在普通方法中创建对象,因为这样容易引起内存泄漏
实例全局变量:在堆中开辟空间,对象创建时开辟,有默认的值,构造方法会为实例全局变量初始化 创建几次对象就会被创建几次,对象调用,空间在对象消失时销毁gc
静态全局变量:具有全局性,该属性归该类所有对象公有,没有this指针,一般通过类使用也可以通过对象调用
静态全局变量的内存空间是固定的一块,被修改后,往后的引用的值都会一起被修改
实例全局变量只能对对象调用,局部变量只能由方法调用,静态全局变量能由方法,类,对象调用。
静态全局变量:
静态全局变量加载时在类的方法区的静态数据区开辟一次空间,构造方法不对静态变量初始化,并且静态变量没有this指针,不使用对象调用但是每个对象都对它可以有引用,静态变量的生存周期和程序的一样,一般不会被释放。
静态数据和类的关系叫关联(属性与类)
动态和类的关系叫绑定(方法与类)
Java没有真正的输出地址,除非复写了hashcode toString方法,当你拿到对象的地址时,JVM会自动的通过地址找到对应的值输出。
在同一个类中,加载此类时,为静态全局变量的开辟内存空间比为main()开辟空间早
局部变量存储在栈中,没有初始化是不会有默认值,全局变量有默认值
形参是指函数申明时的参数,实参是程序运行过程中实际调用此函数的时候给它传递的参数
传值:形参(方法中的参数)的变化不会影响实参(局部变量)的变化。因为形参和实参不在同一个工作空间,形参在栈中的空间 本方法执行后就消失了。所有的方法工作空间(栈桢)在方法执行后就自动释放了。
没有特殊情况尽量不要定义静态变量和静态方法(单例除外),因为会参加维护成本
调用其他类时可以不导包,但是写类的全路径名 例如 java.lang.Math
JVM的输出机制:内部有个valueOf()方法,先判断对象是不是null,如果是就直接输出null,如果不是就调用toString()输出对象的值。
只有属性没有方法的对象叫贫血模型
只有方法没有属性的模型叫充血模型
在方法中,如果变量名冲突,会优先使用方法中的形参(栈中开辟的空间 局部变量)
主方法与实例方法用了同名变量,被调方法改变变量,main( )中变量值也变
在普通方法中创建对象,方法调用完内存释放后 对象会变成游离状态,容易造成内存泄漏(尽量不要在普通方法中创建对象)
方法内部中优先在自己的栈工作空间找变量,找不到再通过对象找
实例变量可以与局部变量同名,但是实例变量与实例变量不能同名
重载时根据参数签名来调用方法
普通类型传值时,形参改变,实参不变
引用类型,传值时,形参改变,实参也变
Class Test{
int ab=12;
public void setAb(int ab){//形参
ab=ab;//ab的值为形参的值
System.out.println(ab);//11
System.out.println(this.ab);//12
}
Main(){
Test t=new Test();
t.setAb(11);
}
}
数据的运算:
除法:
整形数据除以0,//算术逻辑异常,ArithmeticException(12/0,,0/0)
浮点型数据的运算:
浮点型数据除以0,或者整形数据除以0.0,结果为Infinity(1/0.0 3.0/0, 3.0/0.0 -1/0.0 )
当 0/0.0 : NaN (Not a Number)
取余: 被除数小于除数,结果为被除数 17%18 = 17
17%0 : ArithmeticException
17%0.0 1.3%0 -1.3%0 :NaN
17.6%4 整数部分照常取余,小数不变
17.6%4.2 不出错,单数结果不一样 17%4.6 也是一样
位运算:
能够参与 位运算的数据类型包括; int short long char byte
>>右移,相当于除以2
<<左移,相当于乘以2
Java常用的是循环右移
循环右移的位数为: int n ,a ; a>>n 当n>32 时 n=n%32; a 右移n%32位
Long n,a, a>>n 当 n>64时,n=n%64,a 右移 n%64位
字符串运算:
X 和y都是 int类型数据时
System.out.println(“”+x+y);//生成5对字符串对象
“” / “”+x / “”x / y / “”xy
Sytem.out.println(x+y+””);//生成3个字符串对象
Xy / “” / xy””
数据的比较:
在引用类型中 equals比较对象的类型再比较对象的值
在基本类型中直接比较值
==号用来比较数值
基本类型只能用==来比较
&&短路与运算:当逻辑表达式有一个值为假时,则不再计算后面的表达式,直接给出值
||短路或运算:当逻辑表达式有一个为真时,则不再执行后面的表达式,直接给出值
Java只给四个类复写了toString方法,(没复写的会调用==)
File Integer String Date
Integer对象在 -128~127之间会把对象创建在常量池中,相同的引用只创建一个,后来的先判断是否存在,存在的话直接调用,否则创建。
除了(-128~127)以外的Integer对象在堆中创建对象
Null不能调用 equals方法,否则会出现NullPointException
从表单中获取的数据不可能为null,只会出现“”空串和字符串
Switch可以接收的数据类型:int char byte short
在循环条件为真时,while 和 do while{} 执行的次数是一样的 ,当循环次数为假时,while 不执行 do while执行一次
非正常出口跳出循环:
break:跳出本层循环
continue:结束本次循环,开始下一次循环
Label标记:标识循环体
inner: for(){} break inner;//表示跳出label所标记的那层循环
标识符直接写在循环体上面,命名规则和变量名相同
int k =System.in.read();//从键盘缓冲区中读取一个字符
例: 输入 a 回车, 会读出三个字符 97 ‘\r’ ‘\n’(10)
continue label :结束label所标识的那层循环的本次循环,开始那层循环的下一次循环。
数组和变量:
变量的空间不连续,数组的空间是连续的
数组的空间叫元素名
变量寻址:自动创建变量名 自动映射表 直接寻址
数组元素寻址: 间接寻址(根据下标来寻址)
数组没有复写toString (),输出的是地址
数组:存放数据的容器,长度固定 类型一样
给数组对象赋值: String[] str={“1”,”2”};//1个变量,两个对象
数组的主要应用是排序: 插入排序 冒泡排序 堆排序 选择排序 快速排序
冒泡排序:两两比较,大的数据往后
选择排序:选择一个元素和其他元素比较,比它大的不变,小的交换位置
插入排序:第一个数确定位置 后来的数都和第一个比较 大的数位置不变,小的数放前面
Int[] a=new int[5];//1个变量,5个元素,每个元素为int类型
Int[][] b=new int[3][];//1个变量,1个int[][]对象 3个int[]对象,9个元素
使用new来创建多维数组的时候,只需要指明最左边的大小
Int[][][] a=new int[3][][];//3个元素,每个元素都是int[][]类型
多维数组就是数组当中的数组
类的继承:
父类的构造方法和私有属性和私有方法不会被子类继承
Java是单继承,多实现的类
继承关系中,创建子类对象时不会创建父类的对象,但是会调用父类的构造方法
调用子类的构造方法,父类中的构造方法中传递的是子类的地址
哈希地址: 前面是类型 +后面是地址
为什么创建子类对象要调用父类的构造方法?
因为子类需要调用父类中私有的属性和方法,需要父类的构造方法为之初始化并与父类建立动态绑定便于父类引用型变量调用
编译时的绑定为静态绑定
向上转型:父类引用型变量指向子类,父类引用型变量只能看到子类继承的和覆盖的方法,看不到子类新增的方法和属性 只能通过静态绑定找到类,再通过类的动态绑定找到属性
创建子类对象的机制?
在堆里面为父类的私有属性和方法开辟空间和父类进行动态绑定
在堆里为子类的方法和属性开辟空间(静态绑定父类的和子类新增的)
重载:方法的重载是在同一个类中,参数签名不同(对返回值类型访问权限没有要求)
抽象方法可以被重载,main方法可以被重载,static方法可以重载为非静态方法
JVM在编译的时候就已经知道该调用哪个方法,这叫静态多态,编译多态
覆盖:是指在两个类中,有继承关系,子类继承了父类的方法才能覆盖,并且子类对父类方法的覆盖只能有一次
覆盖准则:出现在两个有继承关系的类中,方法名和参数签名必须相同,返回值类型相同,子类的访问权限不能比父类的访问权限更严格,一定要大于父类的访问权限
Java中子类对父类的实例方法的重写叫覆盖,对是static 方法的重写和对属性的重写叫隐藏
造型:一定是两个类,并且存在继承关系,要有向上转型(造型的效果只是临时性的,只是把变量的值改为理想型)
访问权限的控制: private(本类) default(包) protected(子类) public(全部)
关联和绑定:
关联:与静态方法静态属性的关联 没有绑定this指针
静态关联:编译时类和方法的关联
动态关联:加载时 类与静态全局变量的关联
绑定: 与构造方法实例方法的绑定有this指针
动态绑定:与私有属性和私方法的绑定
继承关系中: 先在堆中为父类的私有属性和方法开辟空间 与父类进行动态绑定,父类中的实例方法与父类静态绑定,覆盖后的方法和子类静态绑定。
创建对象
创建对象时会先调用父类的构造方法在调用子类的构造方法,在父类里面定义的方法被子类调用时会看不到子类中新增的方法。子类引用型变量调用继承父类中的方法时,无法找到子类中新增的同名方法,所以会先调用父类的方法
Super:是用来找到和父类动态绑定的私有属性和方法
Super的特点:
是父类的一种特征。
只能使用在实例方法中
不能用在static(静态)方法中
通过super找到父类的绑定的方法,在通过父类的动态绑定找到父类的私有属性
Super:在构造方法中的作用是显示的调用父类的哪个构造方法,出现在指定构造方法的第一条语句,只能有一条 super(); 参数填在括号中,调用父类无参构造方法时,就不写参数。 默认调用父类无参的构造方法。
普通类的权限只有默认的和public ,内部类什么权限都有
继承关系中,父类不提供可供子类访问的构造方法时,子类对象无法创建(创建子类对象一定有调用父类的构造方法)
可以在子类的有参构造方法中,通过使用super调用父类的有参数的构造方法
this:代表其所在类的当前对象的引用
this在类中构造方法中的作用:指定调用本类的某个构造方法
没有继承关系时,创建对象也可能调用多个构造方法 this指定
this和super不能出现在同一个构造方法中
初始化块:作用和构造方法相似,给类的变量赋初值 (仅给定义在初始化块之前的变量初始化) 比构造方法调用的早 会传递this指针
创建对象的执行顺序:
1、 先给类中的静态属性开辟空间
2、 再给属性和实例方法开辟空间
3、 调用初始化块为之初始化
4、 然后再调用构造方法进行初始化
创建几次对象就会调用几次初始化块语句
WapperClass//封装类:
封装类 : Boolean Character的父类是Object //没有valueOf()方法
Integer Byte Short Long Float Double BigDecimal 的父类是Number 都可以调用valueOf()
封装类都是final类型,不能被继承
封装类的构造方法 :
以基本类型数据为参数的构造方法
以基本类型数据字符串的形式构造方法
Integer i=new Integer(10); 等价于 Integer i=new Ingeter(“10”);
int ii=i.valueof();等价于 int ii=Integer.valueOf(i);
封装类复写了toString()方法和 equals()方法
toString() 表示返回这个对象的字符串,每个类都有toString()方法
封装类 异常 集合 date 网络复写了toString()
Object 的toString() ,
this.getClass().getName+“@”+Integer.toHexString(hashCode());
得到对象的地址的方法,Integer.toHexString(hashCode());
实际开发中复写的tostring()
Public String toString(){
return this.getclass()+”[name”+name+”age”+age+”]”;
}
在调用toString方法时,会先调用valueOf 方法,判断对象是否为null,为null的时候直接输出null,不为null的时候才会调用toString()
Object中的valueOf( ):
Public static String valueOf(Object obj){
return (obj==null)?”null”:obj.toString();
}
任何类都有equals方法,object 中的equals方法直接调用==
自己复写equals方法:
1判断被比较的对象是否为null
2 判断对象是否相等==
3 判断类型是否一致 p.getClass()
4 判断类的属性是否相等 需要对对象进行造型
Public boolean equals(Object obj){
if(obj==null) return false;
if(obj==this) return true;
if(this.getClass()==obj.getClass()){
Person p=(Person)obj;
if(p.getName().equals(this.getName()){//String复写了equals方法
return true;
}
}
}
Static和单例设计模式
同一个类只有一个反射对象
Static用来修饰类变量 语句块内部类 static不能用来修饰构造器
构造方法不能对Static变量进行初始化,只能由static语句块为静态变量初始化,并且在类中只加载一次
编译的时候不能创建对象,反射对象是加载类的时候创建的
静态方法不属于对象,创建对象时不会为静态方法开辟空间
静态初始化快中没有this指针,静态初始化块在main()之前执行
静态初始化块和初始化块的区别:
1、语法不同,静态的有static{}
2、 调用机制不同,静态加载类时调用
3、 执行次数不同 静态的只执行一次,
相同点:都是为属性初始化
静态用来实现单例,
单例模式的特点:共享数据线程同步项目安全降低并发性,增加耦合性
饥汉式单例:一加载就创建对象,只创建一个对象
Public class Singleton{
Private Singleton(){}//构造方法私有化,让外部只能通过类创建对象
Public static Singleton instance=new Singleton();
Public static Singleton getInstance(){
return instance;
}
}
饱汉式单例:第一次使用时 先判断对象是否存在,不存在时在创建,只创建一个对象
Public class Singleton{
Private Singleton(){}//构造方法私有化,让外部只能通过类创建对象
Public static Singleton instance=null;
Public static Singleton getInstance(){
If(instance==null){
instance=new Singleton();
}
return instance;
}
}
推荐使用饱汉式单例模式效率高
final:
1、final 定义的方法可以被继承,但是不能被覆盖
2、final修饰变量只能初始化一次,不能赋值
final static int a =20;//a的值不能再修改了
3、final实例全局变量也只能初始化不能赋值
4、引用类型final定义的实例全局变量不能改变对象,但是可以改变对象的引用 a.name
5、final局部变量如果没有初始化,只能赋值一次
6、方法的形参可以是final类型的,只能初始化一次
7、方法内部类中只能使用final类型的形参
final类不能被继承,final修饰的属性还是变量不是常量,常量没有初始化,常量不能被赋值
Abstract类中会有构造方法,子类继承abstract类时会调用父类的构造方法创建对象,但是不会创建父类对象
接口:
Interface中没有构造方法,接口中不能有静态初始化快语句
内部类可以保证线程的安全,内部类分为方法内部类也叫局部内部类,实例内部类也叫全局内部类
一个接口可以继承多个接口,但是一个抽象类只能继承一个类
类与接口的关系叫实现
多态是在执行时才知道的,父类引用型变量调用了子类的那个对象
重写是编译时知道的
接口中不能有初始化块和构造方法,接口中定义的变量都是 public final static 类型的
预运算符 : 类名:方法(); 指定使用哪个类中的那个方法
接口的特点:
1、编译时会生成字节码文件
2、接口中的新增方法也要通过造型来调用,//接口回调
3、接口是一种功能规范,更注重抽象方法,不注重属性
4、接口的功能:降低耦合性,提高可维护性
内部类:
内部类和外层封装它的类之间存在逻辑上的从属关系
内部类可以访问外部类的私有属性
外部类 outer 内部类inner 编译后 outer$inner.class
拥有内部类的外部类对象创建时不要考虑内部类,和正常类一样创建
但是内部类的创建需要考虑外部类
Outer oute=new Outer();
Outer.Inner inner= outer.new Inner();
在外部类的方法中可以自动识别内部类,内部类对象直接拥有对外部类对象的引用
内部类私有时,外部测试类中不能创建内部类对象
外部类方法中可以创建内部类对象,使用内部类
匿名内部类:私有内部类省略名字,外部无法使用
局部内部类:定义在方法中的类 作用空间只在方法中
方法内部类对象不能使用方法内部非final类型的变量
内部类可以声明为抽象类被其它内部类继承,也可以声明为final类型的
Exception:保证程序的健壮性,先处理问题,应对用户需求的变化
Error:错误,是解决不了的问题
RuntimeException:程序本身的异常,还未编译,运行时异常
IOException : 外部资源问题,编译时异常
finally:用于异常处理时关闭资源,finally中最好不要有return语句。
异常处理机制中,有返回值栈,返回值压栈,只返回最后一个return值
Throwable包括Error 和Exception
实际开发中一个try{}可以配多个catch() 最后catch的是 Exception
父类和子类的异常关系,继承后 子类不能比父类声明更多的不处理的异常
子类需要处理的异常一定要比父类多
Throws :表示不处理此类异常,在方法后面申明,程序没有编译错误(throws IOException)
Throw :出现在方法内部,后面只能跟一个异常对象,把异常对象抛出(throw new RuntimeException(e))
Throw可以由系统来写,系统自动检测程序,也可以自己定义异常
方法使用了throws表示把异常 抛给了调用它的方法的对象
系统先在catch中找到throw声明的异常,没找到再去throws中找不处理的异常
Message :由自定义异常类的有参构造方法设值
e.getMessage();公有的获取异常信息的方法
e.toString();显示异常对象的类型,显示message()信息
e.printStrackTrace();调用toString() 找到异常出现的类方法//最详细的显示异常信息
自定义异常类: 1 继承Exception类 2 提供一个无参的和有参的构造方法 super(msg)
不在业务层和持久层处理异常,交给表示层处理异常,业务层直接抛出异常
异常栈的跟踪:从栈顶开始跟踪,一直到异常处理
异常会影响系统的效率,发生异常的位置和捕获异常的位置越近越好
处理异常的原则:
1 解决运行时异常,设定编译异常
2对方法的异常做声明 throws
3捕获异常把异常代码放在try{}中,try中的代码量不能过多
4 catch一定要尽量和try中的代码一一对应,多个catch对应一个try
5 捕获了异常要处理异常
6 有资源存取时,finally中要有关闭资源的,finally中不要有return语句
命令行参数 在cmd 命令中 编译完A.java 后 在解释时 输入 java A 1 2 3 输入参数
1 2 3 会存入String[] args 数组中
System.exit(1);//手动退出系统
String --.> int :
int age=Integer.parseInt(params);
Java是先编译后解释的语言
标准流: System.out 和System.err 都是PrintStream 对象
System.in 是InputStream对象
System 继承了PrintStream 和 InputStream的父类 (标准流类)
字节流:一次只读取一个字节
字符流:一次读取两个字节,读中文时不会出错,读英文时会自动扩充
Data流和Object流比较重要
Properties其实就是map 以键值对的形式存储数据
一般的key 和 value是Object 类型,Properties的key和value是String类型
创建properties对象 :
Properties props=new Properties();
往properties对象中设定属性:
props.setProperty(“name”,”zhangsan”);
从properties对象中取值
props.getProperty(name);
Properties文件中的key和value课可以用 等号 空格 冒号 分隔开
Properties的对象,内存的存储空间
Properties文件:外存的存储文件
.\\ 表示相对路径
InputStrean in=new FileInputStream(“a.properties”);
props.load(in);//读取配置文件的信息
FileOutputStream fos=new FileOutputStream(“b.properties”);
props.store(fos,name);//将props对象中的数据存储到b.properties文件中 name 为对b.properties文件的说明
Key 不能重复,value可以重复
Java中的系统属性是java的环境变量
Properties props=System.getProperties();//会创建properties对象, 把环境变量中的内容设到props中
Enumeration 枚举类型的迭代
Props.propertyNames();///获取所有的属性名
HasMoreElement();
nexElement();
复习:
Java的起源 94年出现 为网路开发而生。 java的出现推动了B/S模式,面向切面编程理念
Java的应用 –网络应用,应用广泛。
IOS Andriod 不是很流行
Java技术体系架构: J2EE 企业级应用开发
Java的优点:简单,面向对象,健壮,多线程,结构中立跨平台,安全,一次编写到处运行
一次编写到处运行:因为有虚拟机,先编译后运行
垃圾回收机制:GC,对内存的使用有很大的提高,但是没有解决内存泄漏问题(瞬时态和离线态进行回收) 降低了效率 ,是一个后台线程 占用了CPU效率
Applet:字节码文件嵌入到html中,通过浏览器解析执行,浏览器自带虚拟机
程序设计简史:
结构化程序设计:一次性把所有需求找完,类似瀑布模型
面向对象程序设计:以对象为核心,对象是指事务在问题领域中的抽象,核心问题是如何找对象。
面向对象的四大特点:隐藏(private),封装(set/get),多态(父类的方法在子类中的不同实现),继承
领域模型:通过类抽象出类和类之间的关系
类中叫属性名,对象中叫属性值
构造方法:由虚拟机系统调用,可以被重载,创建一个对象可能会调用多个构造方法,编译时知道调用哪个构造方法
信息的封装和隐藏:给属性加private,外部提供get、set方法 保证信息的安全性和完整性。
Java源文件的结构:
包申明(package abc):字节码文件必须方法当前包目录下才能使用,体现了分包管理的原则
包导入(import abc.*;):找到指定的字节码文件的位置
Strust1.0 :编码效率低,执行效率高 SpringMVC
Java的注释:是否可以被收集 javadoc 可以收集文档注释 /** */
标识符:不能定义为关键字和标准类库中的类名
Java数据类型 8中基本类型和引用类型
引用类型存放的是对象的地址,不一定在栈中开辟空间,可能在对堆中 或者是方法区中(static在方法区中)
传递方法:只有一种值传递
双目运算符会改变运算量的类型,单目运算不会改变
Set和Map一定要复写equals方法
只有Integer String Date File复写了equals方法
表单中拿到的字符串不会是null(request.getParameter(“name”))
位运算适合的运算类型:int long byte char short
Switch中的数据类型只能是 int byte short char
数组是个容器,是通过类来创建的对象,长度固定
数组存放的数据类型必须一致,查找速度快,插入删除比较慢
空数组没有意义,空集合有意义
链表:插入和删除比较快,查找速度慢
数组是length属性,集合有size(), 字符串有length()
List集合是动态加长的集合数组
继承的特点:单一继承,私有属性和私有方法不能被继承
创建子类对象不会创建父类对象,但是会为父类的属性开辟空间
重载:在一个类中,多个方法名相同,参数签名不同的方法。编译时识别
主方法和静态方法和抽象方法都能被重载
Java对属性和静态方法的重写叫做隐藏
Java对父类的方法重写叫做覆盖
重写的特点:在两个有继承关系的类,方法名相同参数签名返回值类型相同,访问权限要大,抛出的异常要少(处理的异常一定要多于父类) 抽象方法必须要重写,静态方法可以重写 叫隐藏。
This:当前对象的地址, new对象的时候创建的,(先有this,才调用构造方法),this所指定的属性和方法一定在堆中。
Super:父类的特征,父类绑定的实例属性和实例方法,只能在实例方法中调用。在构造方法中显示指定调用哪个方法。 Super() 一定在子类的构造方法中的第一句
调用super一定要有this
静态方法中没有this,
初始化快:在构造方法前对在定义初始化块以前的实例全局变量进行初始化,放到最后。创建几次对象调用几次初始化块
静态初始化块:只调用一次,比main方法还早调用
this super 初始化块:
第一步:压栈
第二步:为父类的属性初始化
第三步: 为子类的属性初始化
第四步:调用父类的初始化块初始化
第五步:调用子类的初始化块初始化
第六步:弹出子类的构造方法”
封装类:是一种类型,基本类型的首字母大写 char Character ---- int Integer
封装类的数据结构:都是final类型,复写了toString() 复写了equals()
封装类的对象的创建不能用无参的构造方法,只能有参的
所有的类有都有equals方法,跟==等值比较一样去比较
Static 可以通过类调用,也可以通过类中的任意对象调用
静态属性随意使用会增加耦合性 ,静态方法不能传递this指针,在静态方法中也不能使用super。
静态内部类不能访问外部类的非静态属性
构造方法不能静态(因为没有this)
单例:Singleton (饿汉式)
静态全局变量,private static Student instance=new Student();
构造方法私有化 private Student(){}
静态的公有的public static Student getInstance(){ return instance;}
Singleton (饱汉式)
静态全局变量,private static Student instance=null;
构造方法私有化 private Student(){}
静态的公有的public static Student getInstance(){
if(instance==null){
instance=new Student();
} return instance;
}
缺点:耦合性高,效率低
优点: 安全性高
final: final修饰的类不能被继承,final修饰的变量只能初始化一次。数组类型的引用类型为final类型,数组对象的类型不能改变,final类型的变量不是常量。 方法内部类的形参只能是final类型的
抽象:方法无法实现时,先声明定义为抽象方法。(例:求某个图形的面积和周长)
空方法不算抽象方法,它有空实现。
适配器的特点:把不同的对象进行统一的处理
多态:动态编译,在接口上经常使用多态
模板模式:继承
接口:接口中的所有属性是 public static final 类型的 ,一加载接口字节码的时候就初始化,在方法区开辟空间
接口中的所有方法是public abstract类型的
接口不是类,更不是特殊的抽象类
接口语法: interface定义,
一个子类只可以extends 一个父类,抽象类有构造方法,但是不能创建对象,接口没有构造方法 所以接口不是特殊的抽象类
接口的回调:用接口的引用型变量 接收 实现接口的类的对象
例如:DaoInterface daoInterface=new Daompl();
接口是功能规范,便于维护,降低耦合性
多个无关的类可以实现同一个接口
一个类可以实现多个无关的接口
instanceOf造型的时候使用,判断对象类型是否符合造型要求
内部类:一个类定义在一个类的内部,分为实例内部类和方法内部类,不能多用,
内部类和外部类的关系是组成关系。
实例内部类的特点:
命名空间只在外部类中的方法可以识别,如果外部类外部的方法需要使用的话 ,需要使用调用外部类.内部类来使用
类中的内部类可以访问外部类的私有属性
创建内部类对象,必须先有外部类对象
内部类会自动拥有外部类对象的引用
私有内部类只能在包含此内部类的外部类实例方法中被创建,私有内部类省略名称成为匿名内部类。
局部内部类使用的参数是final类型,也会自动拥有对外部类对象的引用
Inner class 可以声明为 static private ,如果是静态内部类就不能再访问外部类的非静态属性。
异常:
使用异常目的:
1,由于资源原因造成程序出错,预先用异常进行处理(IOException)编译检查此类异常
2,对于运行时异常,通过异常机制找到异常原因并且给予解决
持久层不处理异常,业务层不处理异常,一般都在测试层处理异常
异常的种类:
Error:AWTError / LinkageError
Throwabel
Exception:IOException/……/RuntimeException
异常的数据结构:
e.getMessage();公有的获取异常信息的方法
e.toString();调用e.getMessage(),并且显示异常对象的类型
e.PrintStrackTrace();异常栈跟踪,调用e.toString(),并且输出发生异常的代码位置
一个try{}后面可以接多个catch()成正金字塔形式,最后catch (Exception e)
拦截异常后要进行处理
假若finally 中有 return:
返回值栈,会覆盖正常流程中的return值
会隐藏异常流程中的异常结果,还会隐藏异常
finally中一般最好不要有return值
自定义异常:
自定义异常类继承Exception,定义两个构造方法,一个无参的,一个有参数的构造方法。
public MyException(String msg){ super(msg); }
Property在java util.*;包下面
Property 中用空格,冒号区分 name 和value;
用setProperty()方法修改property对象中的值
用getProperty()方法获取property对象中的值
load();//用于加载property文件
store();//用于保存修改后的property文件
明日讲课内容:
Math
String
Collection
文件过滤
1.3版本:
使用线程同步,同一时刻只能使用一个线程
采用工厂模式完成业务层和持久层操作
业务层和持久层之间加了工厂
类锁的机制(静态锁):用反射对象的锁
业务层:设计单例时也加上静态锁。
1.4版本
awt 界面
Math和字符串
1 Math类
1.1 final类型,构造方法私有。所有使用的方法都是静态的,由类调用。
1.2 提供两个static final 类型的实例全局量 PI(圆周率)和E(自然对数)
1.3 方法
1.3.1 ceil (返回大于或等于参数的最小整数)
1.3.2 floor (返回小于或等于参数的最大整数)
1.3.3 round(返回四舍五入的整数值)
1.3.4 变量的取值:max,min,abs(绝对值)
1.3.5 对数指数:log,exp
1.3.6 平方根 sqrt,幂运算pow,random 随机数,
1.3.7 常量 PI,E
1.3.8 sin cos acos atan tan asin (里面的参数是弧度,double类型的数值),toDegrees():转化为角度 toRadians();//转换为弧度
2 字符串类
2.1 String类,final类型,不能被继承,实现了序列化接口,比较器的接口,排序接口,可以进行存储,比较,排序
2.2 String内部是final类型的字符数组对象,一旦创建不能被修改,只能创建新的字符串对象
2.3 String对象代表一组不可改变的Unicode字符序列 private final char value[],任何试图改变string对象的方法都会创建一个新的字符串对象
2.4 String对象的创建
2.4.1 String str1=”abc”;//在方法区的常量池,可以共享
2.4.2 通过new String对象
2.4.2.1 String str=new String(“abc”);//会创建两个对象,一个在方法区的常量池,一个在堆里面
2.4.2.2 String(char[] value);//按字符数组创建字符串对象
2.4.2.3 String(byte[] value);//按字节数组创建字符串对象
2.4.2.4 String(byte[] value,String charsetName);//根据charsetName指定的字符编码,按字节数组参数创建字符串对象
2.5 String类的方法
2.5.1 concat:字符串的附加,类似 连接+,新串在方法区中
2.5.2 s1+=s2;//s1,s2都存在的情况下,创建两个字符串对象,一个在堆中,一个在方法区
2.5.3 replace ,repalcaeAll:字符串的替换
2.4.2.1 str.replaceAll(“hello”,”dfx”);//把所有的hello换成dfx
2.5.4 subString:求子串
2.3.4.1 subString(1,9);//取第一个位置包括第一个,到第9-1个位置的子串
2.3.4.2 subString(1);//取从第一个位置开始以后的所有字符
2.3.5 toLowerCase:转换为小写
2.3.6 toUpperCase:转换为大写
2.4 查找字符的方法
2.4.4 endsWith:尾部匹配
2.4.4.1 str.endsWith(“.java”);//判断末尾的字符串是否匹配 匹配返回true
2.4.5 startsWith:头部匹配//返回boolean类型
2.4.6 indexOf:找出索引位置
2.4.6.1: str.indexOf(“a”,2);//从第二字符起,a首次出现的位置
2.4.7 lastIndexOf:
2.4.7.1:str.lastIndexOf(“a”,2);//从第二个位置开始反向找a首次出现的位置(反向搜索)
2.4.7.1 str.lastIndexOf(“a”);//从第0个位置开始反向搜索,没有 返回-1
2.4.8 split :分隔
2.4.8.1 str.split(“,”);按逗号分割后用String[] 接收,分隔为多个字符串
2.4.9 trim:首位空格删除
2.4.10 charAt;找到字符串出现的位置
2.4.11 String方法的实例
String str=”123”; String str1=”123”; //生成一个对象,在方法区的常量池中创建
String str2=new String(“aa”);//生成两个String对象,堆中一个方法区一个
String str3=new String(“vv”); String str4=new String(“vv”);//创建三个 两个在堆中,一个在方法区的常量池中
str+=str1;//创建一个新的字符串对象,连接后的新字符串在堆中开辟空间
String s=new String(“hello”);
String s1=”world”;
s+=s1;
System.out.println(s==”helloWorld”);//结果为false
用追加(+)或者是连接(contact)创建的新串,在常量池中有一份,在堆中也有一份。比较的是堆中的那个对象。
2.4.12 StringBuffer:
构造方法 public StringBuffer(){super(16);}//自动增加16个缓冲空间
Stringbuffer具有一定的缓存,缓存容量是可以增加的
StringBuilder是线程安全的,使用缓存时不会创建新的对象
Stringbuffer和 String比较:
都用来处理字符串
都提供 了length() charAt() subString()
StringBuffer类对象可变,改变其缓冲区不会创建新的对象
StringBuffer没有复写equals方法,不支持+运算
stringBuffer覆盖了toString(),但是 和String的方法实现不一样
String中追加一定会创建新的String对象,StringBuffer中追加不会创建新的对象,追加以后的对象就是原来被追加的对象。
StringBuffer和String不是同一种类型,不能比较。
StringBuffer 使用toString()方法后,会创建String类型的对象,把stringBuffer里面的字符串赋值给String类型的对象,返回新的String类型对象的地址。新的String对象再调用string类型的toString()显示输出字符串。
String和StringBuffer的使用场合:
StringBuffer sb=new StringBuffer(“as”);//创建两个对象
StringBuffer更节省内存空间
字符串经常变换的场合用StringBuffer
StringBuffer的容量为20,如果追加200的数据,也不会创建新的StringBuffer对象,只会扩大缓存空间,不会创建新的对象。
如何优化StringBuffer?(如何优化字符串管理?)
预先估算缓存容量,提高stringBuffer的执行效率。String 和 StringBuffer内部都是char[] 数组,最大的缺点是长度固定。
2.4.13 StringBuilder:
StringBuffer和StringBuilder比较
都具有缓存功能
StringBuffer线程安全,一个线程使用不会影响别的线程使用信息
StringBuilder线程不安全(可以人为加锁,保证安全 )
文件搜索
1 文件操作
File 在 java.io.*;下面
创建文件对象:
File file=new File(“a.txt”);//在相对路劲下创建文件
File file1=new File(“Mydos”,”a.txt”);//指定文件所在的目录
2 Deprecation
javac –deprecation A.java;//用来编译过期的文件类 表示已经过期了 发出警告
File 类的方法
getName();//获取文件或者目录的名字
getPath();//获取文件或者目录的路径
getAbsolutePath();//获取文件或者目录的绝对路径
getParent();//获取文件或者目录的根路径
boolean renameTo(File newName);//改名
boolean exist();//是否存在
boolean canWrite();//是否可写
boolean isDireactory();//是否是目录
String[] list();//把路径下的所有文件遍历输出
File currDir=new File(“f://abc”);//找到指定目录
if(currDir.isDirecory){//判断是否为目录
String[] files= currDir.list();//将目录用String[] 对象接收
}
for(String file1:files){//循环输出所有目录
System.out.println(“file:”+file1);
}
文件的过滤:JavaFilter implements FilenameFilter接口,需要实现accpet()方法。
public boolean accpet(File dir,String name){
File f=new File(dir,name);
if(f.isDirectory()){
return true;
}else {
retrun name.endsWith(“.java”);
}
}
String[] files= currDir.list(new JavaFilter());//将通过过滤的目录用String[] 对象接收
自动调用accept()的叫回调
accept的实现叫搜索策略
递归搜索:(思想)
先按顺序搜索,找到目录时,直接进去目录下搜索,再按顺序进行搜索,如果目录中存在目录则依旧再次进入目录搜索。以此类推,最后一个目录结构搜索结束后再处理别的文件依次从最后一层倒过来搜索。(搜索到目录时就进入目录搜索知道没有目录了再跳出来一次搜索)
作业:1.手工实现回调和策略搜索
2.多目录文件查找
作业1:
import java.io.File;
/**
* 手工实现一级文件搜索
* 找出所有的前缀名为String的文件
* @author DFX 2017/07/12
* */
public class FileSearchOne {
File currDir=new File("F:\\Users\\javaBase\\src\\string");//创建目录文件夹
public static void main(String[] args) {
FileSearchOne fso=new FileSearchOne();//创建当前对象
fso.fileList();//调用列出符合过滤条件的文件或目录名的方法
}
public void fileList(){//列出文件的方法
if(currDir.isDirectory()){//判断当前文件是否为目录
String[] javaFiles=currDir.list(new JavaFilter());//用字符串接收当前符合过滤条件的文件
for(int i=0;i System.out.println(javaFiles[i]);//输出文件或者目录名称 } } } } import java.io.File; import java.io.FilenameFilter; /** * 文件过滤类 * 实现FilenameFilter接口,覆盖accpet()方法 * @author DFX 2017/07/12 * */ public class JavaFilter implements FilenameFilter{ public JavaFilter(){} @Override public boolean accept(File dir, String name) {//dir:指文件目录,name:文件名 File f=new File(dir,name); if(f.isDirectory()){//判断是否为目录结构 return true;//为目录结构时返回目录 }else{ return name.startsWith("String");//返回以String开头的文件 } } } 作业二: import java.io.File; /*** * 递归搜索指定过滤文件 * @Author DFX 2017/07/12 * * */ public class FileSearchMore { /** * @param args */ public static void main(String[] args) { FileSearchMore fsc=new FileSearchMore(); File dir=new File("F:\\Users"); fsc.showDir(dir); } //函数调用自己 称为递归 public void showDir(File currDir){ File[] files=currDir.listFiles(new JavaFilterMore());//列出当前目录下的所有文件 for(int x=0;x if(files[x].isDirectory()){ showDir(files[x]);//是目录的时候就递归, }else{ System.out.println(files[x]);//不是目录就不用递归,直接输出 } } } } import java.io.File; import java.io.FilenameFilter; /** * 多个层件目录搜索过滤类 * @author DFX 2017/07/12 * * */ public class JavaFilterMore implements FilenameFilter { @Override public boolean accept(File dir, String name) { File f=new File(dir,name); if(f.isDirectory()){ return true; }else{ return name.endsWith(".java");//返回以.txt结尾的文件 } } } 集合和数组: 相同点:都是存放数据的容器 不同点: 1, 数组通过类创建数组对象,集合通过接口实现类创建对象(更好维护) 2,一旦数组创建对象了长度固定,集合长度可变(空数组无意义,空集合有意义) 3,数组可以放基本类型的数据也可以放引用类型的数据,集合只能存放对象的地址(引用类型的数据) 4,数组定义了元素类型必须保持一致,集合引用类型的数据可以不一样(集合存在造型问题) 5,原则上数组存放的元素是有序可重复的,集合除了(List)都是无序不可重复存储的 6,数组通过下标取得元素,集合一般通过迭代取元素(list可以不通过迭代) 7,数组没有复写toString(),集合复写了toString();(集合还复写了equals()和hashCode()) 8, 数组的长度用length(),集合的长度用size(); 9,集合的空间是可以删除的(remove),数组只能删掉某个空间的元素 10,原则上数组类是不能被复写的,但是集合可以复写(Hibernate复写了set集合增加了 懒加载功能) Collections 存储数据的容器:集合数组对象变量 1 Collection:都有add和remove方法 都用Iterator迭代元素 1.1 Set 无序,不可重复,复写了equals hashCode toString,遍历用Iterator,也可以使用增强for输出集合中的元素 1.1.1 hashSet:散列表 通过一维数组+单链表 1.2 List有序:是一个一维数组,有序可重复,可以通过下标遍历也可以通过Iterator遍历 1.2.1 ArrayList:线程不安全,高效 1.2.2 Vector:线程安全,效率低 1.2.2.1:Strack 2 Map:键值对存储,没有继承collection 没有Iterator方法,输出map中的元素对象需要借助于List ,Set 集合。 Key 和 Value 加在一起叫 Entry 2.1 HashMap:遍历HashMap中的所有元素, 用KeySet 把Map中的Key放入set集合,还有一种EntrySet, 2.2 HashTable 2.2.1 Properties:Key 和 Value为String类型 3 Iterator:线程不安全,效率高,都有自身的迭代功能 hasNext() next(),作用:方便的遍历list set集合中的元素 4 Enumeration:线程安全,效率低 5 使用集合一定要导入import java.util.*;包 集合一般通过接口实现类来创建 List 有序可重复 List list=new ArrayList();//list集合内部是一个可变长的一维数组 数组变长是指在创建一个新的集合数组 往集合中添加数据 list.add(1); //自动装箱和拆箱 Integer 类型 list.add(“as”);//String类型 list.add(new Integer(1));//Integer类型 list.add(newe Float(5.0f));//Float类型 集合复写了equals(),比较时 调用equals方法, list.size();//集合长度 list.remove(“as”);//移除集合中的 as 元素 System.out.println(list);// 复写了toString() 输出集合中的所有元素,输出顺序和添加时一致 Set 无序不重复 Set set=new HashSet(); set.size();//集合长度 set.add(“one”);//String 类型 set.add(4);//自动装箱 Integer set.add(new Integer(4));//Integer Iterator it=set.iterator();//Iterator对象不能通过new来创建,只能通过方法创建。 While(it.hasNext()){ //Iterator自身迭代输出set集合中的元素输出的元素类型为Object类型,输出的时候会自动调用toString方法,需要使用的时候需要人为造型 System.out.println(it.next()); } Set集合中的元素不能通过下标来取元素,因为它是无序的,只能通过遍历取元素 Iterator是一个集合,不是迭代器,线程不安全效率高 Enumeration 是集合,线程安全效率低 Set集合的数据结构:一个线性数组+一个链表 Set集合的存储特点?(先hashCode再 equals) 往set集合中add元素:先通过hashCode()方法计算一维数组下标位置(哈希值),此位置不存在数值的话则直接添加,位置已经有元素了就把已存在的元素和新元素做equals比较,equals比较相同则用新的替换旧的,不同则通过链表找到下一位置放入元素。每个一维数组中的桶的哈希值是一样的,但是key不同。 容量为7,桶的个数相加 查找元素时,先做HashCode()比较,相同再做equals比较。 桶 equals比较相同,hashCode一定相同。 Size=2,一个桶(位置)中存储两个元素,hashCode值相同,equals不等 一维数组每个元素的位置叫做桶,桶中存放链表 元素的个数叫做容量 7 Size是指每个位置上实际元素的个数 什么叫容量?所有的一维数组的个数叫容量(默认容量 16),数组长度为16 什么叫负载因子?size除以容量叫负载因子(默认 0.75)负载因子=size/capacity,负载因子越小,碰撞率越低,查找和删除效率越高。 Map 无序不可重复(key),Map的key 和value都是Object类型,除了Properties中的key 和 value是String 类型 Map map=new HashMap(); map.put(“1”,”mon”);//往map中设数值,分别是key和 value, key不能重复,重复时会覆盖,后面的那个value被保存 map.get(“1”);//取出map集合中key为1 的value值 System.out.println(map);//复写了toString(),把key 和value用等号连接输出 [1=mon] map.remove(“1”);//移除key为1 的元素 遍历map中的元素 方法1: Set set=map.keySet();//创建set集合,把map中的key设置到set集合中 Iterator it=set.iterator();//把set集合中存放的key放到iterator中进行迭代 While(it.hasNext()){ String key=(String)it.next();//得到key(一般是Object类型,所以需要转型) String value=(String)map.get(key);//得到value } 方法2: Set set1=map.entrySet(); Iterator it1=set1.iterator();//把set集合中存放的key放到iterator中进行迭代 While(it1.hasNext()){ Map.Entry entry=(Map.Entry)it.next();//得到entry entry.getKey();//通过entry得到key entry.getValue();//通过entry得到value } Map的数据结构是key 和value的键值对 作业:通过map实现饱汉式单例,通过key找到类对象 import java.util.HashMap; import java.util.Map; /** * Map实现 单例模式 * 比饱汉模式和饥汉模式更直接 * */ public class SingleTonMap { public static Map map=new HashMap();//创建map集合 private SingleTonMap(){} public static synchronized SingleTonMap getInstance(String key){//通过map集合的可以得到value ---单例对象 SingleTonMap value=(SingleTonMap)map.get(key); if(value==null){//判断value是否为null value=new SingleTonMap();//给value赋值 map.put(key, value);//将key和value设置到map中 } return (SingleTonMap)map.get(key);//得到value 单例对象 } } 测试类: public class TestSingleTonMap { /** * 主测试类 * @param args */ public static void main(String[] args) { SingleTonMap stm=SingleTonMap.getInstance("new SingleTonMap()"); SingleTonMap stm1=SingleTonMap.getInstance("new SingleTonMap()"); System.out.println(stm+"---"+stm1);//返回对象的地址 System.out.println(stm==stm1);//true } } 集合的扩充: Collection 是一个接口,有几个子接口 list set List 接口底下的类: ArrayList:底层是数组,查找很快,删除和修改慢 LinkedList:内部是双向循环链表,既有前驱也有后继,插入删除特别快,查找比较慢 Vector:底层和arrayList一样,效率低,线程安全 Set接口底下的类 HashSet :数组加链表 SortedSet:实现了Comparable接口 ,无序不可以重复,但是可以按照key进行排序 TreeSet是sortedSet的子类 Map: HashMap 的查找和删除都依赖于hashCode 和 equals HashTable:数据结构和hashMap类似 散列表(哈希表),线程安全,效率低 Properties:数据结构和map一样,散列表。Key 和value都是String类型 程序:一段静态代码,程序执行的蓝本(代码,代码不占用资源,资源是指内存) 进程:程序的一次动态执行过程,从代码加载,执行到执行完毕的一个完整过程,作为蓝本的程序可以被多次加载到系统的不同的内存区域分别执行,分为不同的进程。同一个程序它的进程不一样。(进程需要资源,但是不同的进程不会争用资源,进程可以同时进行,互不影响) 线程:进程内部单一的一个顺序控制流。一个进程执行过程中可以产生多个线程,每个线程也有自己的产生,存在和消失的过程。(一个进程的多个线程,线程之间争用资源和空间) 线程是为多个用户服务的独立线索。 线程类必须复写run()方法,JVM执行程序,主线程先创建, public class Machine extends Thread{ public void run(){ for(int i=0;i<10;i++){ System.out.println(currentThread().getName()+”--”+a); //currentThread()是指得到当前线程的地址(线程的引用) //currentThread().getName();指得到当前的线程名 } sleep();//静态方法 try{}catch(InterruptedException e){} } public static void main(String[] args){ Machine mc=new Machine();//创建一个线程对象 mc.setName(“m1”);//将线程命名为m1 mc.setPriority(“Thread.MAX_PRIORITY”); mc.start();//使得mc处于就绪状态,就绪状态的线程特点,已经被分配了指令计数器,已经分配了工作空间 --栈 mc.run();//主线程执行mc线程的run方法,m1线程的入口方法 } } 线程依赖于底层,调度策略不唯一。调度策略混乱,所以每次执行结果不唯一。 按线程分栈,每个线程对应一个栈(相互独立),每个栈中有多个方法。 处于死亡状态的线程特点:释放栈里面的工作空间,被GC回收 线程命名优先级和名字应该在线程就绪状态之前设定。 线程生命周期状态转换图: 线程的生命周期: 线程的创建状态: Jvm一启动就创建主线程 普通线程在主线程中创建(在堆里创建空间 new 线程对象) 主线程里创建的线程和主线程具有相同的优先级,激活了start,就会和主线程抢占资源。 Start是激活线程 线程的就绪状态: 主线程执行某个线程的start ,线程就被激活,可以和主线程抢占资源。就绪状态线程的特点:已经分配了资源处理机的指令计数器(PC),已经被分配了栈工作空间 线程的运行状态: 虚拟机从就绪状态中选择一个线程占用处理机,这种选择就绪线程占用处理机的方式叫做调度策略。run()方法是除了主线程以外,其它线程的入口。特点:占用CPU执行程序。 线程的阻塞状态: 等待CPU,还不能释放资源,pc记录方法执行的指令,把栈桢的使用空间放在计数器里面。(计数器比较快),阻塞不能直接进入运行状态,要先进入到就绪态,等待运行。 线程的死亡状态: 内存空间被GC回收 1 线程: 线程的工作空间是稳定的,除非线程死亡 线程的方法的工作空间是不稳定的,执行完后就会被GC回收 处于就绪状态的线程可以有多个,但是处于执行状态的线程只有一个 强行唤醒睡眠中的线程会使得线程处于异常状态 线程睡眠时会进入阻塞状态,这时候就绪状态的线程会根据调度策略来进入执行状态。Java的调度策略是抢占式的策略。(随机调度策略) C语言的调度策略是时间片轮转,先来先服务 栈中存放形参,局部变量,断点,临时数据 死亡状态的线程释放资源 处于阻塞态资源保存在PC和寄存器中 线程可以从运行态直接转到就绪态 主线程死亡的状态是主方法执行完,其它线程的死亡状态是其run方法执行完 线程的优先级: Thread.MIN _PRIORITY=1; Thread.NORM _PRIORITY=5;//主方法的线程优先级为5,普通线程没有设定优先级默认和主线程一样 均为5级 Thread.MAX _PRIORITY=10; 主线程是第五级,也可以通过SetPriority()方法设定优先级 yield()//只给同一优先级的线程让步, 抢占的时候 低优先级的给高优先级的让步 setDaemon(true);//将线程设为后台线程,并且启动(也叫守护线程) 后台线程的特点:由主线程创建,前台线程执行完了 ,后台线程自动结束(死亡) Join的特点:当A线程执行到B线程的join方法时,A线程就会等待,一直等到B线程执行完,A才会执行。Join用来临时加入线程执行。 前台线程和后台线程的调度机制就是时间片轮转机制 主线程死亡后,后台线程在分配的时间内会继续执行,时间到了就会自动销毁 如果调度机制是时间片轮转机制,yeild()方法不会对线程的执行顺序造成影响 主线程执行完主方法创建线程对象后会死亡,但是不影响其创建的程的执行 让多个线程共享同一个资源,需要在内部类中创建线程 创建线程的方法 1.1 继承Thread类,实现run()方法 1.2 实现Runnable接口,实现run()方法 创建的类不是线程类,对象也不是线程对象。把接口创建的对象作为参数方法Thread 对象创建中才是线程类。这个类的对象是为了线程类提供run()方法。 例如: MyThread mt=new MyThread();//MyThread类实现了Runnable接口,并且实现了run()方法 Thread thread=new Thread(mt);//thread才是线程对象,调用有参构造方法,参数一定是对象的地址,并且所指的对象一定要实现Runnable接口,实现run()方法。 实现资源共享时,Thread thread1=new Thread(mt); Thread thread2=new Thread(mt); thread,thread1,thread2共享mt所指对象的变量。 1.3 线程的创建,实现Runnable接口,可以不用创建内部类实现资源共享。 1.4 isAlive();//判断线程对象是否处于激活状态 1.5 终止线程的方法 1.5.1 通过设定共享数据,根据标记控制线程执行。共享相同对象的变量,修改变量的属性值。 1.5.2 thread.stop();//在时间片范围内直接终止thread线程,此方法不推荐使用,如果thread线程正在执行原子操作,突然终止会破坏数据的原子性,影响对其他线程的使用。 1.5.3 thread.interrupt();//thread在睡眠时,主线程执行thread.interrupt(),主线程强行终止thread线程的睡眠,让thread线程处于就绪状态,一旦thread被唤醒后,thread线程从(catch)异常代码开始执行,抛出异常,终止线程 2 线程的同步 Synchronized:同步可以保证数据的原子性不被破坏 不同的线程对象执行的run方法在不同的工作空间 锁是一种对临界资源控制的机制,利用每个对象自身的功能。 Synchronized(obj){};//对象锁,控制语句块 对实例方法加锁时,锁的是调用实例方法的对象。传递的锁对象是 this 对象锁:主要锁语句块(同步块)作用范围为上锁的语句块 方法锁:锁的是调用该方法的对象的所有方法(实例方法)作用范围是对象的所有方法 类锁:锁的是加载类时创建的反射对象。静态方法通过类调用,加载类会创建和类相关的反射对象,给静态方法加锁时,锁的是所有类上的静态方法。一个线程访问一个静态方法,所有线程和所有静态方法都不能被执行。作用范围是该类的所有静态方法 传递的锁对象是 类名.class 作业: 使用内部类创建四个线程,两个对实例全局变量i做+1操作,两个线程对i做-1操作。加减操作不用考虑顺序。 1 添加sleep() 可以人为的控制调度策略,更改线程的执行顺序 线程对象都具有start()方法,如果不能调用start()方法,则不是线程对象 普通线程的run()方法执行完后,线程死亡,内存被GC回收。 锁体现在哪? 对象就是锁。synchronized(new Object()); synchronized的作用? 锁语句块,保证同一时刻只有一个线程可以操作这段语句块。只能操作结束后其它线程才能操作。 上锁的条件:至少有两个或两个以上的线程。 不管是对象锁还是类锁或者是方法锁,起作用的都是对象。 实例方法锁:锁对象是this,一旦调用一个方法,其它线程无法使用该类对象的 其它方法。 2 同步代码块设置不当会导致程序出错 This.notify();//(唤醒等待池中的线程,放到锁池里),保证生产者和消费者不会争用资源(此状态下当前线程依旧占领处理机,当执行到wait方法时才让出处理机,自己进入等待池中) this.wait();//开启锁,让别的线程执行,当前线程进入等待池。 3 生产者和消费者 进入锁池表示进入阻塞态 Wait():先把锁池开启,把自己放入等待池中等待 生产者送情报,送完之后要等消费者取到情报后才能离开 notify():把等待池中的线程放到锁池中,这时候处理机依旧是当前线程在占领,直到当前线程执行wait()方法,被唤醒的线程才会开始占领处理机执行程序。 消费者先回来的· 4 线程如何保证安全性:synchronized 线程如何保证并发性:ThreadLocal 线程出现死锁:方法锁中的有语句块锁,语句块锁中有方法锁,锁的对象不同,一个是this,一个是obj。wait(),使得线程处于等待池;notify(),唤醒的线程是按顺序唤醒的,一般是唤醒第一个进入等待池中等待的线程。线程进入等待池是按顺序进入的,先进入的一般会先被唤醒。 notifyAll():唤醒线程池中所有处于等待的线程。 等待和唤醒必须是同一个锁,锁可以是任意对象,可以被任意对象调用的方法定义在Object类中。 非线程资源类中也可以有同步资源,同步代码块。 /** * 用内部类实现线程,创建四个线程, * 其中两个线程对实例全局变量+1操作,两个线程对实例全局变量-1操作。 * 加减不考虑先后顺序 * */ public class TestThread { int i=10;//目标操作数 // int count=0,count1=0;//用来控制执行次数的标记,当count>10的时候就结束 // Object obj=new Object(); /** * @param args */ public static void main(String[] args) { TestThread tt=new TestThread();//创建当前类的外部类对象,用于创建内部类 AddThread addThread=tt.new AddThread();//创建内部类对象 SubstractThread subThread=tt.new SubstractThread();//创建内部类对象 Thread threadAdd=new Thread(addThread);//创建做+1操作的线程对象 Thread threadAdd1=new Thread(addThread); Thread threadSub=new Thread(subThread);//创建做-1操作的线程对象 Thread threadSub1=new Thread(subThread); threadAdd.start();//启动线程,使得线程处于就绪状态 threadAdd1.start(); threadSub.start(); threadSub1.start(); } private synchronized void inc(){//同步这两个方法,使得线程不能同时对i进行+1 和-1操作,只能选择其中一个操作。 i++; } private synchronized void dec(){ i--; } //做+1操作的内部类 class AddThread implements Runnable{ public void run(){ for(int k=0;k<5;k++){ //打印出当前执行的线程名 和当前i的值 synchronized(obj){ System.out.println(Thread.currentThread().getName()+"--add---前:i= "+i); inc(); System.out.println(Thread.currentThread().getName()+"--add---后:i= "+i); } } } } //做-1操作的内部类 class SubstractThread implements Runnable{ public void run(){ for(int j=0;j<5;j++){ //打印出当前执行的线程名 和当前i的值 Synchronized(obj){ System.out.println(Thread.currentThread().getName()+"--substract---前:i= "+i); dec(); System.out.println(Thread.currentThread().getName()+"--substract---后:i= "+i); } } } } } 5 I/O流:java通过流完成了和外部数据的交互 外部数据是指外存,标准设备,网络 I/O流是一条用来传送数据的管道 5.1 输入流:读 从外向内,读入到内存的存储文件中 5.2 输出流:写 从内向外,输出到外部的文件中 5.3 流的传送单位是字节流 byte 5.4 从文件的角度看待流: 5.4.1:文本文件 显示器和键盘,是用一个字节来存放一个字符的,是可见的,显示器是典型的文本文件,从键盘中输入的都是字符,之后转换为二进制。 特点:以字符的形式出现,每个字符占用一个字节, 文件文件都是可见的 文本文件最大的特点是需要转换 3.141592678:以文本文件存储占11个字节,存放的是ASIIC码值,最后显示的是字符 5.4.2:二进制文件 .exe文件,以内存的方法存储,double类型3.141592678 八个字节。二进制文件和内存的形式相同,不可见,不需要转换,二进制文件的读写效率更高 数据在内存中运算都是以二进制类型进行运算的 字节流的种类: 文件字节流:从文件中拿数据 标准字节流:从键盘缓冲区中拿数据 网络字节流:通过Socket从信道中拿数据 管道字节流:从管道中拿数据 顺序字节流:sequence流,一般只有一种顺序输入流, 所有字节流都有个共同的父类,也叫做基类。输入流的基类:InputStream (抽象类),输出流的基类:OutputStream(抽象类),这两个基类是抽象类,不是接口。 文件字节输入流和文件字节输出流:(会产生编译异常) 主要catch()两个异常,FileNotFoundException 和 IOException FileInputStream: FileInputStream:如果文件存在,会删除重新创建,不存在也不会抛出异常,会创建新的文件 System.out.println()和System.err.println()是一样的,可以一起使用 随机访问流: RandomAccessFile:它的基类 (父类)是Object,随机流不区分输入流和输出流,可以随机访问文件当中的任何字符,随机流可以访问文本文件和二进制文件,对文件的访问方式要设定, 二进制文件的特点:存放数据的方式和内存一样 randf.seek(2*4);//将文件位置移动8个位置,每个int类型的数据占4个字节 readInt();//按二进制文件读取数据,一次读取四个字节。 1、 文件字节流处理文本文件,对象流处理二进制文件 2、 字节流:是最基本的流 int c; while(c=fis.read()!=-1) fos.write(c); 对于输入流,一定要 fis.flush(); 3、随机流 RandomAccessFile RandomAccessFile raf=new RandomAccessFile(“a.txt”,”rw”);//随机流,指定文本和访问方式。 文件位置指针的移动位置是可以手动确定的,以字节为单位 raf.seek(1*4);//指定指针开始查看的位置 4、套接流BufferedInputStream BufferedOutputStream 过滤流,缓冲流: BufferedInputStream bin=new BufferedInputStream(in,256);//in:表示文件,256表示一次最多读取字节或者字符的个数 byte[] buf=new byte[1024]; int c=bin.read(buf,0,512);//c返回的是实际读取到的字符的个数 流关闭时,可以直接只关闭套接流。 BufferedOutputStream : bos.write();之后一定要用bos.flush();来强制输出。 Buffered流的特点: 1、 要有字节流对象作为参数,也可以是对象流 2、 一般处理文本文件,不处理二进制文件 3、 一般来说不能一次读取一行字符 4、 作为套接流,并且区分输入流和输出流 Data流(数据流) 1、是一种嵌入流,首先得有字节流(文件字节流,网络字节流,一般不用标准字节流) 2、既可以读取文本文件,又可以像随机流一样存取二进制文件,而且能够按行读取(readLine()) 3、如果嵌入到网络字节流,可以读取还能对网络字节流进行编码 DataInputStream Dis.readUTF(); Dis.writeUTF(); Data流处理二进制文件:一定要按存的顺序去读取文件,不然可能会乱码 Data流的readLine()读取的一定是文本文件, 通讯的双工理论:只能听见对方的不能听见自己的声音。 文件流的目标是文件,网络流的目标是网络信道 标准流: 一般套接buffered流,一般不套接Data流 键盘和显示器都是字节设备 标准字节输入流(InputStream)的最大功能是 从键盘中拿到的字节数据,转换成字符串 System.in 标准字节输出流(PrintStream)从文本中拿到数据到显示器中显示 System.out/System.err System类不能同时继承InputStream和PrintStream这两个类,所以System中需要调用到InputStream和PrintStrem 的方法是静态方法,将InputStream 和 PrintStream作为参数传入方法中。 对象流: 能够输入输出对象的流称为对象流 对象的持续性:能够记录自己的状态以便将来再生的能力,叫做对象的持续性 对象的串行化(序列化):对象传送自己状态的过程叫做串行化 从流中拿出对象的状态生成对象的过程又叫反序列化,反正串行化 怎么完成串行化? 1、 生成对象的类一定要加入串行化协议(对象类 implements Serializable接口,不用实现任何方法 ) 2、 对对象的属性赋予状态,对对象属性赋值 (创建类对象,并且给对象的实例全局变量赋值) 3、 创建对象输出流对象,调用相应的方法 writeObject();(FileOutputStream ) 4、 把对象所指的状态通过管道一步一步的写到对象输出流所指定的二进制文件中 存放对象状态的文件只能是二进制文件 对象的状态想要传送,对象类必须实现Serializable 对象输入流对象:ObjectInputStream ObjectInputStream ois=new ObjectInputStream(fin); Student stu=(Student)ois.readObject(); 对象输出流对象:ObjectOutputStream 反序列化:通过对象输入流对象从指定位置读取对象的状态,调用 readObject(),将对象状态再生成对象 在变量前加上 transient表示不想进行传送,在进行网络传送时会不进行传送 对象的序列化: //对象的序列化 public void objSer()throws IOException{ //创建需要被序列化的对象 Student stu1=new Student(); stu1.setNum(123); stu1.setName("西欧爱心"); FileOutputStream fos=new FileOutputStream("1.txt");//创建字节文件输出流对象 ObjectOutputStream oos=new ObjectOutputStream(fos);//创建对象输出流对象 oos.writeObject(stu1);//序列化对象 oos.close();//关闭对象输出流 } 创建对象的五种方法: 1、 New//new Student(); 2、 方法 //set.iterator(); 3、 反射 in=new FileInputStream("hello.properties"); prop.load(in);//加载文件 String className=prop.getProperty("className");//得到配置文件中的类名 Class c=Class.forName(className);//反射类 ReflectDemo reflectDemo=(ReflectDemo)c.newInstance();//将反射类得到的实例对象造型为接口类型 4、 反序列化 //对象的反序列化 public void objRecov() throws IOException, ClassNotFoundException{ FileInputStream fis=new FileInputStream("1.txt");//创建字节文件输入流 ObjectInputStream ois=new ObjectInputStream(fis);//创建对象输入流对象 Student stu=(Student)ois.readObject();//对象的反序列化,通过反序列化得到对象 System.out.println(stu.getNum()+"--"+stu.getName());//使用反序列化得到的对象 ois.close();//关闭对象输入流 5、 克隆 管道流 PipedInputStream PipedOutputStream 特点: 1、 输入流和输出流首位相接 2、 文件内存把输出放在管道输出流(内到外),另一个线程从管道中拿出数据到内存 称为管道输入流(外到内) 3、 管道流的传输不涉及文件也不涉及网络(都是字节流) PipedInputStream pis=new PipedInputStream();//创建一个管道输入流对象 PipedOutputStream pos=new PipedOutputStream(pis);//创建一个管道输出流对象,将管道输入流对象作为参数传给管道输出流对象 pos.write((byte)123);//将字节123写入管道中 pis.read();//读取管道中的数据,如果管道中没有数据则会一直处于等待状态 管道流的作用一般是用于线程之间的数据传送,管道流必须依附线程 线程死亡后,管道流如果未关闭继续读取的话会报错: write and dead 生产者拿到管道输出流,消费者拿到管道输入流,将管道流作为创建线程对象的构造方法的参数传入。 管道流使用完后要记得关闭, pis.close(); 顺序流:SequenceStream FileInputStream fis=new FileInputStream(“file.txt”); FileInputStream fis1=new FileInputStream(“file1.txt”); SequenceInputStream sis=new SequenceInputStream(fis,fis1);//连接多个文件字节输入流 DataInputStream dis=new DataInputStream(sis);//将顺序流方法数据流中 以行为单位读取 String str=dis.readLine();//(判断结束的比较对象为 null) 字符流:一次只能读取一个字节 字符流的基类为Reader 和 Writer InputStreamReader dis=new InputStreamReader(new FileInputStream(“file.txt”)); 字符流基于字节流,创建字符流对象时,要把字节流对象作为参数传入。 字符BufferedReader 一次可以读取一行 readLine() 三种套接流:(用来套接字节流) 1)、Buffered流 2)、Data流 3)、Object流 反射:如何通过字符串拿到反射对象 完成自动装配 String className="package.className";//写出类名的全路径名 * Class c=class.forName(className);//加载反射类 * Object obj=c.newInstance();//创建反射类对象 * User user=(User)obj;//强转类型 * user.setName();//调用方法 * * 反射的缺点: * 效率低,打破对象的封装性,代码复杂,可维护性不是很好 * * 优点: * 可以直接访问对象的方法 属性 * 创建对象 可以反编译 * * 一般情况下不要用反射 用反射的目的:用户的需求改变不用改变当前的代码,只需要改配置文件就可以了 作业:用管道流实现生产者消费者 package homework; import java.io.IOException; import java.io.PipedOutputStream; /** * 生产者类 * */ public class ProductorPipe implements Runnable { private PipedOutputStream pipeOut;//定义管道流输出流用来传输数据 public ProductorPipe(PipedOutputStream pipeOut){//在构造方法中传入管道流输出流对象,使得创建生产者线程对象的时候注入管道流输出对象 this.pipeOut=pipeOut; } public void run(){ for(int i=0;i<10;i++){ try { pipeOut.write((byte)i);//往管道流中写入数据 System.out.println("write---"+i); } catch (IOException e) { e.printStackTrace(); } } try { pipeOut.close();//关闭输出管道流 } catch (IOException e) { e.printStackTrace(); } } } package homework; import java.io.IOException; import java.io.PipedInputStream; /** * 消费者类 * */ public class ConsumerPipe implements Runnable{ private PipedInputStream pipeIn;//定义管道输入流来读取管道中的数据 public ConsumerPipe(PipedInputStream pipeIn){//在构造方法中传入管道流输入流对象,使得创建消费者线程对象的时候注入管道流输入对象 this.pipeIn=pipeIn; } public void run(){ for(int i=0;i<30;i++){ try { int j=(byte)pipeIn.read();//读取管道流中数据,当管道中没有数据的时候,会输出 -1 System.out.println("读数据---*****"+j); } catch (IOException e) { e.printStackTrace(); } } try { pipeIn.close();//关闭管道输入流 } catch (IOException e) { e.printStackTrace(); } } } package homework; import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; /** * 测试管道流连接的生产者消费者线程 * * */ public class TestPipeProCon { public static void main(String[] args) { PipedOutputStream pipeOut =new PipedOutputStream();//创建管道输出流对象 PipedInputStream pipeIn=null; try { pipeIn=new PipedInputStream(pipeOut);//创建管道输入流对象 } catch (IOException e) { e.printStackTrace(); } ProductorPipe pro=new ProductorPipe(pipeOut);//创建生产者对象 ConsumerPipe com=new ConsumerPipe(pipeIn);//创建消费者对象 Thread thPro=new Thread(pro);//创建生产者线程对象 Thread thCom=new Thread(com);//创建消费者线程对象 thPro.start();//启动生产者线程 thCom.start();//启动消费者线程 } } 字符Buffered流可以按行读取,字节Buffered不可以按行读取。字符流不能读取二进制文件。 Data流可以按行读取也可以读取二进制文件 Awt:事件突发机制 观察者机制 (内部类/匿名类) 本周需要完成的项目: 计算器:菜单栏 银行项目1.4 :工厂模式 反射类 银行项目1.5 :awt java聊天室:线程数组,线程集合 反射:如何通过字符串拿到反射对象 完成自动装配 String className="package.className";//写出类名的全路径名 * Class c=class.forName(className);//加载反射类 * Object obj=c.newInstance();//创建反射类对象 * User user=(User)obj;//强转类型 * user.setName();//调用方法 * * 反射的缺点: * 效率低,打破对象的封装性,代码复杂,可维护性不是很好 * * 优点: * 可以直接访问对象的方法 属性 * 创建对象 可以反编译 * * 一般情况下不要用反射 动态装配: 1、 如何创建一个类型不断变化的对象? 2、 怎么用工厂完成装配? Faotory 处于业务层和持久层之间,创建factory类的步骤: 1、 创建properties对象 2、 创建流对象 FileInputStream 3、 加载 load(),关闭流 4、 从properties文件中读取className的值(存储的类名要写全称 com.cx.UserDaoFactory),拿到的仅仅是字符串 类名 className 5、 加载到内存Class c= Class.forName(className);//创建映射对象,可以通过映射对象得到属性,方法。 映射对象可以通过getClass()得到。 6、 创建对象实例: Object obj=c.newInstance(); 7、 造型 : userDao=(BankDaoInterface)obj;//按照接口造型, 8、 加同步的饱汉式单例得到当前类的对象 用反射的目的:用户的需求改变不用改变当前的代码,只需要改配置文件就可以了 1 工厂创建对象是单例模式,在项目当中只有一个工厂完成装配 2 工厂使用synchronized 保证安全性 工厂属于持久层 表示层:接受用户的请求,对请求进行分析 业务层:完成表示层所要求请求的功能 持久层:完成对数据库的存储操作 模型层:把数据封装到bean中 (bean:是数据在内存中的存储方式) 3、怎么使用工厂? 业务层接口上面对应的是表示层 业务层和持久层必须用接口 业务层也是单例模式 synchronized ,(一个请求多个线程来访问,使用单例保证一个请求创建一个业务对象,保证访问的安全性 <单例+同步>) 业务层使用<单例+同步>来创建业务对象是有必要的 安全级别越低,并发性越高 4、表示层怎么获取业务对象操作持久层? 业务层有持久层接口变量,工厂也有持久层接口变量 在创建业务层对象的时候,会调用业务层无参的构造方法,所以在业务层的构造方法中,创建工厂,通过创建工厂拿到持久层对象 业务层和工厂都使用<单例+同步>创建对象,持久层可以不用同步,保持单例就行 业务层通过工厂对象拿到工厂装配的持久层对象的地址,指定业务层该调用的持久层对象(体现了拥抱需求) 在业务层 通过工厂得到的持久层对象给业务层中定义的持久层实例全局变量赋值,然后在调用持久层中的方法 private ManagerImpl(){//业务层的构造方法 UserDaoFacory userDaoFactory= UserDaoFacory.getInastance(); UserDao= userDaoFactory.createUserDao();//createUserDao是工厂中的一个实例方法,用来返回通过反射得到的userDao对象方法 } 创建工厂做两件事: 1、 通过反射创建持久层对象(根据用户需求动态创建持久层对象) 2、 将持久对象装配给userDao(赋值给)(把持久层对象传给指定变量) 业务层: 1、在构造方法中创建工厂拿到工厂对象 2、拿到工厂对象中得到的(动态变化的)持久层对象 为什么引用工厂模式? 持久层经常变化 ---- 拥抱需求 工厂被用了几次? (只用一次,完成装配) 只用一次,第一个线程被调度执行业务层对象的时候使用了工厂,业务对象只创建一次,所以也只调用了一次业务对象的构造方法,往后业务对象已存在,不用再创建。并且业务层已经确定了该用哪个持久层对象了,就不用再使用工厂了 在用工厂装配的时候,可不可以有多个线程完成装配操作? 不可以,工厂必须是<单例+同步>,保证安全性 工厂模式 连接业务层和表示层 作业 /** * 工厂类<单例+同步> * 通过反射从properties文件中得到持久层的实现类对象 * @Author DFX * @Version 1.3 2017/07/18 * */ public class DaoFactory { private DaoInterface daoImpl;//定义持久层接口实现类的对象 /* * <饱汉式单例+同步> * * */ private static DaoFactory daoFactory=null; private DaoFactory(){} public synchronized static DaoFactory getInstance(){ if(daoFactory==null){ daoFactory=new DaoFactory(); } return daoFactory; } /* * 通过反射得到持久层实现类对象 * @Params null * @Return DaoInterface * * */ public DaoInterface getObj(){ Properties prop=new Properties(); FileInputStream fis=null; try { fis=new FileInputStream("daoImpl.properties"); prop.load(fis); String className=prop.getProperty("className");//得到反射类全名 Class c=Class.forName(className);//加载反射类 Object obj=c.newInstance();//创建反射类对象 daoImpl=(DaoInterface)obj;//对反射类对象造型为实例接口 } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return daoImpl;//返回持久层实现类对象 } } 在业务层的构造方法中通过工厂类得到持久层对象 DaoInterface daoImpl; // 无参构造方法 public ManagerImpl() { DaoFactory daoFactory=DaoFactory.getInstance();//得到工厂类对象 daoImpl=daoFactory.getObj();//通过工厂类对象得到持久层接口实现类对象 } public static synchronized ManagerImpl getInstance(){};//静态锁,作用于和类所关联的反射对象 静态锁的目的:把线程的并行变成串行 业务层加锁->持久层加锁-> 数据库加锁->事务加锁(效率越来越高,安全性越来越低) 自我介绍:个人信息 自己所学的专业知识 应聘岗位技术 常用的容器 Frame 和 Panel TextArea:文本域,一次可以写多行 TextField:文本区,一次只可以写一行 Panel 需要被加载到Frame中,panel 是不可见的//frame.add(panel); TextField:文本 TextField(int columns):新建一个指定行数的文本域 布局:组件在框架中的排列方式 流式布局FlowLayout:从左到右,从上到下(根据框架大小自适应) 面板的默认布局 网格式布局 GridLayout: GridLayout(2,1);//指定 两行一列(计算器) 边界布局BorderLayout:是frame框架的默认布局,中间大,东南西北分 网格包布局GridBagLayout Gridx=1;//X轴距离 Gridy=1;//Y轴距离 setHeight=1;//高度 setWidth=1;//宽度 Awt中没有单选框,只有Checkbox(复选框) 要设置单选框时,需要设定复选框组 CheckboxGroup CheckboxGroup cg=new CheckboxGroup(); Checkbox cb=new Checkbox(female,cg,true); 选择框: Choice movies=new Choices();//下拉列表框 Movies.addItem(“^^^”); 网格式布局 * public GridLayout(int rows,int cols,int hgap,int vgap) * rows:行 * cols: 列 * hgap: 水平间距 * vgap: 垂直间距 * * String str=but0.getLabel();//得到按钮上的内容 Awt的事件突发机制 组件会产生什么事件,监听器实现什么接口,有什么方法 1.1、 事件源产生事件,监听器处理事件。 1.2、 一个事件源可能会产生不同类型的事件 1.3、 MyListener实现ActionListener接口,实现actionPerformed方法 1.4、 观察者模式:MyListener ml=new MyListener();//其中ml为观察者对象 1.5、 AddActionListener(new ActionListener(){//事件驱动 public void actionPerformed(ActionEvent e){ //这里写出需要的操作 } }); 1.6、 class ButAction extends Frame Implements ActionListener{//定义类时继承Frame框架实现ActionListener接口 public ButAction(){//构造方法中有this指针 but.addActionListener(this);//默认调用当前对象的actionPerformed方法 public void actionPerformed(ActionEvent e){//实现ActionListener接口中定义的方法 } } } 1.7、 ActionEvent:实现ActionListener接口,实现 actionPerformed方法 1.8、 ItemEvent:复选框,单选框,列表框的单击,选择框的单击会产生这个事件 实现 ItemListener接口 只有一个方法需要实现 itemStateChanged() 1.9、 FocusEvent(焦点事件):每个组件都有这个事件,实现FocusListener接口,实现focusLost(焦点丢失) 和 focusGained(焦点获得)方法(适配器类:FocusAdapter) 焦点事件对数据的检查非常重要 tf.addFocusListener(new FocusAdapter(){//调用空实现类,可以只选择其中一个方法实现 ,还有个focusGained方法被空实现了 public void focusLost(FocusEvent e){//焦点丢失方法 TextField tf=(TextField)e.getSource();//拿到事件源 if(!tf.getText().equals(“Tom”)){//分析数据是否正确 tf.setText(“”);//清空文本框中的数据 tf.requestFocus();//重新获得焦点(光标的位置) } } }); 焦点事件可以对用户输入的信息做格式验证 1.10、 KeyEvent:键盘事件 。(可以对输入的任何字符做监听并且获取输入的字符)实现KeyListener接口,实现三个方法 keyPressed:键盘输入(按下)//显现键值① int keCode=e.getKeyCode();//得到输入的字符 keyTyped:键盘按下并释放//显现键符② e.getKeyChar();//得到键盘录入的字符 keyReleased :键盘释放(松开) int keyCode=e.getKeyCode(); repaint();//重绘,让frame设置的属性立即显示效果 addKeyListener(new KeyAdapter(){ public void keyPressed(KeyEvent e){ } }); 1.11、 MouseEvent:鼠标事件,有两种监听器 需要实现MouseListener或者 MouseMotionListener接口,接口中的方法有: mouseClicked:单击 int clickNum=e.getClickCount();//得到鼠标单击次数(可以对文本框产生的事件做鼠标事件监听) e.getX();//得到X坐标e.getY();//得到Y坐标 mouseEntered: mouseExited: mousePressed: mouseReleased: mouseDragged: mouseMoved: (适配器类:MouseAdapter) 1.12、 适配器类:将监听器中的所有方法都实现,只是空实现。空实现的类叫做适配器类 1.13、 WindowListener:窗口事件。可以继承WindowAdapter适配器类,此类已经对WindowListener接口中的所有方法都做了空实现。所以使用的时候可以选择自己需要用到的方法进行复写。 frame.addWindowListener(new WindowAdapter(){//窗体驱动 public void windowClosed(WindowEvent e){ System.exit(0);//退出,关闭窗体 } }); 1.14、 多点监听器: 一个组件实现多个监听器接口,实现每个接口中的每个方法 1、 匿名内部类 将私有内部类,写到构造方法中,直接变成匿名内部类 在构造方法中: this.addWindowListener(new WindowAdapter(){//这个对象没有类名,只知道这个对象的类继承了WindowAdapter父类 public void windowClosing(WindowEvent e){ System.exit(0); } }); this.addWindowListener(new WindowListener(){//new后面跟一个接口,但是接口不能创建对象,这只能说明 这是个实现了WindowListener 接口的匿名类 }); 匿名类的优点:代码简单 匿名类的缺点:可维护性差,不易理解,使用范围窄, 只能在一个场合使用 匿名类只在特殊场合使用: 1、 事件突发机制 做监听器 2、 Spring IOC 和AOP 的机制 callback 回调的核心技术 2、 Swing(Menu)用swing如何做菜单 Awt的缺点: 1、 依赖底层,在不同的系统下显示的结果不同 2、 没有单选框 3、 做出来的效果比较死板 4、 关闭时一定要使用事件突发机制 Swing: Java 为swing提供JFC类库 使用swing要导入三个包 javax.swing.*; java.awt.*; java.awt.event.*; Model:业务层 持久层 模型层 原则:做图形界面最好是不要把swing和awt混在一起写,否则会混乱 (因为swing是稳定的,awt是基于底层不稳定的)凡是以J开头的组件都属于swing,类似JFrame 。 JFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//自动实现窗口关闭 JButton.setMnemonic(KeyEvent.VK_F);//在按钮上设置快捷键 F JButton上可以添加背景图片,再添加文字 JTextField jtxt=new JTextField(Document doc,int column,int row); New JtextField(“默认文字”,”图片(ImageIcon)”,””); JComboBox :组合框 菜单: JMenuBar mb=new JMenuBar();//创建菜单条 f.setMenuBar(mb);//将菜单条放入框架中 JMenu m1=new JMenu(“文件”);//创建菜单 mb.add(m1);//把菜单添加到菜单条中 mb.setHelpMenu();//创建帮助菜单项 JMenuItem mi1=new JMenuItem(“新建”);//创建菜单项 m1.add(mi1);//将菜单项添加到菜单条上的菜单中 mi1.addActionListener();//菜单项可以添加事件监听事件 菜单项可以添加二级菜单(可以是普通菜单项,也可以是复选菜单项) JCheckBoxMenuItem:复选菜单项 m1.add(“Hello World”);// 可以把字符串作为子菜单 m1.addSeparator();//在菜单中添加间隔线 菜单中添加菜单,表示被添加的菜单是二级菜单,之后再添加菜单项 1 通讯协议:计算机域计算机之间通讯的约定的方式 1.1、 OSI协议分为七层:物理层、链路层、网络层、传输层、会话层、表示层、应用层 1.2、 TCP/IP(传输控制/网际协议)参考模型包括五层:物理层、链路层、网络层、传输层、应用层(URL和URLConnection) URL:是高层的网络设备的资源 URLConnection:是对设备的连接 1.3、载波监听多路访问:先听后发,边听边发,碰撞后随机重发 1.4、 网络层:点到点 1.5、 传输层:端到端,不关心中间怎么传输(TCP和UDP协议) 1.6、UDP(用户数据报)协议特点:不可靠,无序,但是传送效率高 1.7、TCP(传输控制)协议特点:可靠,有序,以字节流的方式传送数据,通常被称为流通信协议 1.8、端口号:标识网络应用的逻辑地址 1.8.1、 Oracle端口号:1521 sqlServer 端口号:1433 Mysql端口号:3306 1.8.2、端口号定义规范: 0~65535,其中0~1023为系统保留 2 Java提供的网络功能的四大类: 2.1、InetAddress:面向IP层,用于标识网络上的硬件资源(InetAddress:封装IP地址和端口信息) 网络层 2.1.1、构造方法私有化,不能用new来创建对象也不能用反射来创建。可以通过静态方法创建 2.1.2、域名:用来标识的字符串 例如:www.sina.com 2.1.3、静态方法:getLocalHost();//获取本机InetAddress对象 2.1.4、静态方法:InetAddress.getByName(“www.ztenc.com.cn”);//进入中兴服务器,通过域名找到服务器的IP地址和机器名 InetAddress address=InetAddress.getLocalHost();//得到主机名和主机的IP地址USER-20150330OI/172.16.0.126 System.out.println("address:"+address); InetAddress host=InetAddress.getByName("USER-20150330OI");//根据主机名得到主机名和IP地址(USER-20150330OI/172.16.0.126) System.out.println("host:"+host); 2.2、URLs:面向应用层,通过URL,标识网络服务器上的资源(URL和URLConnection) 2.3、Sockets:面向传输层。(Socket:客户端通过socket发送请求 ,ServerSocket:服务端通过ServerSocket对象做请求监听) 2.4、Datagram:DatagramPacket:数据报,DatagramSocket:邮递员 MulticastSocket:多波协议,发送方在指定端口上发送信息,发送端没有明确的地址,可以同时有多个接收方在指定端口上接收。(一个人发送多个人接收) 3 Applet类的字节码文件嵌入在html中,服务器通过下载字节码文件,运行程序 4 URL url=getCodeBase();//得到提供它的主机的URL实例,只在Applet程序中用 Host host=url.getHost();//利用URL得到主机名 InetAddress address=InetAddress.getByName(host);//根据主机名称得到主机名和主机的IP地址 DataSocket ds=new DataSocket();//邮递员 DatagramPacket dp= new DatagramPacket(buf,length,address,port);//buf:字节数组, length :一次传送数据的长度,address:主机的IP地址,port:服务器端口号 ds.send(dp);//邮递员发送数据报 5 URL url= new URL(“http://www.ztenv.com.cn/text.html”);//得到url(用于下载text.html文件) 通过URL(统一资源定位器)获取网络字节输入流对象,url.openStream(),返回一个网络输入字节流(从网络开始,读到客户端) new InputStreamReader(url.openStream());//把字节流变成字符流 BufferedReader br=new BufferedReader(new InputStreamReader(url.openStream()));//把字符流套入buffer流 URL url=new URL(“file:/D:/a.txt”);//file协议,表示读取本机文件 6 URLConnection (用于上传文件) URL url=new URL(“file:/D:/a.txt”); URLConnection uc=url.openConnection();//得到连接对象 uc.getInputStream();//通过连接对象创建网络字节输入流对象,通过输入流读取数据,叫做下载 URL url1=new URL("file:/f:/Users/a.java");//file协议 读取本机文件 URLConnection uc=url1.openConnection();//得到连接对象 InputStreamReader in=new InputStreamReader(uc.getInputStream());//创建网络字节输入流对象,封装到输入字符流中 BufferedReader br1=new BufferedReader(in);//将字符流套入buffer流中while(br1.readLine()!=null){//读取buffered缓冲流中的数据 System.out.println(br1.readLine()); } uc.getOutputStream();//创建网络字节输出流对象,通过这个流把客户端的数据上传到服务端,通过输出流写入数据叫做上传 OutputStreamWriter out=new OutputStreamWriter(uc.getOutputStream());//创建网络输出字节流对象,封装到输出字符流中 BufferedWriter bw=new BufferedWriter(out);//将字符流套入buffer流中 bw.flush(); 7 使用第四层协议TCP协议来通信 服务器相当于中介转发作用 7.1、Socket通信: ServerSocket :用在服务端,创建时需要给定端口号,调用accpet()方法,对用户在指定端口上的请求做监听 ss=new ServerSocket(port); ss.accpet(); Socket:客户端通过Socket向该端口发送连接请求 Socket(host,port); 客户端和服务端都要创建两个流,服务端的getInputStream()从信道中读取数据,客户端也可以创建网络自己输入流读取数据。服务端和客户端都可以创建getOutputStream()可以把信息写入信道中。 客户端只能从信道中读取服务端放入信道中的数据,服务端也只能读取客户端放入信道中的数据。(全双工理论) 先走服务端,ServerSocket在4331端口上创建服务端对象,调用accpet()方法,返回的对象为Socket类型。 客户端建立连接请求,new Socket(“localhost”,4331);,一旦没有出异常,信道就已经创建了.客户端通过mySocket创建网络字节输入流对象,从信道中读数据到客户端,mySocket创建网络字节输出流对象,输出数据到信道中。 如果服务端和客户端一起从信道中读取信息会产生死锁。 mySocket(客户端) DataOutputStream out=new DataOutputStream(mySocket.getOutputStream());//客户端获取输出数据流 out.writeUTF(“你好”);//客户端往信道中写入数据给服务端 DataInputStream out=new DataInputStream(mySocket.getInputStream());//客户端获取输入数据流 out.readUTF();//客户端从信道中读取服务端写入的数据 you(服务端) DataOutputStream out=new DataOutputStream(you.getOutputStream());//服务端获取输出数据流 out.writeUTF(“你也好”);//服务端往信道中写入数据给客户端 DataInputStream out=new DataInputStream(you.getInputStream());//服务端获取输入数据流 out.readUTF();//服务端从信道中读取客户端写入信道中的数据 readUTF();//一次只能读取一个字符串 连接时,先开启服务器,断开连接时,先断开客户端。 InetAddress复写了toString():显示主机名+IP地址 协议名:规定访问资源的方式(file:本地访问,http:浏览器) URL url=new URL(“file:/f:/t.txt”); url.openStream();//字节输入流 客户端在同一时刻可以读取信道中的数据和写入数据到信道中。服务端也可以在从信道中读取数据的时候同时写入数据到信道中。因为客户端和服务端有两个虚信道。 只需要避免客户端和服务端都从信道中读取数据,并且信道中没有数据的时候就不会产生死锁。 1 UDP协议(无序不可靠但是高效) 先把数据放在数据报中,数据以字节数组对象的形式放入数据报中。 String str=”abcdefg”; byte[] buf=str.getBytes();//将字符串变为字节数组对象 一个类中至少要有两个线程,一个线程负责发送数据,另一个线程负责接收数据。(也有可能发送数据的时候正好接收数据了) 上海用Thread线程接收北京发送的数据,上海在主线程通过actionPerformed方法向端口号8888发送信息,北京通过调用Thread线程所执行的run方法中8888端口号中接收信息。 主线程用来发送数据,Thread线程用来接收数据。 主线程是虚拟机创建的,另外一个线程应该在主线程主方法中创建。 class BeiJing extends Frame implements ActionListener,Runnable{ public BeiJing(){ Thread thread=new Thread(this);//在构造方法中创建线程对象,用于对方接收信息 thread.start();//启动接收数据的线程 this.addActionListener(this); } //响应发送数据报按钮事件(发信) public void actionPerformed(ActionEvent e){ byte[] buf=textOut.getText().trim().getBytes();//拿到输出文本框的内容 InetAddress address=InetAddress.getByName(“localhost”);//得到地址 DatagramPacket dp=new DatagramPacket(buf,buf.length,address,port);//把数据封装到数据报(类似于把信放入信箱中) DatagramSocket ds=new DatagramSocket();//邮递员 ds.send(dp);//发送数据报 } //对方接收信息(收信) public void run(){ byte[] buf=new byte[8192];//空的字节数组对象 DatagramPacket dp=new DatagramPacket(buf,buf.length);//空的数据报 DatagramSocket ds=new DatagramSocket(port);//在port端口上创建邮递员 ds.receive(dp);//接收数据(收到的数据存入空的数据报dp中) dp.getPort();//获取发送端端口号 dp.getAddress();//获取发送端的IP地址 dp.getLength();//获取接收到的数据的长度 dp.getData();//是一个字节数组对象 String message=new String(dp.getData());//将字节数组对象变为字符串 textIn.setText(message);//将接收到的数据设置到 textIn.append(“hahaha”);//在文本框中追加数据 } frame.pack();//自动调整组件大小 } 2 多波协议(群发消息) MulticastSocket:多播套接字 InetAddress group=InetAddress.getByName(“239.255.8.0”);// 得到多播组地址对象 MulticastSocket socket=new MulticastSocket(port);//指定播送消息的套接字,邮递员 socket.joinGroup(group);//加入多播组 接收端和发送端的group和port应该是相同的。 调用线程的interrupt()方法,线程从封锁态进入就绪态 接收端: 在public void actionPerformed(ActionEvent e){} 方法中创建一个线程,new Thread(this); 接收信息类实现 Runnable接口。 在public void run(){}方法中接收信息。 创建空字节数组byte[] buf=new byte[8192]; 创建空数据报new DatagramPacket(buf,buf.length,group,port)(指定group,port 接收端和发送端相同), MuticastSocket多播套接字为邮递员。 socket.receive(dp);//将数据放入数据报中 dp.getData();//得到数据报中的数据,为字节数组类型 String message=new String(dp.getData());//将字节数组变为字符串 停止接收按钮响应: thread.interrupt();//只应用一个线程处于封锁状态 继续设置标志位 flag=true; thread.interrupt()方法一执行,接收线程会执行catch中的代码,在catch代码块中判断flag的值,为true时 break。 3 Java聊天室(多线程,接口,GUI 事件突发机制) 服务器创建ServerSocket对象 new ServerSocket(port,10);//端口号和最大连接数 对于最大限度用户数,服务器端应该用线程池或者线程集合。重点在服务端。 为多个用户创建信道,同时为多个用户服务。 通过线程对象拿到信道,获得流 怎么管理10个线程对象? 线程数组来维护多个线程对象。定义一个线程继承了Thread类,复写run方法,提供个有参的构造方法。在服务端定义该类的线程数组,使用数组管理多个线程对象。 private ServerThread[] serThread=new ServerThread[maxClients]; serThread[i]=new ServerThread(ss.accept(),i);//创建服务器线程,i表示等待连接线程数 serThread[i].start();//启动线程 下线: 定义一个下线数组,10个元素 int[],先判断所有上线的人数,再判断下线人数 用for循环判断用户是否已经下线(用户ID) for(int m=0;m boolean flag=true;//标记用户是否为上线状态:true表示已上线,false:表示已下线 for(int j=0;j if(i==outline[j]){//判断用户i是否已经下线,如果i不在下线数组里面表示它以成功上线 flag=false; break;//跳出本层循环 } } 私聊: if((!message.equals(""))&&(message!=null)&&(message.startsWith("*"))){//判断是否为私聊信息 StringTokenizer str=new StringTokenizer(message,"*"); //截取出以*号分隔的的字符 *12*qwe 截取后结果为: 12 num=Integer.parseInt(str.nextToken());//得到信息接收端目的用户 serThread[num].output.writeUTF(message);//将信息写给目的用户 服务端接收信息,客户端发送信息 StringTokenizer(str,”*”);//用*分隔字符串 公聊: 双重for循环,把信息发给每一个在线的用户 for(int i=0;i boolean flag=false; for(int k=0;k if(i==outline[k]){ flag=true; break; } } if(flag){ try { serThread[i].output.writeUTF(ID+"号用户刚刚下线了"); }catch (IOException e1) { e1.printStackTrace(); } } } 问题? 用户下线后,此用户还能继续上线嘛? 不能,因为线程数组已经满了,下线后内存已经被占。此现象称为假溢出 可以用集合来存储线程对象,也可以用线程池 聊天时只有一个服务器,只需要先开启并且开启一次就好,客户端可以开启多个,相互发送消息。 周末作业: 聊天室,TCP协议,UDP协议。版本各一个,使用线程池或者集合数组存储线程对象 多播协议聊天实例: 服务端: //服务端 public void actionPerformed(ActionEvent e6){ byte[] buf=textOut.getText().trim().getBytes();//将文本框中需要发送的数据变成字节数组对象 try { InetAddress address=InetAddress.getByName("239.255.8.0");//端口号必须为D类地址 240.0.0.0-239.255.255.255 MulticastSocket ms=new MulticastSocket();//多播邮递员 ms.joinGroup(address);//加入多播组织 DatagramPacket dp=new DatagramPacket(buf,buf.length,address,23342);//将数据打包 ms.send(dp);//发送数据 } catch (UnknownHostException e1) { e1.printStackTrace(); } catch (SocketException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }} 客户端: //启动线程接收用户发来的数据 public void run(){ while(true){ byte[] buf=new byte[8192];//创建空的字节数组对象,接收多播发过来的数据 try { InetAddress addr=InetAddress.getByName("239.255.8.0");//指定接收同一广播地址的广播 DatagramPacket dp=new DatagramPacket(buf,buf.length);//准备好数据包接收数据 MulticastSocket ms=new MulticastSocket(23342);//监听多播端口号 ms.joinGroup(addr);//加入多播组织 ms.receive(dp);//接收数据 String message=new String(dp.getData());//将接收过来的数据变为字符串类型 textIn.append(message+"\n");//存入文本输出域中 System.out.println("lalala__"+message); } catch (UnknownHostException e1) { e1.printStackTrace(); } catch (SocketException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } B GRVFC集合(Map,Set,List):
线程
I/O
通过反射实现工厂模式
Awt( 以及swing)