java学习笔记4

  CoreJava第十天     2007510

 

一、复习内部类

 1、成员内部类

    可以访问外部类的私有成员,外部类类名.this.属性

    构造成员内部类对象,必须先构造一个外部类对象,外部类对象.new 构造内部类对象

 2、静态内部类

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

    构造静态内部类对象时,不再需要构造外部类对象

 3、局部内部类

    在外部类方法内定义的内部类

    不仅能访问外部类的私有成员,而且还能访问外部类的局部变量,但是要求局部变量是final

 4、匿名内部类

    局部内部类,用于实现一个借口或者继承一个类,只会构造一次

 

 内部类的作用:

    访问外部类的私有成员,不破坏封装。可以给编程带来一些方便

    我们可以把接口公开,把接口的实现类以内部类的形式隐藏起来。强制用户通过接口来实现弱耦合

    接口+内部类实现多继承

 

二、List接口的实现类

1ArrayList

   底层使用数组实现

2Vector

  ArrayList  轻量级  线程不安全

  Vector     重量级  线程安全的

3LinkedList

  底层使用双向循环链表实现

  ArrayList   数组 查询快 增删操作慢

  LinkedList  链表 查询慢 增删操作快  

  使用组合复用实现栈

 

三、Set接口

1HashSet

Set的实现类的集合对象中不能够有重复元素,HashSet也一样是使用了一种标识来确定元素的不重复,是元素内容不重复

HashSet用一种算法来保证集合中的元素是不重复的,HashSet的底层实现还是数组。

Object类中的hashCode()的方法是所有子类都会继承这个方法,这个方法会用Hash算法算出一个Hash(哈希)码值返回,HashSet会用Hash码值去和数组长度取模,

模(这个模就是对象要存放在数组中的位置)相同时才会判断数组中的元素和要加入的对象的内容是否相同,如果不同才会添加进去。

 

Hash算法是一种散列算法。

 

注意:所有要存入HashSet的集合对象中的自定义类必须覆盖hashCode(),equals()两个方法,才能保证集合中元素容不重复。

      在覆盖hashCode()方法时,要使相同对象的hashCode()方法返回相同值,覆盖equals()方法再判断其内容。为了保证效率,所以在覆盖hashCode()方法时,也要尽量使不同对象尽量返回不同的Hash码值。

 

如果数组中的元素和要加入的对象的hashCode()返回了相同的Hash值(相同对象返回相同整数),才会用equals()方法来判断两个对象的内容是否相同(不同对象返回不同整数)。

 

练习:

把若干Employee对象放在Set中并遍历,要求没有重复元素

 

2SortedSet接口是Set的子接口。

TreeSetSortedSet接口的实现类,他可以对集合中的元素进行排序。

要存放在TreeSet中自定义类的对象,这个类要么是已经实现了Comparable接口,要么是能给出Comparator比较器,

TreeSet可以自动过滤掉重复元素所以不用重载hashCode()方法,TreeSet会根据比较规则判断元素内容是否相同,TreeSet会在元素存入时就进行了排序。

判断对象重复的依据:compareTo()方法的返回值为,就是重复元素

(在TreeSet给出排序规则时,一定要注意对象内容相等的条件,一定要注意在主观的认为两个对象内容相同时,才可以使用比较少的条件来进行判断)

 

在要排序时才使用TreeSet类(存储效率比较低),HashSet的存储效率比较高,在需要为HashSet的对象排序时,就可以把HashSet中的元素放入TreeSet

 

四、Map

Map中只可以存放键值对(Keyvalue),其中Key是不可以重复的。Keyvalue是一一对应的。

 

HashMap,是Map接口的实现类,Key时无序存放的,其中Key是不可以重复的,它也是通过Hash码值来保证Key不重复的,Keyvalue是一一对应的。

如果要加入的键值对和HashMap中键值对的Key是相同的就会将这个集合中的Key所队应的value值进行覆盖,在使用自定义类型作为Key时,那就是要覆盖hashCode(),equals()方法,也就是和HashSet中要放入自定义类型是的处理方法相同。

这个类的对象是线程不安全的。

 

