java的面向对象(二)

内部类:

(注:所有使用内部类的地方都可以不用内部类,使用内部类可以使程序更加的简洁,便于命名规范和划分层次结构)。

内部类是指在一个外部类的内部再定义一个类。

内部类作为外部类的一个成员,并且依附于外部类而存在的。

内部类可为静态,可用PROTECTED和PRIVATE修饰。(而外部类不可以:外部类只能使用PUBLIC和DEFAULT)。

 

内部类的分类:

成员内部类、

局部内部类、

静态内部类、

匿名内部类(图形是要用到,必须掌握)。

 

①  成员内部类:作为外部类的一个成员存在,与外部类的属性、方法并列。

内部类和外部类的实例变量可以共存。

在内部类中访问实例变量:this.属性

在内部类访问外部类的实例变量:外部类名.this.属性。

 

成员内部类的优点:

⑴内部类作为外部类的成员,可以访问外部类的私有成员或属性。(即使将外部类声明为PRIVATE,但是对于处于其内部的内部类还是可见的。)

⑵用内部类定义在外部类中不可访问的属性。这样就在外部类中实现了比外部类的private还要小的访问权限。

注意:内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两类。

对于一个名为outer的外部类和其内部定义的名为inner的内部类。编译完成后出现outer.class和outer$inner.class两类。

 