遍历:(1vlaues() 返回所有值(value)的集合,是一个Collection

      2keySet() 返回所有键对象的集合,是一个Set

          过遍历这个Set,用get()方法来获得Key所对应的value,也就遍历了Map

 

Hashtable,也是Map接口的实现类,他和HashMap比较相似,只不过这个类对象是重量级的,也是线程安全的。他不允许Keyvaluenull

 

Properties,这个类是Hashtable的子类,他的Keyvalue只能是字符串。

 

SortedMapMap的子接口

TreeMap,是SortedMap的实现类,他会按照Key进行排序。和TreeSet类一样,在使用自定义类作Key时,要用自定义类实现Comparable接口。

 

 

 

练习:

达内希望在学生毕业的时候统计出学生在校期间考试成绩的排名,写一个Student,其中用集合来管理每个学生的各个科目的考试成绩,

将多个Student对象放在集合中,打印出学生的总分以及排名

 

(集合)

改写Bank,采用集合的方式来管理多个Account对象

Bank类添加一个方法

打印所有用户的总资产排名

说明:一个用户可能会有多个账号,以身份证号为准.总资产指多个账户余额的总和,不需要考虑贷款账户的贷款额

 

考试系统

Exam   考试类

属性: 若干学生  一张考卷

提示:学生采用HashSet存放

 

Paper   考卷类 

属性:若干试题

提示:试题采用HashMap存放,keyString,表示题号,value为试题对象

 

Student     学生类

属性:姓名   一张答卷   一张考卷

 

Question    试题类

属性:题号 题目描述    若干选项    正确答案(多选)

提示:若干选项用ArrayList

 

AnswerSheet    答卷类

属性:每道题的答案   

提示:答卷中每道题的答案用HashMap存放,keyString,表示题号,value为学生的答案

 

问题:为Exam类添加一个方法,用来为所有学生判卷,并打印成绩排名(名次、姓名)

 

 

                         CoreJava第十一天      2007511

一、复习

集合:用一个对象储存管理多个对象

 

Collection:元素都是对象

遍历:迭代遍历

 

List:元素有顺序,可以重复

      遍历:还可以用for循环(下标)

      排序:Collections.sortlist

实现类:

ArrayList:底层数组实现,查询快,而增删慢;轻量级,线程不安全

Vector:底层数组实现,重量级,线程安全

LinkedList:底层链表实现,查询慢,增删快

Java中用LinkedList实现一个栈,不用数组,因为栈的主要功能就是增删,数组慢;不用Vector,因为效率低

 

Set:元素无序,元素内容不重复

SortedSet:按照各种排序规则给Set排序

 

实现类:

HashSet:采用哈希算法保证元素不重复,覆盖hashCode()保证哈希码相同,equals()保证true

TreeSet:元素一定要实现了Comparable接口的

         根据排序规则,compareTo()返回,说明是重复元素

 

Map:元素是键值对

     key  无序,不重复       value 可以重复

SortedMap:按照key排序

遍历:values() 遍历所有的值对象

      keySet() 遍历所有的键对象

实现类:

HashMap:线程不安全  允许null作为keyvalue

Hashtable:线程安全,不允许

TreeSetSortedSet的实现类

 

二、java中的图形界面

 

GUI,图形化的用户接口,为了人机交互使用的。

 

BSCS的联系与区别。

C/SClient/Server的缩写。服务器通常采用高性能的PC、工作站或小型机,并采用大型数据库系统,如OracleSybaseInformix SQL Server。客户端需要安装专用的客户端软件。

B/S是Brower/Server的缩写,客户机上只要安装一个浏览器(Browser),如Netscape NavigatorInternet Explorer,服务器安装OracleSybaseInformix SQL Server等数据库。

在这种结构下,用户界面完全通过WWW浏览器实现,一部分事务逻辑在前端实现,但是主要事务逻辑在服务器端实现。浏览器通过Web Server 同数据库进行数据交互。

C/S  B/S 区别:

1.硬件环境不同:

C/S 一般建立在专用的网络上小范围里的网络环境局域网之间再通过专门服务器提供连接和数据交换服务.

B/S 建立在广域网之上的不必是专门的网络硬件环境,例与电话上网租用设备信息自己管理有比C/S更强的适应范围一般只要有操作系统和浏览器就行

2.对安全要求不同

C/S 一般面向相对固定的用户群对信息安全的控制能力很强一般高度机密的信息系统采用C/S 结构适宜可以通过B/S发布部分可公开信息.

B/S 建立在广域网之上对安全的控制能力相对弱可能面向不可知的用户。

3.对程序架构不同

C/S 程序可以更加注重流程可以对权限多层次校验对系统运行速度可以较少考虑.

B/S 对安全以及访问速度的多重的考虑建立在需要更加优化的基础之上C/S有更高的要求 B/S结构的程序架构是发展的趋势MS.Net系列的BizTalk 2000 Exchange 2000,

     全面支持网络的构件搭建的系统. SUN IBM推的JavaBean 构件技术等,使 B/S更加成熟.

4.软件重用不同

C/S 程序可以不可避免的整体性考虑构件的重用性不如在B/S要求下的构件的重用性好.

B/S 对的多重结构,要求构件相对独立的功能能够相对较好的重用.就入买来的餐桌可以再利用,而不是做在墙上的石头桌子

5.系统维护不同?

C/S 程序由于整体性必须整体考察处理出现的问题以及系统升级升级难可能是再做一个全新的系统

B/S 构件组成,方面构件个别的更换,实现系统的无缝升级系统维护开销减到最小.用户从网上自己下载安装就可以实现升级.

6.处理问题不同

C/S 程序可以处理用户面固定并且在相同区域安全要求高需求与操作系统相关应该都是相同的系统

B/S 建立在广域网上面向不同的用户群分散地域这是C/S无法作到的与操作系统平台关系最小.

7.用户接口不同

C/S 多是建立的Window平台上,表现方法有限,对程序员普遍要求较高

B/S 建立在浏览器上有更加丰富和生动的表现方式与用户交流并且大部分难度减低,减低开发成本.

8.信息流不同

C/S 程序一般是典型的中央集权的机械式处理交互性相对低

B/S 信息流向可变化, B-B B-C B-G等信息、流向的变化更像交易中心。

 

构造图形界面的步骤

1,选择一个容器

2,设置容器的布局管理器

3,向容器添加组件

4,事件的监听

 

容器(Container)用于管理其他的组件的对象。组件必须放到容器里。

JFrame,这是一个最顶层的窗体容器,所有其他的组件必须放在顶层容器里。

   JFrame frame = new JFrame("Hello Swing");  //创建窗体,字符串为窗体的标题

   frame.setSize(500,300);  //设置窗口大小,500像素长,300像素高

   frame.setVisible(true);  //设置可见性

JPanel,他不是顶层容器,必须放在顶层容器中,任何一个容器都有add()方法,Panel面板是透明的(默认)。他也是一个组件。

JDialog 对话框容器,他要依附于其父组件,他不是一个顶层容器。

 

 

布局管理:对于任何一个容器类中都有setLayout()方法,用容器对象调用这个方法,来设置容器的布局管理器(LayoutManager这是一个接口,所有布局管理器都实现了这个接口)。

 

可用的布局管理器:

所有的布局管理器实现了一个接口java.awt.LayoutManager

FlowLayout,流式布局管。尝试在一行中按增加顺序摆放组件,窗体大小改变时,组件位置会相应发生改变

    Panel的默认布局管理就是FlowLayout

    FlowLayout flow = new FlowLayout();

     frame.setLayout(flow);

BorderLayout,按方位进行布局管理,(NorthSouthEastWestMiddle)不明确指定,就会默认加载在中间(Middle),每个部分只能放一个组件

    frame.add(Component comp,String place);这个方法是在指定的位置添加组件。

    JFrame的默认布局管理器

GridLayout,网格布局,通过行列,间距,来用网格分割,把组件放入如网格中,先行后列摆放组件。可以保证每个组件的大小都是一样的

    frame.setLayout(new GirdLayout(3,2));  //把容器平均的分为32列,先左后右,先上到下的顺序排列

CardLayout,卡片布局,组件重叠放置。

GridBagLayout,组件可以跨行跨列的网格布局。

 

*** 注意:一定要在图形界面都其他功能都设置好之后才能设置可见性。

 

JButton :按钮

JTextField:单行文本域

JTextArea:多行文本区

JPasswordField:密码输入框

JScrollPane:滚动窗体  使用一个多行文本域作为参数创建滚动窗体

JComboBox:下拉选择框

 

JRadioButton:单选按钮

JCheckBox:多选按钮

JList:多行列表

JLabel:标签

JEditorPane:显示结构化文档

Border:边框

 

JMenuBar:菜单条

JMenu:菜单

JMenuItem:菜单项

JPopupMenu:弹出式菜单

 

JSlider:滑动条

JProgressBar:进度条

JTabbedPane:分层面板

JSplitPane:分隔面板

JToolBar:工具条

 

JFileChooser:文件选择器

JColorChooser:颜色选择器

 

显示对话框

JoptionPane 里面有很多静态方法可以弹出对话框

 

注意:具体的方法可以去参看Java2 SEAPI文档。

 

 

三、awt事件模型(观察者模式)(重点)

 

事件模型中,包括事件源对象,事件处理者(事件监听者对象),事件对象。

 

事件源和事件处理者之间建立了授权注册关系,也就是在事件源类中有一个事件处理者的对象作为属性,也可能是一个事件处理者的集合。

      

          事件对象

事件源————————〉事件处理者

 

这就是事件模型的机制,也就是由事件源对象发送一个消息(事件对象),然后事件处理者调用相应的方法处理事件。

 

在事件监听器接口中定义的方法,都要以事件对象为参数。

 

***   一个事件源可以注册多个同类型的监听器,也可以注册多种多个事件监听器,

      一个事件监听器也可以为多个事件源服务。

 

事件对象继承自EventObject类,并可以通过getSource()方法获得事件源对象,当然需要在构造事件对象时将事件源对象传入,来区分是哪个事件源发出的事件,所以要用事件对象作为参数。

 

事件源,事件对象,监听接口,在java.awt包中提供了很多已经定义好的,只需要实现监听接口就好了。

 

什么是发消息:

ABC三个类,分别作为事件源,事件处理者,事件对象。

A类中有一个B类的属性或者是一个内容为B类对象的集合,也就是事件源和事件处理者之间的建立了授权关系,

B类需要实现一个自定义的接口,这个自定义的接口继承了EventListenerEventListener接口中没有定义任何方法,这只是一个标记接口。

实现在自定义接口中定义好的用于事件处理的方法,C类要继承EventObject类。

这些方法是以事件对象为参数的b(C c),而后在Aa(C c)方法中使用B类的对象调用B类中的b(C c)方法,

并把事件对象作为参数,并在main方法中用A类的对象调用了a(c)方法,这也就叫做A类对象给B类发送了消息。

 

也就是说事件源对象间接调用了事件监听器的方法,并以事件对象为实参传到事件监听器的方法中,要就叫事件源给事件监听器的方法发了一个消息(事件对象)。

 

 

例子如下:

import java.util.*;

//事件源类

class A{

         private String test;

         private List li=new ArrayList();

         public A(String test){

                   this.test=test;

         }

         public String getTest(){return this.test;}

         public void addB(B b){

                   this.li.add(b);

         }

         public void removeB(B b){

                   this.li.remove(b);

         }

       /*

       * 所谓的事件源给事件监听器,发送事件对象。

       * 其实就是事件源用事件为参数,调用时间监听器的相应方法

       */

         public void fire(){

                   C c=new C(this);

                   Iterator it=li.iterator();

                   while(it.hasNext()){

                            B b=(B)it.next();

                            b.b(c);    

                   }

         }     

}

//事件监听器的接口,要继承EventListener标记接口

//监听接口中的每一个方法,都应该以对应的时间对象作为参数

interface Blistener extends EventListener{

         void b(C c);

}

//事件监听器,实现接口

class B implements Blistener{

         public void b(C c){

                   A a=(A)c.getSource();

                   System.out.println(a.getTest()+" "+c.getMessage());

         }

}

/*

事件对象类

事件对象类中要封装事件源对象

*/

class C extends EventObject{

         private String message;

         public C(Object src){

                   super(src);

         }

         public void setMessage(String message){

                   this.message=message;

         }

         public String getMessage(){return this.message;}     

}

public class Test{

         public static void main(String[] args){

                   A a1=new A("Event");

                   B b1=new B();

                   c1.setMessage("Test");

                   a1.addB(b1);//注册监听器

                   a1.fire();//发送事件

         }

}

以上代码只是事例,在引入包之后可以运行。

 

Java的图形编程中,所有动作(事件)都已经提供了相应的事件对象和事件监听接口,例如:实现窗口的关闭按钮,点击关闭按钮会发出相应的事件对象,相应的调用监听器中实现好的方法。

相应的方法清参阅Java2 SE API帮助文档。

 

缺省适配模式,通过一个抽象类实现接口,抽象类中的接口方法实现,都是一个无意义的空实现,可以继承这个抽象类,只覆盖向覆盖的方法就可以了。

 

java.awt.event包中,会有一些适配类,也就是把相应的XXXListener,换成XXXAdapter就是适配类。

java.awt.event包中的ActionEvent类,在以下操作中会发送这个事件,

1JButton组件,按钮被点击

2JTextField组件,在单行文本域中按Enter键。

3JCheckBox组件,选中了复选框。

4JRadioButton组件,选中了单选按钮。

5JMenu组件,选中菜单项。

 

 

作业:

写一个股市类作为事件源,事件源会随机产生波动,写两个监听类,一个会在股市跌的时候卖出,涨的时候买入,另一个投资逻辑刚好相反

                                 CoreJava第十二天    2007-5-14

 

一、复习AWT事件模型(Observer模式)

   1、事件源

   2、事件对象

   3、事件监听器

 

事件源和监听器事先进行授权注册,当事件条件满足时,事件源会给注册的监听器发送一个事件对象,由事件监听器作出相应的处理。

 

一个事件源可以是多种事件的事件源

一个事件源就同一类事件可以注册多个监听器

一个监听器可以同时注册在多个事件源当中

 

事件源和监听器是独立,弱耦合的,是各司其职的

 

事件对象中会封装事件源对象

事件监听接口中的每一个方法都要以事件对象为参数

事件源中要保存和它有监听关系的监听器

事件源给事件监听器发送事件对象:事件源以事件对象作为参数,调用监听器接口的相应方法,通过回调,调用的是不同监听实现类的方法

 

二、

Java的图形编程中,所有动作(事件)都已经提供了相应的事件对象和事件监听接口,

例如:实现窗口的关闭按钮,点击关闭按钮会发出相应的事件对象,相应的调用监听器中实现好的方法。

相应的方法清参阅Java2 SE API帮助文档。

 

缺省适配模式,通过一个抽象类实现接口,抽象类中的接口方法实现,都是一个无意义的空实现,可以继承这个抽象类,只覆盖向覆盖的方法就可以了。

 

java.awt.event包中,会有一些适配类,也就是把相应的XXXListener,换成XXXAdapter就是适配类。

适配类是抽象类,其中对接口XXXListener中的方法进行了空实现,实现这个类,覆盖对自己有用的方法

 

java.awt.event包中的ActionEvent类,在以下操作中会发送这个事件,

1JButton组件,按钮被点击

2JTextField组件,在单行文本域中按Enter键。

3JCheckBox组件,选中了复选框。

4JRadioButton组件,选中了单选按钮。

5JMenu组件,选中菜单项。

 

 

添加事件监听:

1、实现监听接口

2、将监听器对象注册在组件(事件源)

 

ActionEvent 

事件源 --- 组件 JButton 按钮   点击触发ActionEvent 

               JTextField 单行文本域  输入内容以后回车触发ActionEvent 

jtf.getText();  //得到文本域中的内容

 

练习:

1、写一个图形界面,采用BorderLayout布局,中间的部分放置一个可以滚动不可编辑的JTextArea,南面放置一个可以编辑的JTextField,

但在TextField中输入文字并按下回车的时候,文字会添加到TextArea

 

2、为BAM添加用户界面

需要以下几个类:

 

BAMClient 其中会包含一个Frame,这是用户主界面

MainPanel:主界面,用户可以选择开户或者登录

RegisterPanel:用户开户具体用到的界面

LoginPanel:用户登录需要的界面

BusinessPanel:界面上会显示账户的功能 至少包括存款和取款,对于可透支的用户,还允许用户修改透支额度,对于贷款用户,还允许用户贷款和还贷款

 

:本练习的界面布局不做要求,请阅读现有代码,添加事件处理代码

提示:在开户或者登录之后都会跳到BusinessPanel,而用户点击了交易之后,界面停留在BusinessPanel

要随时注意在BusinessPanel上根据数据的变化更新显示信息

 

三、多线程

C++的多进程是OS系统并发的一个任务

Java中没有多进程,一个JVM就是一个进程

 

线程是在进程中并发的一个顺序的执行流程

 

多进程:划分时间片,宏观上并行,微观上串行

多线程:cpu在进程内部再划分时间片

 

CPU ,代码 ,数据

进程:进程间数据独立

线程:数据空间共享,堆空间的共享(堆空间中存放的是对象),栈空间是独立的

所以线程间切换容易,称为轻量级进程

 

一个线程对象代表了一个线程,并非就是一个线程

线程是操作系统中负责维护的资源

java.lang.Thread类的一个对象就代表一个线程

线程是底层OS维护的资源,JVM跑在OS上,在JVM中创建一个Thread对象,调用其start()方法,底层OS会申请一个线程资源,线程对象可到底层管理一个线程

创建好线程之后,把要让线程执行的代码封装到线程对象中(覆盖run()方法)

 

实现线程代码的方式:

1、继承Thread 类,覆盖run()方法

   去底层申请线程并运行,对线程对象调start()方法,main方法是一个主线程

   宏观并行,微观串行

2、实现Runnable接口

  使用多态获得Runnable对象,成为目标对象

  再利用目标对象构造线程对象  Thread t = new Thread(target);

 

 

四、多线程的状态转换图(7状态图)

  见另一文件,名为Thread.pdf

 

 

作业:

用两种方式实现两个线程,一个线程负责打印1-2600,另一个线程打印A-Z,反复打印100

 

CoreJava Day13

 

13.1

   阻塞状态

 

     初始状态                ▲阻塞状态          ▲终止状态

        \                  /           ^ 1           ^

         \                /             \ 2sleep    /

          \start        /                \ 3join   /stop

           \           /                  \       /

            V        V                     \     /

          可运行状态 _ _ _ _ OS选中 _ _ _ _\ ▲运行状态

          (只缺CPU)   \  CPU到期或调用yield

          

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

    1,初始状态,线程创建,线程对象调用start()方法。

    2,可运行状态,也就是等待Cpu资源,等待运行的状态。

    3,运行状态,获得了cpu资源,正在运行状态。

    4,阻塞状态,也就是让出cpu资源,进入一种等待状态,而且不是可运行状态,有三种情况会进入阻塞状态。

      1)如等待数据输入(输入设备进行处理,而CPU不处理),则放入阻塞,直到输入完毕,阻塞结束后会进入可运行状态。

      2)线程休眠,线程对象调用sleep()方法,阻塞结束后会进入可运行状态。

                           public static void sleep(long millis)

                                           throws InterruptedException

                                括号中以毫秒为单位使线程停止一段时间,间隔期满后,线程不一定立即恢复执行。

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

                                       try{

                                                 Thread.sleep(1000);

                                   }catch(InterruptedException e){

                                            e.printStackTrace(e);

                                       }

                                       线程中有异常,只能trycatch,子类中不能抛出比父类更多的异常,父类run方法没有抛出异常。

      3)线程对象2调用线程对象1join()方法,那么线程对象2进入阻塞状态,直到线程对象1中止。

                         public final void join() throws InterruptedException

                                表示其他运行线程放弃执行权,进入阻塞状态,直到调用线程结束。

                                实际上是把并发的线程变为串行运行。

                                               t1 num

                                               t2 char  if(c=='m') -> t1.join()

                                               //t2t1调用joint2进入了阻塞状态

                                               //当条件成立时,t1加入打印数字,一直到打印完,此时t2继续运行

 

    5,中止状态,也就是执行结束。

    6,锁池状态

    7,等待队列

 