(编写一个程序检验:在一个TestOuter.java程序中验证内部类在编译完成之后,会出现几个class.

 

成员内部类不可以有静态属性。(为什么?)

 

如果在外部类的外部访问内部类,使用out.inner.

 

建立内部类对象时应注意:

在外部类的内部可以直接使用inner s=new inner();(因为外部类知道inner是哪个类,所以可以生成对象。)

而在外部类的外部,要生成(new)一个内部类对象,需要首先建立一个外部类对象(外部类可用),然后在生成一个内部类对象。

Outer.Innerin=Outer.new.Inner()。

错误的定义方式:

Outer.Innerin=new Outer.Inner()。

注意:当Outer是一个private类时,外部类对于其外部访问是私有的,所以就无法建立外部类对象,进而也无法建立内部类对象。

 

②  局部内部类:在方法中定义的内部类称为局部内部类。

与局部变量类似,在局部内部类前不加修饰符public和private,其范围为定义它的代码块。

 

注意:局部内部类不仅可以访问外部类实例变量,还可以访问外部类的局部变量(但此时要求外部类的局部变量必须为final)??

在类外不可直接生成局部内部类(保证局部内部类对外是不可见的)。

要想使用局部内部类时需要生成对象,对象调用方法,在方法中才能调用其局部内部类。

 

③  静态内部类:(注意:前三种内部类与变量类似,所以可以对照参考变量)

静态内部类定义在类中,任何方法外,用static定义。

静态内部类只能访问外部类的静态成员。

生成(new)一个静态内部类不需要外部类成员:这是静态内部类和成员内部类的区别。静态内部类的对象可以直接生成:

Outer.Innerin=new Outer.Inner();

而不需要通过生成外部类对象来生成。这样实际上使静态内部类成为了一个顶级类。

静态内部类不可用private来进行定义。例子:

对于两个类,拥有相同的方法:

People

{

  run();

}

Machine{

   run();

}

此时有一个robot类:

class Robotextends People implement Machine.

此时run()不可直接实现。

注意:当类与接口(或者是接口与接口)发生方法命名冲突的时候,此时必须使用内部类来实现。

用接口不能完全地实现多继承,用接口配合内部类才能实现真正的多继承。

 

④  匿名内部类(必须掌握):

匿名内部类是一种特殊的局部内部类,它是通过匿名类实现接口。

IA被定义为接口。

IA I=newIA(){};

注:一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类,没有类名,根据多态,我们使用其父类名。

因其为局部内部类,那么局部内部类的所有限制都对其生效。

匿名内部类是唯一一种无构造方法类。

匿名内部类在编译的时候由系统自动起名Out$1.class。

 

如果一个对象编译时的类型是接口,那么其运行的类型为实现这个接口的类。

因匿名内部类无构造方法,所以其使用范围非常的有限。

(下午:)Exception(例外/异常)(教程上的MODEL7)

对于程序可能出现的错误应该做出预案。

例外是程序中所有出乎意料的结果。(关系到系统的健壮性)

 

JAVA会将所有的错误封装成为一个对象,其根本父类为Throwable。

Throwable有两个子类:Error和Exception。

一个Error对象表示一个程序错误,指的是底层的、低级的、不可恢复的严重错误。此时程序一定会退出,因为已经失去了运行所必须的物理环境。

对于Error错误我们无法进行处理,因为我们是通过程序来应对错误,可是程序已经退出了。

我们可以处理的Throwable对象中只有Exception对象(例外/异常)。

Exception有两个子类:Runtime exception(未检查异常)

非Runtimeexception(已检查异常)

(注意:无论是未检查异常还是已检查异常在编译的时候都不会被发现,在编译的过程中检查的是程序的语法错误,而异常是一个运行时程序出错的概念。)

在Exception中,所有的非未检查异常都是已检查异常,没有另外的异常!!

 

未检查异常是因为程序员没有进行必要的检查,因为他的疏忽和错误而引起的异常。一定是属于虚拟机内部的异常(比如空指针)。

 

应对未检查异常就是养成良好的检查习惯。

已检查异常是不可避免的,对于已检查异常必须实现定义好应对的方法。

已检查异常肯定跨越出了虚拟机的范围。(比如“未找到文件”)

 

如何处理已检查异常(对于所有的已检查异常都要进行处理):

首先了解异常形成的机制:

当一个方法中有一条语句出现了异常,它就会throw(抛出)一个例外对象,然后后面的语句不会执行返回上一级方法,其上一级方法接受到了例外对象之后,有可能对这个异常进行处理,也可能将这个异常转到它的上一级。

对于接收到的已检查异常有两种处理方式:throws和try方法。

 

注意:出错的方法有可能是JDK,也可能是程序员写的程序,无论谁写的,抛出一定用throw。

 

例:public void print() throws Exception.

 

对于方法a,如果它定义了throwsException。那么当它调用的方法b返回异常对象时,方法a并不处理,而将这个异常对象向上一级返回,如果所有的方法均不进行处理,返回到主方法,程序中止。(要避免所有的方法都返回的使用方法,因为这样出现一个很小的异常就会令程序中止)。

 

如果在方法的程序中有一行throw new Exception(),返回错误,那么其后的程序不执行。因为错误返回后,后面的程序肯定没有机会执行,那么JAVA认为以后的程序没有存在的必要。

 

对于try……catch格式:

try  {可能出现错误的代码块}   catch(exception e){进行处理的代码} ;

                                对象变量的声明

 

用这种方法,如果代码正确,那么程序不经过catch语句直接向下运行;

如果代码不正确,则将返回的异常对象和e进行匹配,如果匹配成功,则处理其后面的异常处理代码。(如果用exception来声明e的话,因为exception为所有exception对象的父类,所有肯定匹配成功)。处理完代码后这个例外就完全处理完毕,程序会接着从出现异常的地方向下执行(是从出现异常的地方还是在catch后面呢?利用程序进行验证)。最后程序正常退出。

 

Try中如果发现错误,即跳出try去匹配catch,那么try后面的语句就不会被执行。

一个try可以跟进多个catch语句,用于处理不同情况。当一个try只能匹配一个catch。

我们可以写多个catch语句,但是不能将父类型的exception的位置写在子类型的excepiton之前,因为这样父类型肯定先于子类型被匹配,所有子类型就成为废话。JAVA编译出错。

 

在try,catch后还可以再跟一子句finally。其中的代码语句无论如何都会被执行(因为finally子句的这个特性,所以一般将释放资源,关闭连接的语句写在里面)。

 

如果在程序中书写了检查(抛出)exception但是没有对这个可能出现的检查结果进行处理,那么程序就会报错。

而如果只有处理情况(try)而没有相应的catch子句,则编译还是通不过。

如何知道在编写的程序中会出现例外呢

1.  调用方法,查看API中查看方法中是否有已检查错误。

2.  在编译的过程中看提示信息,然后加上相应的处理。

 

Exception有一个message属性。在使用catch的时候可以调用:

Catch(IOExceptione){System.out.println(e.message())};

Catch(IOExceptione){e.printStackTrace()};

上面这条语句回告诉我们出错类型所历经的过程,在调试的中非常有用。

 

开发中的两个道理:

①如何控制try的范围:根据操作的连动性和相关性,如果前面的程序代码块抛出的错误影响了后面程序代码的运行,那么这个我们就说这两个程序代码存在关联,应该放在同一个try中。

①  对已经查出来的例外,有throw(积极)和try catch(消极)两种处理方法。

对于try catch放在能够很好地处理例外的位置(即放在具备对例外进行处理的能力的位置)。如果没有处理能力就继续上抛。

 

当我们自己定义一个例外类的时候必须使其继承excepiton或者RuntimeException。

Throw是一个语句,用来做抛出例外的功能。

而throws是表示如果下级方法中如果有例外抛出,那么本方法不做处理,继续向上抛出。

Throws后跟的是例外类型。

断言是一种调试工具(assert)

其后跟的是布尔类型的表达式,如果表达式结果为真不影响程序运行。如果为假系统出现低级错误,在屏幕上出现assert信息。

Assert只是用于调试。在产品编译完成后上线assert代码就被删除了。

 

方法的覆盖中,如果子类的方法抛出的例外是父类方法抛出的例外的父类型,那么编译就会出错:子类无法覆盖父类。

结论:子类方法不可比父类方法抛出更多的例外。子类抛出的例外或者与父类抛出的例外一致,或者是父类抛出例外的子类型。或者子类型不抛出例外。

如果父类型无throws时,子类型也不允许出现throws。此时只能使用try catch。

 

练习:写一个方法:int add(int a,int b)

{

  return a+b

}

a+b=100;抛出100为异常处理。

12.02

集合(从本部分开始涉及API)

集合是指一个对象容纳了多个对象,这个集合对象主要用来管理维护一系列相似的对象。

数组就是一种对象。(练习:如何编写一个数组程序,并进行遍历。)

java.util.*定义了一系列的接口和类,告诉我们用什么类NEW出一个对象,可以进行超越数组的操作。

(注:JAVA1.5对JAVA1.4的最大改进就是增加了对范型的支持)

集合框架接口的分类:(分collection接口 和 map接口)

            Collection接口                         Map接口

 

 


List接口         Set接口                       SortedMap接口

 

 


                   SortedSet接口

JAVA中所有与集合有关的实现类都是这六个接口的实现类。

 

Collection接口:集合中每一个元素为一个对象,这个接口将这些对象组织在一起,形成一维结构。

 

List接口代表按照元素一定的相关顺序来组织(在这个序列中顺序是主要的),List接口中数据可重复。

 

Set接口是数学中集合的概念:其元素无序,且不可重复。(正好与List对应)

 

SortedSet会按照数字将元素排列,为“可排序集合”。

 

Map接口中每一个元素不是一个对象,而是一个键对象和值对象组成的键值对(Key-Value)。

Key-Value是用一个不可重复的key集合对应可重复的value集合。(典型的例子是字典:通过页码的key值找字的value值)。

例子:

key1—value1;

key2—value2;

key3—value3.

SortedMap:如果一个Map可以根据key值排序,则称其为SortedMap。(如字典)

!!注意数组和集合的区别:数组中只能存简单数据类型。Collection接口和Map接口只能存对象。

 

以下介绍接口:

List接口:(介绍其下的两个实现类:ArrayList和LinkedList)

ArrayList和数组非常类似,其底层①也用数组组织数据,ArrayList是动态可变数组。

①  底层:指存储格式。说明ArrayList对象都是存在于数组中。

注:数组和集合都是从下标0开始。

ArrayList有一个add(Object o)方法用于插入数组。

ArrayList的使用:(完成这个程序)

先import java.util.*;

用ArrayList在一个数组中添加数据,并遍历。

ArrayList中数组的顺序与添加顺序一致。

只有List可用get和size。而Set则不可用(因其无序)。

Collection接口都是通过Iterator()(即迭代器)来对Set和List遍历。

通过语句:Iterator it=c.iterator(); 得到一个迭代器,将集合中所有元素顺序排列。然后可以通过interator方法进行遍历,迭代器有一个游标(指针)指向首位置。

Interator有hasNext(),用于判断元素右边是否还有数据,返回True说明有。然后就可以调用next动作。Next()会将游标移到下一个元素,并把它所跨过的元素返回。(这样就可以对元素进行遍历)

练习:写一个程序,输入对象信息,比较基本信息。

集合中每一个元素都有对象,如有字符串要经过强制类型转换。

Collections是工具类,所有方法均为有用方法,且方法为static。

有Sort方法用于给List排序。

Collections.Sort()分为两部分,一部分为排序规则;一部分为排序算法。

规则用来判断对象;算法是考虑如何排序。

对于自定义对象,Sort不知道规则,所以无法比较。这种情况下一定要定义排序规则。方式有两种:

①  java.lang下面有一个接口:Comparable(可比较的)

可以让自定义对象实现一个接口,这个接口只有一个方法comparableTo(Object o)

其规则是当前对象与o对象进行比较,其返回一个int值,系统根据此值来进行排序。

如 当前对象>o对象,则返回值>0;(可将返回值定义为1)

如 当前对象=o对象,则返回值=0;

如 当前对象<o对象,则返回值〈0。(可将返回值定义为-1)

看TestArraylist的java代码。

我们通过返回值1和-1位置的调换来实现升序和降序排列的转换。

 

②  java.util下有一个Comparator(比较器)

它拥有compare(),用来比较两个方法。

要生成比较器,则用Sort中Sort(List,List(Compate))

第二种方法更灵活,且在运行的时候不用编译。

 

注意:要想实现comparTo()就必须在主方法中写上implement comparable.

 

练习:生成一个EMPLOYEE类,然后将一系列对象放入到ArrayList。用Iterator遍历,排序之后,再进行遍历。

 

集合的最大缺点是无法进行类型判定(这个缺点在JAVA1.5中已经解决),这样就可能出现因为类型不同而出现类型错误。

解决的方法是添加类型的判断。   

 

LinkedList接口(在代码的使用过程中和ArrayList没有什么区别)

ArrayList底层是object数组,所以ArrayList具有数组的查询速度快的优点以及增删速度慢的缺点。

而在LinkedList的底层是一种双向循环链表。在此链表上每一个数据节点都由三部分组成:前指针(指向前面的节点的位置),数据,后指针(指向后面的节点的位置)。最后一个节点的后指针指向第一个节点的前指针,形成一个循环。

双向循环链表的查询效率低但是增删效率高。所以LinkedList具有查询效率低但增删效率高的特点。

ArrayList和LinkedList在用法上没有区别,但是在功能上还是有区别的。

LinkedList经常用在增删操作较多而查询操作很少的情况下:队列和堆栈。

队列:先进先出的数据结构。

堆栈:后进先出的数据结构。

注意:使用堆栈的时候一定不能提供方法让不是最后一个元素的元素获得出栈的机会。

LinkedList提供以下方法:(ArrayList无此类方法)

addFirst();   

removeFirst();

  addLast();

  removeLast();

在堆栈中,push为入栈操作,pop为出栈操作。

 

Push用addFirst();pop用removeFirst(),实现后进先出。

用isEmpty()--其父类的方法,来判断栈是否为空。

 

在队列中,put为入队列操作,get为出队列操作。

Put用addFirst(),get用removeLast()实现队列。

 

List接口的实现类(Vector)(与ArrayList相似,区别是Vector是重量级的组件,使用使消耗的资源比较多。)

结论:在考虑并发的情况下用Vector(保证线程的安全)。

在不考虑并发的情况下用ArrayList(不能保证线程的安全)。

 

面试经验(知识点):

java.util.stack(stack即为堆栈)的父类为Vector。可是stack的父类是最不应该为Vector的。因为Vector的底层是数组,且Vector有get方法(意味着它可能访问到并不属于最后一个位置元素的其他元素,很不安全)。

对于堆栈和队列只能用push类和get类。

Stack类以后不要轻易使用。

!!!实现堆栈一定要用LinkedList。

(在JAVA1.5中,collection有queue来实现队列。)

 

Set-HashSet实现类:

遍历一个Set的方法只有一个:迭代器(interator)。

HashSet中元素是无序的(这个无序指的是数据的添加顺序和后来的排列顺序不同),而且元素不可重复。

在Object中除了有final(),toString(),equals(),还有hashCode()。

HashSet底层用的也是数组。

当向数组中利用add(Object o)添加对象的时候,系统先找对象的hashCode:

inthc=o.hashCode(); 返回的hashCode为整数值。

Int I=hc%n;(n为数组的长度),取得余数后,利用余数向数组中相应的位置添加数据,以n为6为例,如果I=0则放在数组a[0]位置,如果I=1,则放在数组a[1]位置。如果equals()返回的值为true,则说明数据重复。如果equals()返回的值为false,则再找其他的位置进行比较。这样的机制就导致两个相同的对象有可能重复地添加到数组中,因为他们的hashCode不同。

如果我们能够使两个相同的对象具有相同hashcode,才能在equals()返回为真。

在实例中,定义student对象时覆盖它的hashcode。

因为String类是自动覆盖的,所以当比较String类的对象的时候,就不会出现有两个相同的string对象的情况。

现在,在大部分的JDK中,都已经要求覆盖了hashCode。

结论:如将自定义类用hashSet来添加对象,一定要覆盖hashcode()equals(),覆盖的原则是保证当两个对象hashcode返回相同的整数,而且equals()返回值为True

如果偷懒,没有设定equals(),就会造成返回hashCode虽然结果相同,但在程序执行的过程中会多次地调用equals(),从而影响程序执行的效率。

 

我们要保证相同对象的返回的hashCode一定相同,也要保证不相同的对象的hashCode尽可能不同(因为数组的边界性,hashCode还是可能相同的)。例子:

public int hashCode(){

  return name.hashcode()+age;

}

这个例子保证了相同姓名和年龄的记录返回的hashCode是相同的。

 

使用hashSet的优点:

hashSet的底层是数组,其查询效率非常高。而且在增加和删除的时候由于运用的hashCode的比较开确定添加元素的位置,所以不存在元素的偏移,所以效率也非常高。因为hashSet查询和删除和增加元素的效率都非常高。

但是hashSet增删的高效率是通过花费大量的空间换来的:因为空间越大,取余数相同的情况就越小。HashSet这种算法会建立许多无用的空间。

使用hashSet接口时要注意,如果发生冲突,就会出现遍历整个数组的情况,这样就使得效率非常的低。

 

练习:new一个hashset,插入employee对象,不允许重复,并且遍历出来。

 

添加知识点:

集合对象存放的是一系列对象的引用。

例:

Student S

Al.add(s);

s.setName(“lucy”);

Students2=(Student)(al.get(o1));

可知s2也是s。

 

12.05

 

SortedSet可自动为元素排序。

SortedSet的实现类是TreeSet:它的作用是字为添加到TreeSet中的元素排序。

 

练习:自定义类用TreeSet排序。

与HashSet不同,TreeSet并不需要实现HashCode()和equals()。

只要实现compareable和compareTo()接可以实现过滤功能。

(注:HashSet不调用CompareTo())。

 

如果要查询集合中的数据,使用Set必须全部遍历,所以查询的效率低。使用Map,可通过查找key得到value,查询效率高。

集合中常用的是:ArrayList,HashSet,HashMap。其中ArrayList和HashMap使用最为广泛。

 

使用HashMap,put()表示放置元素,get()表示取元素。

 

遍历Map,使用keySet()可以返回set值,用keySet()得到key值,使用迭代器遍历,然后使用put()得到value值。

上面这个算法的关键语句:

Sets=m.keySet();

Interatorit=new interator();

Objectkey=it.next();

Objectvalue=m.get(key);

 

注意:HashMap与HashCode有关,用Sort对象排序。

如果在HashMap中有key值重复,那么后面一条记录的value覆盖前面一条记录。

 

Key值既然可以作为对象,那么也可以用一个自定义的类。比如:

m.put(newsutdent(“Liucy”,30),”boss”)

如果没有语句来判定Student类对象是否相同,则会全部打印出来。

 

当我们用自定义的类对象作为key时,我们必须在程序中覆盖HashCode()和equals()。

 

注:HashMap底层也是用数组,HashSet底层实际上也是HashMap,HashSet类中有HashMap属性(我们如何在API中查属性)。HashSet实际上为(key.null)类型的HashMap。有key值而没有value值。

 

正因为以上的原因,TreeSet和TreeMap的实现也有些类似的关系。

注意:TreeSet和TreeMap非常的消耗时间,因此很少使用。

我们应该熟悉各种实现类的选择——非常体现你的功底。

 

HashSet VSTreeSet:HashSet非常的消耗空间,TreeSet因为有排序功能,因此资源消耗非常的高,我们应该尽量少使用,而且最好不要重复使用。

基于以上原因,我们尽可能的运用HashSet而不用TreeSet,除非必须排序。

同理:HashMap VS  TreeMap:一般使用HashMap,排序的时候使用TreeMap。

HashMap VSHashtable(注意在这里table的第一个字母小写)之间的区别有些类似于ArrayList和Vector,Hashtable是重量级的组件,在考虑并发的情况,对安全性要求比较高的时候使用。

 

Map的运用非常的多。

 

使用HashMap(),如果使用自定义类,一定要覆盖HashCode()和equals()。

 

重点掌握集合的四种操作:增加、删除、遍历、排序。

 

Module8—12利用两天的时间完成。

Module8:图形界面

Module9:事件模型(在本部分最重要)

Module10:AWT

Module11:Swing

Module12:Applet(这个技术基本已经被淘汰)

 

软件应用的三个发展阶段:

单机应用

网络应用(C/S结构)

BS结构:B表示浏览器,S表示server端。即利用浏览器作为客户端,因此对于图形界面的要求已经不高,现在的发展趋势是不使用安装,即不用任何的本地应用,图形很快就会被服务器构件开发所取代。

 

经验之谈:Swing的开发工作会非常的累,而且这项技术正在走向没落。避免从事有这种特征的工作。

AWT也即将被取代。

Module8—Module11所使用的技术都将被JSF技术所取代。

JSF是服务器端的Swing:目前技术已经成熟,但是开发环境(工具)还不成熟。

Module12的Applet技术也将被WebStart所取代。

Module9为重点,所谓事件模型是指观察者设计模式的JAVA应用。事件模型是重点。

 

Module8:图形界面(java.awt.*)

Awt:抽象窗口工具箱,它由三部分组成:

①组件:界面元素;

②容器:装载组件的容器(例如窗体);

③布局管理器:负责决定容器中组件的摆放位置。

图形界面的应用分四步:

①  选择一个容器:

⑴window:带标题的容器(如Frame);

⑵Panel:面板

通过add()想容器中添加组件。

Java的图形界面依然是跨平台的。但是在调用了一个窗体之后只生成一个窗体,没有事件的处理,关闭按钮并不工作。此时只能使用CTRL+C终止程序。

②设置一个布局管理器:用setLayout();

③向容器中添加组件;

③  添加组件的事务处理。P198

 

P204:Panel也是一种容器:但是不可见的。在设置容易的时候不要忘记设置它们的可见性。

Panel pan=new Panel;

Fp.setLayout(null);表示不要布局管理器。

 

五种布局管理器:

P206:Flow Layout(流式布局):按照组件添加到容器中的顺序,顺序排放组件位置。默认为水平排列,如果越界那么会向下排列。排列的位置随着容器大小的改变而改变。

Panel默认的布局管理器为Flow Layout。

 

Border Layout:会将容器非常五个区域:东西南北中。

语句:

Button b1=new Botton(“north”);//botton上的文字

f.add(b1,”North”);//表示b1这个botton放在north位置

注:一个区域只能放置一个组件,如果想在一个区域放置多个组件就需要使用Panel来装载。

Frame和Dialog的默认布局管理器是Border Layout。

 

Grid Layout:将容器生成等长等大的条列格,每个块中放置一个组件。

f.setLayout GridLayout(5,2,10,10)//表示条列格为5行2类,后面为格间距。

 

CardLayout:一个容器可以放置多个组件,但每次只有一个组件可见(组件重叠)。

使用first(),last(),next()可以决定哪个组件可见。可以用于将一系列的面板有顺序地呈现给用户。

 

重点:GridBag Layout:在Grid中可指定一个组件占据多行多列,GridBag的设置非常的烦琐。

 

Module9:AWT:事件模型

事件模型指的是对象之间进行通信的设计模式。

对象1给对象2发送一个信息相当于对象1引用对象2的方法。

模型即是一种设计模式(约定俗成)

对象对为三种:

①事件源:发出事件者;

②事件对象:发出的事件本身;

④  事件监听器:提供处理事件指定的方法。

 

Java AWT事件模型也称为授权事件模型,指事件可以和监听器之间事先建立一种关系:约定那些事件如何处理,由谁去进行处理。这种约定称为授权。

一个事件源可以授权多个监听者(授权也称为监听者的注册);

多个事件源也可以注册多个事件监听器。

监听者对于事件源的发出的事件作出响应。

 

在java.util中有EventListener接口:所有事件监听者都要实现这个接口。

java.util中有EventObject类:所有的事件都为其子类。

 

事件范例在\CoreJava\Girl.java文件中。(文件已加注释)                                          

 

注意:接口因对不同的事件监听器对其处理可能不同,所以只能建立监听的功能,而无法实现处理。

 

下面程序建立监听功能:

//监听器接口要定义监听器所具备的功能,定义方法

{

  void WhatIdoWhenGirlHappy(EmotionEvent e);

void WhatIdoWhenGirlSad(EmotionEvente);

}

注意查看参考书:事件的设置模式,如何实现授权模型。

 

事件模式的实现步骤:

开发事件对象(事件发送者)——接口——接口实现类——设置监听对象

一定要理解透彻Gril.java程序。

 

重点:学会处理对一个事件源有多个事件的监听器(在发送消息时监听器收到消息的排名不分先后)。

事件监听的响应顺序是不分先后的,不是谁先注册谁就先响应。

事件监听由两个部分组成(接口和接口的实现类)。

事件源     事件对象                        事件监听

 gril      EmotinEvent       EmotionListener(接口)、Boy(接口的实现类)

鼠标事件:MouseEvent,接口:MouseListener。

P235 ActionEvent。

注意在写程序的时候:import java.awt.*;以及import java.awt.event.*注意两者的不同。

 

在生成一个窗体的时候,点击窗体的右上角关闭按钮激发窗体事件的方法:窗体Frame为事件源,WindowsListener接口调用Windowsclosing()。

为了配合后面的实现,我们必须将WindowsListener所有的方法都实现,除了Windowsclosing方法,其余的方法均为空实现。

(练习:写一个带button窗体,点关闭按钮退出。)

上面程序中实现了许多不必要的实现类,虽然是空实现。

为了避免上面那些无用的实现,可以利用WindowEvent的一个WindowEvent类,还是利用windowsListener。还有WindowAdapter类,它已经实现了WindowsListener。它给出的全部都是空实现,那就可以只写想要实现的类,去覆盖其中的类,就不用写空实现。

注意:监听过多,会抛tooManyListener例外。

12.06

Module  10

Canvas组件:画布,可以实现动画操作。

TextArea:文本域。

在单行文本域中回车会激发ActionEvent。

用CheckBoxGroup实现单选框功能。

Java中,单选框和复选框都是使用CheckBox实现。

菜单:new MenuBar(),MenuBar表示菜单条。

菜单中的每一项为MenuItem,一般级联菜单不应该超过三级。

练习:

设计一个计算器:注意设置一个boolean值(append)来判断输入数字是位于第一个数的后面还是属于输入的第二个数。

设置一个变量来存放“+”,点完运算符后,将append设置为false。

String number1

Char  operator 存放运算符。

 

Module  11 Swing

AWT是Java最早出现的图形界面,但很快就被Swing所取代。

Swing才是一种真正的图形开发。

AWT在不同平台所出现的界面可能有所不同:因为每个OS都有自己的UI组件库,java调用不同系统的UI。

注意AWT为重量级组件,相当消耗资源,且不同系统的组件可能不同。因为这个问题使得AWT开发的软件难以作到跨平台。
更为要命的是:不同OS的组件库都存在BUG。必须多种平台进行测试,并且AWT的组件库并不丰富。

为解决以上问题,SUN和IBM以及NETSCAPE联合开发出JAVA基础类包Swing:注意JAVA的基础类以Swing为核心。

注意引用:javax.swing.*;javax表示JAVA的扩展。

 

我们在学习JDBC的时候会过度到J2EE。

 

在Swing的组件中,基本上都是在AWT组件的名称前面加“J”。

一般情况下,除了Choise等组件:

importjavax.swing.*;好要加上:import java.awt.*以及import java.awt.event.*。

Swing与AWT的最大区别是Swing为JAVA自身的组件。已经不是对等实体,与底层的OS无关。

(JBUILDER就是使用Swing写的)

 

Swing与AWT在事件模型处理上是一致的。

Jframe实际上是一堆窗体的叠加。

Swing比AWT更加复杂且灵活。

在JDK1.4中,给JFRAME添加Button不可用jf.add(b)。而是使用jf.getContentPane().add(b)。

content是先申请面板。不过在JDK1.5中可以使用add.。

Jpanel支持双缓冲技术。

在Jbutton中可以添加图标。

JscrollPane可以管理比屏幕还要大的组件。

TextArea只有装入JscrollPane中才能实现滚动条。

JeditorPane用于显示浏览器。

注意:Tabbed Panel与Border的比较。

进度条:ProgressBar。

JcomboBox:下拉菜单:在AWT中同类组件是choice。

JlistPanel:选择列表

BorderPanel:设置边框

JsplitPanel:可将容器分为两个部分,其中一个部分有Jtree。

TextBox:也是一种新的容器,可以设置组件的间距。

TextFileChoose:文件选择器。

ColorChoose:颜色选择器

Module 12  Applet

Applet为Panel的子类

Applet是java的自动执行方式(这是它的优势,主要用于HTML)。

工作四种语法:init(),start(),stop(),destory()。

Swing中有一个Japplet,如使用Swing组件。

Applet消亡的原因:

①java为安全起见对Applet有所限制:Applet不允许访问本地文件信息、敏感信息,不能执行本地指令(比如FORMAT),不能访问初原服务器之外的其他服务器。

②  IE不支持新版本的Applet。

Applet的优势:

网络传输,自动下载。

Application的优势:没有执行限制。

WebStart:可在网络传输,并且在本地无限制。因此前景光明。

 

练习:

使用Swing实现一个界面,分为上下两个部分,南边为JtextField组件,可编辑,上面为JtextArea组件,不可编辑,在JtextField组件输入字符,按回车,就可以将内容输入到JtextArea组件。(AREA区域可以滚动)

 

12.07

多线程

进程:任务

任务并发执行是一个宏观概念,微观上是串行的。

进程的调度是有OS负责的(有的系统为独占式,有的系统为共享式,根据重要性,进程有优先级)。

 

由OS将时间分为若干个时间片。

JAVA在语言级支持多线程。

分配时间的仍然是OS。

参看P377

 

线程由两种实现方式:

第一种方式:

class MyThreadextends Thread{

   public void run(){

   需要进行执行的代码,如循环。

}

}

 

public classTestThread{

  main(){

   Thread t1=new Mythread();

   T1.start();

}

}

 

只有等到所有的线程全部结束之后,进程才退出。

 

第二种方式:

ClassMyThread  implements Runnable{

 Public  void run(){

 Runnable target=new MyThread();

 Thread t3=new Thread(target);

 Thread.start();//启动线程

}

}

P384:通过接口实现继承

 

练习:写两个线程:

①  输入200个“###”②输入200个“***”

 

下面为线程中的7中非常重要的状态:(有的书上也只有认为前五种状态:而将“锁池”和“等待队列”都看成是“阻塞”状态的特殊情况:这种认识也是正确的,但是将“锁池”和“等待队列”单独分离出来有利于对程序的理解)

 

 


                  ①              ⑴

                 ②                   ⑵

                ③                        ⑶             run()结束

    Start()

                           OS分配CPU

 


                        CPU时间片结束

                             yield()                      o.wait()

                                            等待锁标记

 

 


                                                notify()

注意:图中标记依次为

①输入完毕;②wake up③t1退出

⑴如等待输入(输入设备进行处理,而CUP不处理),则放入阻塞,直到输入完毕。

⑵线程休眠sleep()

⑶t1.join()指停止main(),然后在某段时间内将t1加入运行队列,直到t1退出,main()才结束。

特别注意:①②③与⑴⑵⑶是一一对应的。

 

进程的休眠:Thread sleep(1000);//括号中以毫秒为单位

当main()运行完毕,即使在结束时时间片还没有用完,CPU也放弃此时间片,继续运行其他程序。

Try{Thread.sleep(1000);}

Catch(Exceptione){e.printStackTrace(e);}

T1.join()表示运行线程放弃执行权,进入阻塞状态。

当t1结束时,main()可以重新进入运行状态。

T1.join实际上是把并发的线程编程并行运行。

线程的优先级:1-10,越大优先级越高,优先级越高被OS选中的可能性就越大。(不建议使用,因为不同操作系统的优先级并不相同,使得程序不具备跨平台性,这种优先级只是粗略地划分)。

注:程序的跨平台性:除了能够运行,还必须保证运行的结果。

 

一个使用yield()就马上交出执行权,回到可运行状态,等待OS的再次调用。

 

下午:

程序员需要关注的线程同步和互斥的问题。

多线程的并发一般不是程序员决定,而是由容器决定。

多线程出现故障的原因:

两个线程同时访问一个数据资源(临界资源),形成数据发生不一致和不完整。

数据的不一致往往是因为一个线程中的两个关联的操作只完成了一步。

 

避免以上的问题可采用对数据进行加锁的方法

每个对象除了属性和方法,都有一个monitor(互斥锁标记),用来将这个对象交给一个线程,只有拿到monitor的线程才能够访问这个对象。

Synchronized:这个修饰词可以用来修饰方法和代码块

 

Object obj;

Obj.setValue(123);

Synchronized用来修饰方法,表示当某个线程调用这个方法之后,其他的事件不能再调用这个方法。只有拿到obj标记的线程才能够执行代码块。

注意:Synchronized一定使用在一个方法中。

锁标记是对象的概念,加锁是对对象加锁,目的是在线程之间进行协调。

 

当用Synchronized修饰某个方法的时候,表示该方法都对当前对象加锁。

给方法加Synchronized和用Synchronized修饰对象的效果是一致的。

 

一个线程可以拿到多个锁标记,一个对象最多只能将monitor给一个线程。

Synchronized是以牺牲程序运行的效率为代价的,因此应该尽量控制互斥代码块的范围。

 

方法的Synchronized特性本身不会被继承,只能覆盖。

 

线程因为未拿到锁标记而发生的阻塞不同于前面五个基本状态中的阻塞,称为锁池。

每个对象都有自己的一个锁池的空间,用于放置等待运行的线程。

这些线程中哪个线程拿到锁标记由系统决定。

 

锁标记如果过多,就会出现线程等待其他线程释放锁标记,而又都不释放自己的锁标记供其他线程运行的状况。就是死锁。

死锁的问题通过线程间的通信的方式进行解决。

线程间通信机制实际上也就是协调机制。

线程间通信使用的空间称之为对象的等待队列,则个队列也是属于对象的空间的。

Object类中又一个wait(),在运行状态中,线程调用wait(),此时表示着线程将释放自己所有的锁标记,同时进入这个对象的等待队列。

等待队列的状态也是阻塞状态,只不过线程释放自己的锁标记。

Notify()

如果一个线程调用对象的notify(),就是通知对象等待队列的一个线程出列。进入锁池。如果使用notifyall()则通知等待队列中所有的线程出列。

 

注意:只能对加锁的资源进行wait()和notify()。

 

释放锁标记只有在Synchronized代码结束或者调用wait()。

注意锁标记是自己不会自动释放,必须有通知。

注意在程序中判定一个条件是否成立时要注意使用WHILE要比使用IF要严密。

WHILE会放置程序饶过判断条件而造成越界。

补充知识:

suspend()是将一个运行时状态进入阻塞状态(注意不释放锁标记)。恢复状态的时候用resume()。Stop()指释放全部。

这几个方法上都有Deprecated标志,说明这个方法不推荐使用。

 

一般来说,主方法main()结束的时候线程结束,可是也可能出现需要中断线程的情况。对于多线程一般每个线程都是一个循环,如果中断线程我们必须想办法使其退出。


如果主方法main()想结束阻塞中的线程(比如sleep或wait)

那么我们可以从其他进程对线程对象调用interrupt()。用于对阻塞(或锁池)会抛出例外InterruptedException。

这个例外会使线程中断并执行catch中代码。

 

多线程中的重点:实现多线程的两种方式,Synchronized,以及生产者和消费者问题(ProducerConsumer.java文件)。

 

练习:

①  存车位的停开车的次序输出问题;

②  写两个线程,一个线程打印1-52,另一个线程答应字母A-Z。打印顺序为12A34B56C……5152Z。通过使用线程之间的通信协调关系。

注:分别给两个对象构造一个对象o,数字每打印两个或字母每打印一个就执行o.wait()。在o.wait()之前不要忘了写o.notify()。

 

补充说明:通过Synchronized,可知Vector较ArrayList方法的区别就是Vector所有的方法都有Synchronized。所以Vector更为安全。

同样:Hashtable较HashMap也是如此。

12.08

Module 10:I/O流(java如何实现与外界数据的交流)

Input/Output:指跨越出了JVM的边界,与外界数据的源头或者目标数据源进行数据交换。

              输出

 

 


              输入                  

 注意:输入/输出是针对JVM而言。

File类(java.io.*)可表示一个文件,也有可能是一个目录(在JAVA中文件和目录都属于这个类中,而且区分不是非常的明显)。

Java.io下的方法是对磁盘上的文件进行磁盘操作,但是无法读取文件的内容。

注意:创建一个文件对象和创建一个文件在JAVA中是两个不同的概念。前者是在虚拟机中创建了一个文件,但却并没有将它真正地创建到OS的文件系统中,随着虚拟机的关闭,这个创建的对象也就消失了。而创建一个文件才是在系统中真正地建立一个文件。

例如:File f=new File(“11.txt”);//创建一个名为11.txt的文件对象

f.CreateNewFile();    //真正地创建文件

 

f.CreateMkdir():创建目录

f.delete();删除文件

f.deleteOnExit();在进程退出的时候删除文件,这样的操作通常用在临时文件的删除。

 

对于命令:File f2=new file(“d:\\abc\\789\\1.txt”)

这个命令不具备跨平台性,因为不同的OS的文件系统很不相同。

如果想要跨平台,在file类下有separtor(),返回锁出平台的文件分隔符。

File.fdir=newFile(File.separator);

Stringstr=”abc”+File.separator+”789”;

使用文件下的方法的时候一定注意是否具备跨平台性。

 

List():显示文件的名(相对路径)

ListFiles():返回Files类型数组,可以用getName()来访问到文件名。

使用isDirectory()和isFile()来判断究竟是文件还是目录。

 

练习:

写一个javaTest程序,列出所有目录下的*.java文件,把子目录下的JAVA文件也打印出来。

 

使用I/O流访问file中的内容。

JVM与外界通过数据通道进行数据交换。

分类:

按流分为输入流和输出流;

按传输单位分为字节流和字符流;

还可以分为节点流和过滤流。

节点流:负责数据源和程序之间建立连接;

过滤流:用于给节点增加功能。

过滤流的构造方式是以其他流位参数构造(这样的设计模式称为装饰模式)。

 

字节输入流:io包中的InputStream为所有字节输入流的父类。

Int read();读入一个字节(每次一个);

可先使用new  byte[]=数组,调用read(byte[] b)

read (byte[])返回值可以表示有效数;read(byte[])返回值为-1表示结束。

 

字节输出流:io包中的OutputStream位所有字节输入流的父类。

Write和输入流中的read相对应。

 

在流中close()方法由程序员控制。因为输入输出流已经超越了VM的边界,所以有时可能无法回收资源。

原则:凡是跨出虚拟机边界的资源都要求程序员自己关闭,不要指望垃圾回收。

以Stream结尾的类都是字节流。

如果构造FileOutputStream的同时磁盘会建立一个文件。如果创建的文件与磁盘上已有的文件名重名,就会发生覆盖。

用FileOutputStream中的boolean,则视,添加情况,将数据覆盖重名文件还是将输入内容放在文件的后面。(编写程序验证)

 

DataOutputStream:输入数据的类型。

因为每中数据类型的不同,所以可能会输出错误。

所有对于:DataOutputStream

         DataInputStream

          两者的输入顺序必须一致。

过滤流:

 bufferedOutputStream

 bufferedInputStream

用于给节点流增加一个缓冲的功能。
在VM的内部建立一个缓冲区,数据先写入缓冲区,等到缓冲区的数据满了之后再一次性写出,效率很高。

使用带缓冲区的输入输出流的速度会大幅提高,缓冲区越大,效率越高。(这是典型的牺牲空间换时间)

切记:使用带缓冲区的流,如果数据数据输入完毕,使用flush方法将缓冲区中的内容一次性写入到外部数据源。用close()也可以达到相同的效果,因为每次close都会使用flush。一定要注意关闭外部的过滤流。

 

(非重点)管道流:也是一种节点流,用于给两个线程交换数据。

PipedOutputStream

PipedInputStream

输出流:connect(输入流)

 

RondomAccessFile类允许随机访问文件

GetFilepoint()可以知道文件中的指针位置,使用seek()定位。

Mode(“r”:随机读;”w”:随机写;”rw”:随机读写)

练习:写一个类A,JAVAA file1 file2

file1要求是系统中已经存在的文件。File2是还没有存在的文件。

执行完这个命令,那么file2就是file1中的内容。

 

字符流:reader\write只能输纯文本文件。

FileReader类:字符文件的输出

 

字节流与字符流的区别:

字节流的字符编码:

字符编码把字符转换成数字存储到计算机中,按ASCii将字母映射为整数。

把数字从计算机转换成相应的字符的过程称为解码。

编码方式的分类:

ASCII(数字、英文):1个字符占一个字节(所有的编码集都兼容ASCII)

ISO8859-1(欧洲):1个字符占一个字节

GB-2312/GBK:1个字符占两个字节

Unicode: 1个字符占两个字节(网络传输速度慢)

UTF-8:变长字节,对于英文一个字节,对于汉字两个或三个字节。

 

原则:保证编解码方式的统一,才能不至于出现错误。

Io包的InputStreamread称为从字节流到字符流的桥转换类。这个类可以设定字符转换方式。

OutputStreamred:字符到字节

Bufferread有readline()使得字符输入更加方便。

在I/O流中,所有输入方法都是阻塞方法。

Bufferwrite给输出字符加缓冲,因为它的方法很少,所以使用父类printwrite,它可以使用字节流对象,而且方法很多。

 

练习:做一个记事本

swing/JfileChoose:getSelect file()

InputStreeamReader:把字节变为字符

JAVA中对字符串长无限制  bufferedReader(ir)

 

 

 

 

 

 

 

12.09

classObjectOutputStream也是过滤流,使节点流直接获得输出对象。

最有用的方法:WriteObject(Object b)

用流传输对象称为对象的序列化,但并不使所有的对象都可以进行序列化的。只有在实现类时必须实现一个接口:IO包下的Serializable(可序列化的)。此接口没有任何的方法,这样的接口称为标记接口。

Class Studentimplements Serializable

把对象通过流序列化到某一个持久性介质称为对象的可持久化。

Hibernate就是研究对象的可持久化。

ObuectInputStream  in =new  ObjectInputStream;

Objecto1=in.readObuect();

Students1=(Student)o1;

注意:因为o1是一个对象,因为需要对其进行保存。

 

Transient用来修饰属性。

 Transient int num;

表示当我们对属性序列化时忽略这个属性(即忽略不使之持久化)。

所有属性必须都是可序列化的,特别是当有些属性本身也是对象的时候,要尤其注意这一点。

判断是否一个属性或对象可序列化:Serialver。

Serialver TestObject(TestObject必须为已经编译)

执行结果:如果不可序列化;则出现不可序列化的提示。如果可以序列化,那么就会出现序列化的ID:UID。

java.until.*有

StringTokenizer(参数1,参数2)按某种符号隔开文件

StringTokenizer(s,”:”)用“:”隔开字符,s为对象。

 

练习:将一个类序列化到文件,然后读出。下午:

1、  网络基础知识

2、  JAVA网络编程

 

网络与分布式集群系统的区别:每个节点都是一台计算机,而不是各种计算机内部的功能设备。

Ip:具有全球唯一性,相对于internet,IP为逻辑地址。

端口(port):一台PC中可以有65536个端口,进程通过端口交换数据。连线的时候需要输入IP也需要输入端口信息。

计算机通信实际上的主机之间的进程通信,进程的通信就需要在端口进行联系。

192.168.0.23:21

协议:为了进行网络中的数据交换(通信)而建立的规则、标准或约定。

不同层的协议是不同的。

 

网络层:寻址、路由(指如何到达地址的过程)

传输层:端口连接

TCP模型:应用层/传输层/网络层/网络接口

端口是一种抽象的软件结构,与协议相关:TCP23端口和UDT23端口为两个不同的概念。

端口应该用1024以上的端口,以下的端口都已经设定功能。

套接字(socket)的引入:

Ip+Port=Socket(这是个对象的概念。)

Socket为传输层概念,而JSP是对应用层编程。例:

java.net.*;

(Server端定义顺序)

ServerSocket(intport)

Socket.accept();//阻塞方法,当客户端发出请求是就恢复

如果客户端收到请求:

则Socket SI=ss.accept();

注意客户端和服务器的Socket为两个不同的socket。

Socket的两个方法:

getInputStream():客户端用

  getOutputStream() 服务器端用

使用完毕后切记Socket.close(),两个Socket都关,而且不用关内部的流。

在client端,Sockets=new Socket(“127.0.0.1”,8000);

127.0.0.1为一个默认本机的地址。

练习:

1、  客户端向服务器发出一个字符串,服务器转换成大写传回客户端。

大写的函数:String.toUpperCase()

2、  服务器告诉客户端:“自开机以来你是第n 个用户”。

12.12

UDP编程:

DatagramSocket(邮递员):对应数据报的Socket概念,不需要创建两个socket,不可使用输入输出流。

DatagramPacket(信件):数据包,是UDP下进行传输数据的单位,数据存放在字节数组中。

UDP也需要现有Server端,然后再有Client端。

两端都是DatagramPacket(相当于电话的概念),需要NEW两个DatagramPacket。

InetAddress:网址

这种信息传输方式相当于传真,信息打包,在接受端准备纸。

模式:

发送端:Server:

DatagramPacket  inDataPacket=new DatagramPacket ((msg,msg.length);InetAdress.getByName(ip),port);

接收端:

clientAddress=inDataPack.getAddress();//取得地址

clientPort=inDataPack.getPort();//取得端口号

datagramSocket.send;  //Server

datagramSocket.accept;  //Client

URL:在应用层的编程

注意比较:

http://Localhost:8080/directory  //查找网络服务器的目录

file://directory                 //查找本地的文件系统

java的开发主要以http为基础。

反射:主要用于工具和框架的开发。

反射是对于类的再抽象;通过字符串来抽象类。

JAVA类的运行:classLoader:加载到虚拟机(vm)

Vm中只能存储对象(动态运行时的概念),.class文件加载到VM上就成为一个对象,同时初始静态成员及静态代码(只执行一次)。

Lang包下有一个类为Class:在反射中使用。此类中的每个对象为VM中的类对象,每个类都对应类类的一个对象(class.class)。

例:对于一个Object类,用getClass()得到其类的对象,获得类的对象就相当于获得类的信息,可以调用其下的所有方法,包括类的私有方法。

注意:在反射中没有简单数据类型,所有的编译时类型都是对象。

反射把编译时应该解决的问题留到了运行时。

你可能感兴趣的:(java,exception,vector,swing,HashMap,equals)