13.2 共享数据的并发处理

13.2.1

   数据的错误发生

  多线程并发访问同一个对象(临界资源)

  破坏了原子操作,就会发生数据不一致的情况

 

13.2.2 共享数据的并发处理

   多线程同时并发访问的资源叫做临界资源。

    多个线程同时访问对象并要求操作相同资源时分割了原子操作就会出现问题。(原子操作,不可再分的操作)会出现数据的不一致或数据不完整,为避免这种现象采用对访问的线程做限制的方法。

 

   互斥锁机制,利用每个对象都有一个monitor(锁标记),当线程拥有这个锁标记时才能访问这个资源,没有锁标记便进入锁池。任何一个对象系统都会为其创建一个互斥锁,这个琐是为了分配给线程的,防止打断原子操作。每个对象的锁只能分配给一个线程。

 

   Synchronized用法

    1Synchronized修饰代码块(同步代码块),

              public void push(char c){

                  synchronized(this)//只有持有当前对象锁标记的线程才能访问这个代码块

                          {

                               ...

                  }

              }

 

      对括号内的对象加锁,只有拿到锁标记的对象才能执行该代码块

    2Synchronized修饰方法

                public synchronized void push(char c) {

                     ...

                }

       在整个方法里,对当前对象的加锁,只有拿到锁标记的对象才能执行该方法。

 

  

     初始状态                ▲阻塞状态          ▲终止状态

        \                  /           1           

         \                /             \ 2sleep    /

          \start        /                \ 3join   /stop

           \           /                  \       /

                                       \     /

          可运行状态  _ _ _ OS选中 _ _ _\ ▲运行状态

          (只缺CPU)    \  CPU到期或调用yield

                                            /

                      \                      /

                       \        Synchronized/

                        \                  /

                         \               

                               锁池状态

          

 

    锁池:一个空间,每个对象都有一个,用来存放等待锁标记的线程

    当一个对象中有了锁标记,不会释放其它对象的锁标记。

 

    t1线程正在访问对象O的同步方法时,别的线程t2不能访问O的任何同步方法,但还是可以访问其它的非同步方法

 

   ArrayList Vector

              list

               

            /     \

           /       \

          /         \

      ArrayList    Vector(所有方法都做成了同步)

       

   构造方法   ×对象没有完全构造好了,没有当前对象概念

    抽象方法   ×抽象方法没有代码块,没用

    静态方法   √是对类对象的加锁

         

    ☆注意:构造方法不能Synchronized修饰

    静态方法可以用Synchronized修饰(是对类对象加锁,类对象会在反射时讲到)

    抽象方法不能用Synchronized修饰,不影响子类覆盖,子类在覆盖这个方法是可以加Synchronized,也可以不加Synchronized,所以根据Java不允许写废代码的特点是不能写在一起。

 

 

   练习:

    1、用数组实现个栈;一个线程负责入栈一个线程负责出栈;长度为6

    char[6]

    T1, Push A~Z

    T2, Pop

    长度为6,并且不允许扩充

 

   

-----------------

注意:对当前对象加锁,一个代码块或者方法是同步的(Synchronized),当前对象的锁标记没有分配出去时,有一个线程来访问这个代码块时,就会的到这个对象的锁标记,直到这个线程结束才会释放着个锁标记,其他想访问这个代码块或者是方法线程就会进入这个对象锁池,如果没有得到当前对象的锁标记,就不能访问这个代码块或者是方法。当一个线程想要获得某个对象锁标记而进入锁池,这个线程又持有其他对象的锁标记,那么这个线程也不会释放持有的锁标记。

 

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

线程因为未拿到锁标记而发生阻塞进入锁池(lock pool)。每个对象都有自己的一个锁池的空间,用于放置等待运行的线程。由系统决定哪个线程拿到锁标记并运行。

 

使用互斥锁的注意事项

 

举例:男孩和女孩例子,每个女孩是一个对象,每个男孩是个线程。每个女孩都有自己的锁池。每个男孩可能在锁池里等待。

Class Girl{

         Public void hand(){

 

         }

         Public syncronized void kiss(){

 

         }

}

Class Boy extends Thread{

         Public void run(){

                  

         }

}

 

注意:只读不用加同步,只写也不用加同步,只有读写操作兼而有之时才加同步。

 

注意:在java.io包中Vector  HashTable 之所以是线程安全的,是因为每个方法都有synchronized修饰。Static 方法可以加synchronized , 锁的是类对象。但是Vector  jdk 1.0   ArrayList  jdk1.2 所以实际应用还是使用ArrayList

 

注意:内同步,外同步,内同步,即,类内的方法加同步(synchronized)修饰,外同步即,在需要控制只能由一个线程进行访问时,把需要控制的方法写在同步代码块里。

转载于:https://my.oschina.net/xiahuawuyu/blog/42580

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Bank Account Management System 银行账户管理子系统 简称BAMS 这是一个基于C/S结构的银行账户在线管理系统,用户可以通过ATM终端界面来操作自己的银行账户. ATM 1: 要求1:封装一个Account类 - 业务数据 写一个账户类(Account),属性并且完全封装(注意:要辨别每个属性的set/get方法是否需要公开): id:账户号码 长整数(Long) password:账户密码 字符串类型(String) name:真实姓名 字符串类型(String) personId:身份证号码 字符串类型(String) email:客户的电子邮箱 字符串类型(String) balance:账户余额 双精度(double) 方法: deposit: 存款方法,参数类型:double, 返回类型:Account withdraw:取款方法,参数类型:double, 返回类型:Account 构造方法: 有参和无参,有参构造方法用于设置必要的属性 ATM 2:要求1:完成以下两种账户类型的编码。 银行的客户分为两大类:储蓄账户(SavingAccount)和信用账户(CreditAccount),两种的账户类型的区别在于: 储蓄账户不允许透支,而信用账户可以透支,并在用户在满足银行条件的情况下允许用户调整自己的透支额度. 注意: 1、CreditAccount需要多一个属性 ceiling 透支额度; 2、CreditAccount需要覆盖(重写)Account中的取款方式withdraw()。 要求2:完成Bank类的编码。 属性: 1.当前所有的账户对象的信息,存放在数组中: Account[]. 2.当前账户数量index. 方法: 1. 用户开户(register) 参数列表: Long 账号, String密码, String确认密码,String 姓名,String身份证号码,String邮箱,int 账户类型; (Long id, String password, String repassword, String name, String personID, String email, int type) 返回类型:Account 项目需求规定账户类型:0 – 储蓄账户 1 – 信用账户 2 – 可贷款储蓄账户 3– 可贷款信用账户 2. 用户登录(login) 参数列表: Long 账号, String密码; (Long id, String password) 返回类型:Account 3. 用户存款(deposit) 参数列表: Long 账号, double存款金额; (Long id, double money) 返回类型:Account 4. 用户取款(withdraw) 参数列表: Long 账号,String 密码,double取款金额; (Long id, String password, double money) 返回类型:Account 5. 设置透支额度(updateCeiling) 参数列表: Long 账号, String 密码,double透支额度金额; (Long id, String password, double money) 返回类型:Account 提示:这个方法需要验证账户是否是信用账户 6. 转账功能(transfer) 参数:from转出账户,passwordFrom 转出账号的密码,to转入账户,money转账的金额 (Long from, String passwordFrom, Long to, double money) 返回值:boolean 要求3:另外,请为Bank类添加几个统计方法 1.统计银行所有账户余额总数 2.统计所有信用账户透支额度总数 要求4:编写测试类 写个测试类,测试以上代码能否正常工作。 要求5:覆盖toString方法 查看对象的内容。 ATM 3: 要求1:让银行来提供账号(id)的生成 修改Account类和Bank类,银行用户的账号(id)应是自动生成的,初始值为: 862150212013020001(国家+邮编+年+月+序号)。 比如:第一个开户的账号为862150212013020001,第二开户的账号为862150212013020002 … 依此类推. 要求2:修改存款和取款方法 对于Account类中的存款方法和取款方法进行修改. 存款方法:改为不允许子类修改 取款方法:改为抽象方法,便于在子类中去覆盖(重写) 要求3:单例 将Bank类作成单例。 提醒:一定要理解使用单例模式的原理。 ATM 4: 要求1:新增一个贷款功能 为了满足业务发展的需求,银行需要为用户提供贷款的功能,来满足更多的用户需求。 抽象出一个贷款功能的接口:Loanable 该接口具有以下功能: a) 贷款(requestLoan) 参数:money贷款金额 返回类型:Account b) 还贷(payLoan) 参数:money还贷款金额 返回类型:Account 提醒:一定要理解抽象接口的原理和真实含义。 要求2:新增两种的新的账户类型 为了满足业务发展的需求,新增两种具有贷款功能的账户类型:可以贷款不可以透支账户和可以贷款可以透支账户; 为SavingAccount和CreditAccount各自添加一个子类LoanSavingAccount类和LoanCreditAccount类,同时让两个新增的子类都必须要实现Loanable接口。为了表示某个贷款账户的贷款金额,需要为所有的可贷款账户提供一个能记录贷款金额,所以要为CreditAccount类整一个普通的成员属性loanAmount,为长整形(long)。 说明1:LoanSavingAccount类表示该账户可以贷款,不可以透支; LoanCreditAccount类表示该账户可以贷款,可以透支。 说明2:贷款和透支是不一样的,透支指的是账户余额小于0,而贷款用户需要一个贷款额的属性. 在ATM机上,用户可以选择贷款,也可以选择还贷款,而还贷款就是要把账户余额上的资金转到贷款额上 例如: 用户余额10000元,贷款额100000元,用户可以选择还款5000元,则用户余额变为5000,贷款额变为95000元. 要求3:为Bank类添加三个新方法 a) 贷 款(requestLoan) 参数:id 账户,money贷款金额 (Long id , double money) 返回类型:Account b) 还贷款(requestLoan) 参数:id 账户,money还贷款金额 (Long id , double money) 返回类型:Account c) 统计所有账户贷款的总额(totoal) 参数:无 返回类型:double ATM 5: 要求1: 修写Bank类,采用集合的方式来管理多个Account对象 注意:通过分析每种集合的具体功能和特性后,选择合适的集合类型实现该功能。 要求2: 为Bank类添加一个方法,能够打印所有用户的总资产排名(提高部分) 说明: 1)、一个用户可能会有多个账号,以身份证号为准. 2)、总资产指多个账户余额的总和,不需要考虑贷款账户的贷 ATM 6:Exception 要求1: 为ATM增加业务异常类: ATMException: ATM业务异常基类。 BalanceNotEnoughException :用于取钱的时候余额不足的情况(包括账户余额超过透支额的情况) RegisterException:用于开户异常的情况,例如密码两次输入不一致等情况 LoginException:用户登录异常的情况,例如id错误,密码错误 LoanException:贷款额不能为负数,如果用户试图将贷款额置为负数,则会抛出这个异常 注意:在此异常的基础也可以继续扩展适合业务的异常类。 ATM 7:Swing GUI开发 第一步部分:为ATM项目添加用户客户端操作界面 需要以下几个类: 1) ATMClient: 其中会包含一个Frame,这是用户主界面. 2) MainPanel:主界面,用户可以选择开户或者登录 3) RegisterPanel:用户开户具体用到的界面 4) LoginPanel:用户登录需要的界面 5) BusinessPanel:界面上会显示账户的功能, 至少包括存款\取款\对于可透支的用户,允许用户修改透支额度\对于贷款用户,允许用户贷款和还贷款\转账。 第二步部分:为用户客户端操作界面添加事件处理 要求:在开户或者登录之后都会跳到BusinessPanel,而用户点击了交易之后,界面停留在BusinessPanel 要随时注意在BusinessPanel上根据数据的变化更新显示信息。 ATM 8:I/O&File 项目详细内容介绍 1、 分析: 将账户对象保存文件中,前期为新的账户对象分配id的做法(使用static特性)不再合适现今业务需求,也应相应的改变。 解决方案: 将下一个可用的id存放在文件中,每创建一个新对象的时候都会读取这个文件,获得新对象的id,并且修改文件中的id,使其加1后,再保存到文件中。 2、 修改Bank类中各个业务方法 分析: 要将账户信息全部保存到文件中,然后再从文件读取到内存中进行业务操作,而后再将处理完的业务对象重新保存到文件中永久保存起来。 解决方案: 1)采用对象序列化和反序列化技术。 2)将全部账户信息采用对象序列化的方式存放在文件中。 提示: 1) 使用文件来保存各种账户的信息,将注册、存款、取款、转账、修改之后的信息要及时的保存到文件中,时刻保证内存和文件中数据的一致性。 2) 采用何种存放方式,自由发挥决定。 ATM 9:NetWork 分析: 在现有的ATM中,用户是通过界面直接访问Bank对象,这种方式不符合业务需求,因为银行后台是受保护的绝对安全的业务操作,所以将其改为C/S结构,由界面充当客户端,通过TCP协议访问服务器端的核心业务对象(Bank对象). 解决方案: 1) 多线程技术 2) 网络编程技术 3) 需要完成服务端的编程,负责完成接收客户端的请求和相关业务处理。 注意:如何保证多个客户端同时登陆,并且保证业务数据在冲突的情况下,不能受到破坏。 提示:客户端和服务器端需要通过对象(TO)来传递信息,,这里会使用对象序列化技术.
项目名称:Bank Account Management System 银行账户管理系统 简称BAM 项目描述:这是一个基于C/S结构的银行账户在线管理系统,用户可以通过ATM终端界面来操作自己的银行账户. 项目实施方式:这是一个同步练习,随着达内CoreJava课程的深入,这个项目将趋于完整,学员的任务是随着知识点的深入,完成每一个进阶的项目要求. 项目一 练习1:(面向对象基础语法) 写一个账户类(Account),属性: id:账户号码 长整数 password:账户密码 name:真实姓名 personId:身份证号码 字符串类型 email:客户的电子邮箱 balance:账户余额 方法: deposit: 存款方法,参数是double型的金额 withdraw:取款方法,参数是double型的金额 构造方法: 有参和无参,有参构造方法用于设置必要的属性 练习2:(封装) 将Account类作成完全封装,注意:要辨别每个属性的set/get方法是否需要公开 练习3:(继承,多态) 银行的客户分为两类,储蓄账户(SavingAccount)和信用账户(CreditAccount),区别在于储蓄账户不允许透支,而信用账户可以透支,并允许用户设置自己的透支额度. 注意:CreditAccount需要多一个属性 ceiling 透支额度 为这两种用户编写相关的类 同时要求编写Bank类,属性: 1.当前所有的账户对象的集合,存放在数组中 2.当前账户数量 方法: 1.用户开户,需要的参数:id,密码,密码确认,姓名,身份证号码,邮箱,账户类型(int),返回新创建的Account对象 2.用户登录,参数:id,密码 返回Account对象,提示 用s1.equals(s2)判断s1和s2两个字符串内容是否相等 3.用户存款,参数:id,存款数额,返回修改过的Account对象 4.用户取款,参数:id,取款数额,返回修改过的Account对象 5.设置透支额度 参数:id,新的额度 ,返回修改过的Account对象.这个方法需要验证账户是否是信用账户 用户会通过调用Bank对象以上的方法来操作自己的账户,请分析各个方法需要的参数 另外,请为Bank类添加几个统计方法 1.统计银行所有账户余额总数 2.统计所有信用账户透支额度总数 写个主方法测试你写的类 项目二 练习4:(语言高级特性,三个修饰符) 1.修改Account类,银行用户的账号(id)是自动生成的,初始值为100000,第一个开户的用户id为100001,第二个为100002,依此类推. 提示:构造对象的时候采用static属性为id赋值 2.对于Account类,有两个方法,存款方法和取款方法,请修改这两个方法. 存款方法改为不允许子类修改 取款方法根据不同的子类而不同,因此,改为抽象方法,在两个子类中分别实现 3.将Bank类作成单例 项目三 练习5:(接口) 为SavingAccount和CreditAccount各自添加一个子类 LoanSavingAccount类:用户可以贷款,不可以透支 LoanCreditAccount类:用户可以贷款,可以透支 说明:贷款和透支是不一样的,透支指的是账户余额小于0,而贷款用户需要一个贷款额的属性. 在ATM机上,用户可以选择贷款,也可以选择还贷款,而还贷款就是要把账户余额上的资金转到贷款额上 例如:用户余额10000元,贷款额100000元,用户可以选择还款5000元,则用户余额变为5000,贷款额变为95000元. 利用接口来抽象出LoanSavingAccount类和LoanCreditAccount类的共性 接口中的方法: requestLoan:贷款 payLoan:还贷 getLoan:获取用户贷款总额 为Bank类添加三个方法, 贷款:参数 id,贷款额,返回修改过的Account对象 还贷款:参数 id,还款额,返回修改过的Account对象 统计所有账户贷款的总数 练习6:(Object) 为Account类及其子类添加toString方法和equals方法 项目四 练习7:(Exception) 为BAM添加几个异常类 BalanceNotEnoughException :用于取钱的时候余额不足的情况(包括账户余额超过透支额的情况) RegisterException:用于开户异常的情况,例如密码两次输入不一致等情况 LoginException:用户登录异常的情况,例如id错误,密码错误 LoanException:贷款额不能为负数,如果用户试图将贷款额置为负数,则会抛出这个异常 以上四个异常类有一个共同的父类 BusinessException 并妥善的处理这些异常 项目五 练习8:(集合) 改写Bank类,采用集合的方式来管理多个Account对象 为Bank类添加一个方法 打印所有用户的总资产排名 说明:一个用户可能会有多个账号,以身份证号为准.总资产指多个账户余额的总和,不需要考虑贷款账户的贷款额 项目六 练习9:(GUI) 为BAM添加用户界面 需要以下几个类: BAMClient 其中会包含一个Frame,这是用户主界面 MainPanel:主界面,用户可以选择开户或者登录 RegisterPanel:用户开户具体用到的界面 LoginPanel:用户登录需要的界面 BusinessPanel:界面上会显示账户的功能 至少包括存款和取款,对于可透支的用户,还允许用户修改透支额度,对于贷款用户,还允许用户贷款和还贷款 注:本练习的界面布局不做要求,请阅读现有代码,添加事件处理代码 提示:在开户或者登录之后都会跳到BusinessPanel,而用户点击了交易之后,界面停留在BusinessPanel 要随时注意在BusinessPanel上根据数据的变化更新显示信息 项目七 在该加资源保护的地方加上,没有标准 项目八 练习10:(I/O) 修改Bank类,账户信息会采用对象序列化的方式存放在文件中.当Bank对象生成的时候会读取文件,设置账户集合.当账户信息改变的时候,会随时更新文件 设计一个FileDAO类(文件数据访问对象),负责对文件的访问,包括存放账户,提取账户等方法,在Bank类中,会通过FileDAO对象来访问文件 注意:如果已有的账户对象会存在文件中,那么为新的账户对象分配id的做法也应相应的改变,过去的用static属性的做法不再合适,应该改为,把下一个可用的id存放在一个文件中,每创建一个新对象的时候都会读取这个文件,获得新对象的id,并且修改文件中的id,使其加1.这个工作可以放在Account类的构造方法中 项目九 练习11:(网络) 在现有的BAM中,用户是通过界面直接访问Bank对象的,将其改为C/S结构,由界面充当客户端,通过TCP协议访问服务器端的Bank对象. 提示:客户端和服务器端需要通过对象来传递信息,这里会使用对象序列化技术.
1.需要完成的任务 使用UserDaoImp1类和User类的相关方法,完成如下功能: (1)对空的User类对象调用getUserInfo()方法抛出的异常处理 (2)修改UserDaoImp1类,要求用户id不能修改,修改则抛出异常 (3)使用log4j输出日志信息 2.技能训练 (1)会使用try-catch-finally捕获和处理异常 (2)会使用throw和throws (3)会使用log4j记录日志 3.实践 实践一:使用try-catch进行异常处理 需求说明 (1)使用UserDaoImp1类的方法查找用户,并用User类的getUserInfo()方法输出用户信息 (2)使用一个不存在的用户名查找用户,使用try-catch对抛出的异常进行处理 实现思路及关键代码 (1)在测试类中调用UserDaoImp类的addUser(User user)方法,添加用户,然后用findUser(String uName)方法查找并输出用户信息 (2)在测试类中调用UserDaoImp1类的findUser(String uName)方法,使用不存在的用户名查找用户,并试图输出用户信息 (3)对抛出的异常使用try-catch进行异常处理。 实践二:使用try-catch-finally进行异常处理 需求说明 (1)对实践1的异常使用try-catch-finally进行异常处理 (2)在finally块输出是否抛出了异常 实现思路及关键代码 (1)在任务一中的代码上增加finally块 (2)为了判断在finally块输出是否抛出异常,可以设置一个变量,在catch块里修改这个变量 实践四:使用log4j 需求说明 (1)使用log4j输出日志信息 (2)查看输出日志信息
实验题目 编写一个类实现银行帐户的概念,包括的属性有“帐号”、“储户姓名”、“地址”、“存款余额”,包括的方法有“存款”、“取款”、“查询”、“计算利息”、“累加利息”等。 实验要求 改写上面的类,增加一个类的静态属性“最小余额”和一个用来修改这个最小余额属性的方法。 改写上面的类,增加一个类的静态属性“活期利率”和封装这个属性的相应方法。 程序模块 实验后的结果验证 总结体会 通过此次实验,我初步掌握了编写一个类的概念,更进一步的熟悉了static等等与之用法相类似的语法类,增强了自己学好JAVA的信心。进一步掌握静态对象和非静态对象的区别与联系。 静态对象的数据在全局是唯一的,一改都改。如果你想要处理的东西是整个程序中唯一的,弄成静态是个好方法。 非静态的东西你修改以后只是修改了他自己的数据,但是不会影响其他同类对象的数据。 静态对象和非静态对象都是对象,是对象都是要实例化的。不同之处就是2者的数据表现和存储方式不一样。 静态的好处: 引用方便。对于公用类型的变量,直接用 类名.静态方法名 或者 类名.静态变量名就可引用并且直接可以修改其属性值,不用getter和setter方法。 保持数据的唯一性。此数据全局都是唯一的,修改他的任何一处地方,在程序所有使用到的地方都将会体现到这些数据的修改。 有效减少多余的浪费。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值