计算器
班级:******姓名:******
学号:*******指导老师:******
实验名称:java计算器
1实验目的:java编程语言在编程方面的具体应用,以及使用面向对象方法,对小应用程序进行需求分
析、概要设计、详细设计,最后使用java编程实现的全过程。
2实验意义:
在编程我们使用的java语言,是目前比较流行的编程语言。在当今这个时代,java语言在编程方面的优势使得编程有了更好的选择。java语言最大的特点是具有跨平台性,使其不受平台不同的影响,得到了广泛的应用。实训性质
本课程是计算机信息管理专业的一门实践性课程,是《java编程》课程的实践性教学环节。实训目标
⑴综合应用java程序设计的知识解决实际问题。
⑵学会在应用程序的设计过程中,应用面向对象的程序设计方法。⑶学会应用jdbc创建数据库应用程序。
⑷学会开发基于swing的应用程序及多文档应用程序的设计。实训任务
用java语言开发工具(例如jdk、jcreator、netbeans等)制作一个简单的可运行的完整的应用程序或小型系统,并编制出各阶段必要的文档。
将创建一个计算器,可以进行常用的加减乘除算术运算。本实例的知识点有:窗口布局器gridlayout的应用,对按钮消息的监听和响应。
6实训条件
<软件:>windowsxp,netbeanside6.527开发背景:java是由sunmicrosystems公司于1995年5月推出的java程序设计语言(以下简称java语言)和java平台的总称。java语言是一个支持网络计算的面向对象程序设计语言。java语言吸收了smalltalk语言和c++语言的优点,并增加了其它特性,如支持并发程序设计、网络通信、和多媒体数据控制等。
8系统部分分析:
1)java语言是简单的。java语言的语法与c语言和c++语言很接近,使得大多数程序员很容易学习和使用java。另一方面,java丢弃了c++中很少使用的、很难理解的、令人迷惑的那些特性,如操作符重载、多继承、自动的强制类型转换。
2)java语言是一个面向对象的。java语言提供类、接口和继承等原语,为了简单起见,只支持类之间的单继承,但支持接口之间的多继承,并支持类与接口之间的实现机制(关键字为implements)。java语言全面支持动态绑定,而c++语言只对虚函数使用动态绑定
3)java语言是分布式的。java语言支持internet应用的开发,在基本的java应用编程接口中有一个网络应用编程接口(),它提供了用于网络应用编程的类库,包括url、urlconnection、socket、serversocket等。java的rmi(远程方法激活)机制也是开发分布式应用的重要手段。
4)java语言是健壮的。java的强类型机制、异常处理、废料的自动收集等是java程序健壮性的重要保证。对指针的丢弃是java的明智选择。java的安全检查机制使得java更具健壮性。
5)java语言是安全的。java通常被用在网络环境中,为此,java提供了一个安全机制以防恶意代码的攻击。除了java语言具有的许多安全特性以外,java对通过网络下载的类具有一个安全防范机制(类classloader),如分配不同的名字空间以防替代本地的同名类、字节代码检查,并提供安全管理机制。6)java语言是体系结构中立的。java程序(后缀为java的文件)在java平台上被编译为体系结构中立的字节码格式(后缀为class的文件),然后可以在实现这个java平台的任何系统中运行。
7)java语言是可移植的。这种可移植性来源于体系结构中立性,另外,java还严格规定了各个基本数据类型的长度。java系统本身也具有很强的可移植性,java编译器是用java实现的。8)java语言是解释型的。如前所述,java程序在java平台上被编译为字节码格式,然后可以在实现这个java平台的任何系统中运行。
9)java是高性能的。与那些解释型的高级脚本语言相比,java的确是高性能的。事实上,java的运行速度随着jit(just-in-time)编译器技术的发展越来越接近于c++。
10)java语言是多线程的。在java语言中,线程是一种特殊的对象,它必须由thread类或其子(孙)类来创建。
11)java语言是动态的。java语言的设计目标之一是适应于动态变化的环境。
目录
课程设计题目………………………………p1
课程设计简介………………………………p2
课程设计源代码……………………………p5
课程设计运行结果………………………p15课程设计心得体会………………………
p16
packagecomputerpad;import.*;import.event.*;import.*;import.*;importlist;importformat;publicclasscomputerpadextendsframeimplementsactionlistener{
numberbuttonnumberbutton[];
operationbuttonoprationbutton[];
button小数点按钮,正负号按钮,退格按钮,求倒数按钮,等号按钮,清零按钮;
panelpanel;
jtextfieldresultshow;
string运算符号[]={“+”,“-”,“*”,“/”};
linkedlist链表;
boolean是否按下等号=false;
publiccomputerpad()
{
super(“计算器”);
链表=newlinkedlist();
numberbutton=newnumberbutton[10];
for(inti=0;i<=9;i++)
{
numberbutton[i]=newnumberbutton(i);
numberbutton[i].addactionlistener(this);
}
oprationbutton=newoperationbutton[4];
for(inti=0;i<4;i++)
{
oprationbutton[i]=newoperationbutton(运算符号[i]);
oprationbutton[i].addactionlistener(this);
}
小数点按钮=newbutton(“.”);
正负号按钮
=newbutton(“+/-”);
等号按钮=newbutton(“=”);
求倒数按钮=newbutton(“1/x”);
退格按钮=newbutton(“退格”);
清零按钮=newbutton(“c”);
eground();
eground();
eground();
eground();
eground();
eground();
ionlistener(this);
ionlistener(this);
ionlistener(this);
ionlistener(this);
ionlistener(this);
ionlistener(this);
resultshow=newjtextfield(10);
izontalalignment();
eground();
t(newfont(“timesroman”,,14));
der(newsoftbevelborder(d));
kground();
table(false);
panel=newpanel();
out(newgridlayout(4,5));
(numberbutton[1]);
(numberbutton[2]);
(numberbutton[3]);
(oprationbutton[0]);
(清零按钮);
(numberbutton[4]);
(numberbutton[5]);
(numberbutton[6]);
(oprationbutton[1]);
(退格按钮);
(numberbutton[7]);
(numberbutton[8]);
(numberbutton[9]);
(oprationbutton[2]);
(求倒数按钮);
(numberbutton[0]);
(正负号按钮);
(小数点按钮);
(oprationbutton[3]);
(等号按钮);
add(panel,);
add(resultshow,);
addwindowlistener(newwindowadapter()
{publicvoidwindowclosing(windowevente)
{
(0);
}
});
setvisible(true);
setbounds(100,50,240,180);
setresizable(false);
validate();
}publicvoidactionperformed(actionevente)
{
if(rce()instanceofnumberbutton)
{
numberbuttonb=(numberbutton)rce();
if(()==0)
{
intnumber=ber();
(“"+number);
t(”“+number);
是否按下等号=false;
}
elseif(()==1&&是否按下等号==false)
{
intnumber=ber();
stringnum=(string)first();
strings=(”“+number);
(0,s);
t(s);
}
elseif(()==1&&是否按下等号==true)
{
intnumber=ber();
first();
(”“+number);
是否按下等号=false;
t(”“+number);
}
elseif(()==2)
{
intnumber=ber();
(”“+number);
t(”“+number);
}
elseif(()==3)
{
intnumber=ber();
stringnum=(string)t();
strings=(”“+number);
(2,s);
t(s);
}
}
elseif(rce()instanceofoperationbutton)
{
operationbuttonb=(operationbutton)rce();
if(()==1)
{
stringfuhao=运算符号();
(fuhao);
}
elseif(()==2)
{
stringfuhao=运算符号();
(1,fuhao);
}
elseif(()==3)
{
stringfuhao=运算符号();
stringnumber1=(string)first();
stringnumber2=(string)t();
string运算符号=(string)(1);
try
{
doublen1=ouble(number1);
doublen2=ouble(number2);
doublen=0;
if((”+“))
{
n=n1+n2;
}
elseif((”-“))
{
n=n1-n2;
}
elseif((”*“))
{
n=n1*n2;
}
elseif((”/“))
{
n=n1/n2;
}
();
(”“+n);
(fuhao);
t(”“+n);
}
catch(exceptionee)
{
}
}
}
elseif(rce()==等号按钮)
{
是否按下等号=true;
if(()==1||()==2)
{
stringnum=(string)first();
t(”“+num);
}
elseif(()==3)
{
stringnumber1=(string)first();
stringnumber2=(string)t();
string运算符号=(string)(1);
try
{
doublen1=ouble(number1);
doublen2=ouble(number2);
doublen=0;
if((”+“))
{
n=n1+n2;
}
elseif((”-“))
{
n=n1-n2;
}
elseif((”*“))
{
n=n1*n2;
}
elseif((”/“))
{
n=n1/n2;
}
t(”“+n);
(0,”“+n);
last();
last();
}
catch(exceptionee)
{
}
}
}
elseif(rce()==小数点按钮)
{
if(()==0)
{
是否按下等号=false;
}
elseif(()==1)
{
stringdot=el();
stringnum=(string)first();
strings=null;
if(f(dot)==-1)
{
s=(dot);
(0,s);
}
else
{
s=num;
}
(0,s);
t(s);
}
elseif(()==3)
{
stringdot=el();
stringnum=(string)t();
strings=null;
if(f(dot)==-1)
{
s=(dot);
(2,s);
}
else
{
s=num;
}
t(s);
}
}
elseif(rce()==退格按钮)
{
if(()==1)
{
stringnum=(string)first();
if(()>=1)
{
num=ing(0,()-1);
(0,num);
t(num);
}
else
{
last();
t(”0“);
}
}
elseif(()==3)
{
stringnum=(string)t();
if(()>=1)
{num=ing(0,()-1);
(2,num);
t(num);
}
else
{
last();
t(”0“);
}
}
}
elseif(rce()==正负号按钮)
{
if(()==1)
{
stringnumber1=(string)first();
try
{
doubled=ouble(number1);
d=-1*d;
stringstr=f(d);
(0,str);
t(str);
}
catch(exceptionee)
{
}
}
elseif(()==3)
{
stringnumber2=(string)t();
try
{
doubled=ouble(number2);
d=-1*d;
stringstr=f(d);
(2,str);
t(str);
}
catch(exceptionee){
}
}
}
elseif(rce()==求倒数按钮)
{
if(()==1||()==2)
{
stringnumber1=(string)first();
try
{
doubled=ouble(number1);
d=1.0/d;
stringstr=f(d);
(0,str);
t(str);
}
catch(exceptionee){
}
}
elseif(()==3)
{
stringnumber2=(string)t();
try
{
doubled=ouble(number2);
d=1.0/d;
stringstr=f(d);
(0,str);
t(str);
}
catch(exceptionee){
}
}
}
elseif(rce()==清零按钮)
{
是否按下等号=false;
t(”0“);
();
}
}publicstaticvoidmain(stringargs[])
{
newcomputerpad();
}
}
packagecomputerpad;import.*;import.event.*;import.*;publicclassnumberbuttonextendsbutton{
intnumber;
publicnumberbutton(intnumber)
{
super(”"+number);
=number;
setforeground();
}
publicintgetnumber()
{
returnnumber;
}}
import.*;import.event.*;import.*;publicclassoperationbuttonextendsbutton{
string运算符号;
publicoperationbutton(strings)
{
super(s);
运算符号=s;
setforeground();
}
publicstringget运算符号()
{
return运算符号;
}}14java实训心得:
未接触java之前,听人说java这门语言如何的强大和难以入门,但学习之后,给我的感觉却是语言没有所谓的难于不难,关键是自己有没有真正投入去学,有没有花时间去学。java是一门很好的语言,经过周围人对java的宣传,我一开始不敢去学习这门语言,因为一门高级语言总是让人想到一开始的学习会很难,但是后来在自己的努力和老师同学的帮助下,我加入了java学习者的行列。
老师把我们带进了门,那么,以后漫长的深入学习还是要靠自己。经常性的编写一些程序,或则去看懂、研究透别人编写的程序对于我们打好基础是非常有利的。让我们怀着对java的一腔热情,用自己的刻苦努力去把java学好。将来,用自己的成绩去回报有恩于我们的社会、家人和朋友。
java编程思想,java学习必读经典,不管是初学者还是大牛都值得一读,这里总结书中的重点知识,这些知识不仅经常出现在各大知名公司的笔试面试过程中,而且在大型项目开发中也是常用的知识,既有简单的概念理解题(比如is-a关系和has-a关系的区别),也有深入的涉及rtti和jvm底层反编译知识。
中的多态性理解(注意与c++区分)
java中除了static方法和final方法(private方法本质上属于final方法,因为不能被子类访问)之外,其它所有的方法都是动态绑定,这意味着通常情况下,我们不必判定是否应该进行动态绑定—它会自动发生。final方法会使编译器生成更有效的代码,这也是为什么说声明为final方法能在一定程度上提高性能(效果不明显)。
如果某个方法是静态的,它的行为就不具有多态性:
classstaticsuper{
publicstaticstringstaticget(){
return“basestaticget()”;
}
publicstringdynamicget(){
return“basedynamicget()”;
}}
classstaticsubextendsstaticsuper{
publicstaticstringstaticget(){
return“derivedstaticget()”;
}
publicstringdynamicget(){
return“deriveddynamicget()”;
}}
publicclassstaticpolymorphism{
publicstaticvoidmain(string[]args){staticsupersup=newstaticsub();.println(get());.println(cget());
}
}
输出:
basestaticget()deriveddynamicget()构造函数并不具有多态性,它们实际上是static方法,只不过该static声明是隐式的。因此,构造函数不能够被override。
在父类构造函数内部调用具有多态行为的函数将导致无法预测的结果,因为此时子类对象还没初始化,此时调用子类方法不会得到我们想要的结果。
classglyph{
voiddraw(){
.println(“()”);
}
glyph(){
.println(“glyph()beforedraw()”);draw();
.println(“glyph()afterdraw()”);
}}
classroundglyphextendsglyph{
privateintradius=1;
roundglyph(intr){radius=r;
.println(“lyph().radius=”+radius);
}
voiddraw(){
.println(“round().radius=”+radius);
}}
publicclasspolyconstructors{
publicstaticvoidmain(string[]args){
newroundglyph(5);
}
}
输出:glyph()beforedraw()round().radius=0glyph()afterdraw()lyph().radius=5为什么会这样输出?这就要明确掌握java中构造函数的调用顺序:
(1)在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制0;
(2)调用基类构造函数。从根开始递归下去,因为多态性此时调用子类覆盖后的draw()方法(要在调用roundglyph构造函数之前调用),由于步骤1的缘故,我们此时会发现radius的值为0;
(3)按声明顺序调用成员的初始化方法;(4)最后调用子类的构造函数。
只有非private方法才可以被覆盖,但是还需要密切注意覆盖private方法的现象,这时虽然编译器不会报错,但是也不会按照我们所期望的来执行,即覆盖private方法对子类来说是一个新的方法而非重载方法。因此,在子类中,新方法名最好不要与基类的private方法采取同一名字(虽然没关系,但容易误解,以为能够覆盖基类的private方法)。
java类中属性域的访问操作都由编译器解析,因此不是多态的。父类和子类的同名属性都会分配不同的存储空间,如下:
//directfieldaccessisdeterminedatcompilesuper{
publicintfield=0;
publicintgetfield(){
returnfield;
}}
classsubextendssuper{
publicintfield=1;
publicintgetfield(){
returnfield;
}
publicintgetsuperfield(){
return;
}}
publicclassfieldaccess{
publicstaticvoidmain(string[]args){supersup=newsub();
.println(“=”++
“,ld()=”+ld());subsub=newsub();
.println(“=”++“,ld()=”+ld()+“,erfield()=”+erfield());
}
}
输出:
=0,ld()=1=1,ld()=1,erfield()=0sub子类实际上包含了两个称为field的域,然而在引用sub中的field时所产生的默认域并非super版本的field域,因此为了得到,必须显式地指明。
-a关系和is-like-a关系
is-a关系属于纯继承,即只有在基类中已经建立的方法才可以在子类中被覆盖,如下图所示:
基类和子类有着完全相同的接口,这样向上转型时永远不需要知道正在处理的对象的确切类型,这通过多态来实现。
is-like-a关系:子类扩展了基类接口。它有着相同的基本接口,但是他还具有由额外方法实现的其他特性。
缺点就是子类中接口的扩展部分不能被基类访问,因此一旦向上转型,就不能调用那些新方法。
3.运行时类型信息(rtti+反射)
概念
rtti:运行时类型信息使得你可以在程序运行时发现和使用类型信息。使用方式
java是如何让我们在运行时识别对象和类的信息的,主要有两种方式(还有辅助的第三种方式,见下描述):一种是“传统的”rtti,它假定我们在编译时已经知道了所有的类型,比如shapes=(shape)s1;另一种是“反射”机制,它运行我们在运行时发现和使用类的信息,e()。
其实还有第三种形式,就是关键字instanceof,它返回一个bool值,它保持了类型的概念,它指的是“你是这个类吗?或者你是这个类的派生类吗?”。而如果用==或equals比较实际的class对象,就没有考虑继承—它或者是这个确切的类型,或者不是。
工作原理
要理解rtti在java中的工作原理,首先必须知道类型信息在运行时是如何表示的,这项工作是由称为class对象的特殊对象完成的,它包含了与类有关的信息。java送class对象来执行其rtti,使用类加载器的子系统实现。
无论何时,只要你想在运行时使用类型信息,就必须首先获得对恰当的class对象的引用,获取方式有三种:
(1)如果你没有持有该类型的对象,e()就是实现此功能的便捷途,因为它不需要对象信息;
(2)如果你已经拥有了一个感兴趣的类型的对象,那就可以通过调用getclass()方法来获取class引用了,它将返回表示该对象的实际类型的class引用。class包含很有有用的方法,比如:packagertti;
interfacehasbatteries{}interfacewaterproof{}interfaceshoots{}
classtoy{toy(){}
toy(inti){}}
classfancytoyextendstoyimplementshasbatteries,waterproof,shoots{fancytoy(){
super(1);
}}
publicclassrttitest{
staticvoidprintinfo(classcc){
.println(“classname:”+e()+“,isinterface?[”+rface()+“]”);.println(“simplename:”+plename());.println(“canonicalname:”+onicalname());
}
publicstaticvoidmain(string[]args){classc=null;
try{
c=e(“oy”);//必须是全限定名(包名+类名)
}catch(classnotfoundexceptione){
.println(“can'tfindfancytoy”);(1);
}
printinfo(c);
for(classface:erfaces()){printinfo(face);
}
classup=erclass();objectobj=null;
try{//requiresdefault=tance();
}catch(instantiationexceptione){
.println(“can'tinstantiate”);(1);
}catch(illegalaccessexceptione){.println(“can'taccess”);(1);
}
printinfo(ss());
}
}
输出:
classname:oy,isinterface?[false]simplename:fancytoycanonicalname:oyclassname:teries,isinterface?[true]simplename:hasbatteriescanonicalname:teriesclassname:roof,isinterface?[true]simplename:waterproofcanonicalname:roofclassname:,isinterface?[true]simplename:shootscanonicalname:classname:,isinterface?[false]simplename:toycanonicalname:(3)java还提供了另一种方法来生成对class对象的引用,即使用类字面常量。比如上面的就像这样:;来引用。
这样做不仅更简单,而且更安全,因为它在编译时就会受到检查(因此不需要置于try语句块中),并且它根除了对forname方法的引用,所以也更高效。类字面常量不仅可以应用于普通的类,也可以应用于接口、数组以及基本数据类型。
注意:当使用“.class”来创建对class对象的引用时,不会自动地初始化该class对象,初始化被延迟到了对静态方法(构造器隐式的是静态的)或者非final静态域(注意final静态域不会触发初始化操作)进行首次引用时才执行:。而使用e时会自动的初始化。
为了使用类而做的准备工作实际包含三个步骤:
-加载:由类加载器执行。查找字节码,并从这些字节码中创建一个class对象
-链接:验证类中的字节码,为静态域分配存储空间,并且如果必需的话,将解析这个类创建的对其他类的所有引用。
-初始化:如果该类具有超类,则对其初始化,执行静态初始化器和静态初始化块。这一点非常重要,下面通过一个实例来说明这两者的区别:
packagertti;
import;classinitable{
staticfinalintstaticfinal=47;
staticfinalintstaticfinal2=t(1000);
static{
.println(“initializinginitable”);
}}
classinitable2{
staticintstaticnonfinal=147;
static{
.println(“initializinginitable2”);
}}
classinitable3{
staticintstaticnonfinal=74;
static{
.println(“initializinginitable3”);
}}
publicclassclassinitialization{
publicstaticrandomrand=newrandom(47);
publicstaticvoidmain(string[]args){
//doesnottriggerinitializationclassinitable=;
.println(“aftercreatinginitableref”);
//doesnottriggerinitialization
.println(final);
//doestriggerinitialization(rand()isstaticmethod).println(final2);
//doestriggerinitialization(notfinal)
.println(nonfinal);
try{
classinitable3=e(“le3”);
}catch(classnotfoundexceptione){
.println(“can'tfindinitable3”);(1);
}
.println(“aftercreatinginitable3ref”);.println(nonfinal);
}}
输出:
aftercreatinginitableref47initializinginitable258initializinginitable2147initializinginitable3aftercreatinginitable3ref74
rtti的限制?如何突破?—反射机制
如果不知道某个对象的确切类型,rtti可以告诉你,但是有一个限制:这个类型在编译时必须已知,这样才能使用rtti识别它,也就是在编译时,编译器必须知道所有要通过rtti来处理的类。
可以突破这个限制吗?是的,突破它的就是反射机制。
t类库一起对反射的概念进行了支持,该类库包含了field、method以及constructor类(每个类都实现了member接口)。这些类型的对
象是由jvm在运行时创建的,用以表示未知类里对应的成员。这样你就可以使用constructor创建新的对象,用get()/set()方法读取和修改与field对象关联的字段,用invoke()方法调用与method对象关联的方法。另外,还可以调用getfields()、getmethods()和getconstructors()等很便利的方法,以返回表示字段、方法以及构造器的对象的数组。这样,匿名对象的类信息就能在运行时被完全确定下来,而在编译时不需要知道任何事情。
####反射与rtti的区别
当通过反射与一个未知类型的对象打交道时,jvm只是简单地检查这个对象,看它属于哪个特定的类(就像rtti那样),在用它做其他事情之前必须先加载那个类的class对象,因此,文件对于jvm来说必须是可获取的:要么在本地机器上,要么可以通过网络取得。所以rtti与反射之间真正的区别只在于:对rtti来说,文件(也就是可以用普通方法调用对象的所有方法);而对于反射机制来说,.class文件在编译时是不可获取的,文件。下面的例子是用反射机制打印出一个类的所有方法(包括在基类中定义的方法):
packagetypeinfo;
importuctor;import;importn;
//usingreflectiontoshowallthemethodsofaclass.//evenifthemethodsaredefinedinthebaseclassshowmethods{privatestaticstringusage=
“usage:n”+
“showmethods”+
“toshowallmethodsinclassor:n”+
“showmethodswordn”+
“tosearchformethodsinvolving'word'”;
privatestaticpatternp=e(“w+.”);
publicstaticvoidmain(string[]args){if(<1){.println(usage);(0);}intlines=0;try{class
c=e(args[0]);method[]methods=hods();constructor[]ctors=structors();if(==1www.chayi5.com){for(methodmethod:methods){
.println(r(ng()).replaceall(“"));}for(constructorctor:ctors){
.println(r(ng()).replaceall(”“));}lines=+;}else{for(methodmethod:methods){if(ng().indexof(args[1])!=-1){
.println(r(ng()).replaceall(”“));lines++;}}for(constructorctor:ctors){if(ng().indexof(args[1])!=-1){
.println(r(ng()).replaceall(”“));lines++;}}}}catch(classnotfoundexceptione){.println(”nosuchclass:“+e);}
}}
输出:
publicstaticvoidmain(string[])publicfinalnativevoidwait(long)throwsinterruptedexceptionpublicfinalvoidwait()throwsinterruptedexceptionpublicfinalvoidwait(long,int)throwsinterruptedexceptionpublicbooleanequals(object)publicstringtostring()publicnativeinthashcode()publicfinalnativeclassgetclass()publicfinalnativevoidnotify()publicfinalnativevoidnotifyall()publicshowmethods()4.代理模式与java中的动态代理
代理模式
在任何时刻,只要你想要将额外的操作从“实际”对象中分离到不同的地方,特别是当你希望能够很容易地做出修改,从没有使用额外操作转为使用这些操作,或者反过来时,代理就显得很有用(设计模式的关键是封装修改)。例如,如果你希望跟踪对某个类中方法的调用,或者希望度量这些调用的开销,那么你应该怎样做呢?这些代码肯定是你不希望将其合并到应用中的代码,因此代理使得你可以很容易地添加或移除它们。
interfaceinterface{
voiddosomething();
voidsomethingelse(stringarg);}
classrealobjectimplementsinterface{
@overridepublicvoiddosomething(){
.println(”dosomething.“);
}
@overridepublicvoidsomethingelse(stringarg){
.println(”somethingelse“+arg);
}}
classsimpleproxyimplementsinterface{
privateinterfaceproxy;
publicsimpleproxy(interfaceproxy){
=proxy;
}
@overridepublicvoiddosomething(){
.println(”simpleproxydosomething.“);thing();
}
@overridepublicvoidsomethingelse(stringarg){
.println(”simpleproxysomethingelse“+arg);
ingelse(arg);
}}
publicclasssimpleproxydemo{
publicstaticvoidconsumer(interfaceiface){thing();
ingelse(”bonobo“);
}publicstaticvoidmain(string[]args){consumer(newrealobject());
consumer(newsimpleproxy(newrealobject()));
}
}
输出:
ingelsebonobosimpleproxyproxysomethingelsebonobosomethingelsebonobo动态代理
java的动态代理比代理的思想更向前迈进了一步,因为它可以动态地创建代理并动态地处理对所代理方法的调用。
importtionhandler;import;import;
classdynamicproxyhandlerimplementsinvocationhandler{
privateobjectproxy;
publicdynamicproxyhandler(objectproxy){
=proxy;
}
@overridepublicobjectinvoke(objectproxy,methodmethod,object[]args)
throwsthrowable{
.println(”***proxy:“+ss()+
”。method:“+method+”。args:“+args);
if(args!=null){
for(objectarg:args)
.println(”“+arg);
}
return(,args);
}}
publicclasssimpledynamicproxy{
publicstaticvoidconsumer(interfaceiface){thing();
ingelse(”bonobo“);
}
publicstaticvoidmain(string[]args){realobjectreal=newrealobject();consumer(real);
//insertaproxyandcallagain:
interfaceproxy=(interface)xyinstance(.getclassloader(),newclass[]{},newdynamicproxyhandler(real));
consumer(proxy);
}
}
输出:
ingelsebonobo***proxy:classtypeinfo.$:publicabstractvoidthing().args:nulldosomething.***proxy:classtypeinfo.$:publicabstractvoidingelse().args:[;@6a8814e9bonobosomethingelsebonobo5.即时编译器技术—jitjava虚拟机中有许多附加技术用以提升速度,尤其是与加载器操作相关的,被称为“即时”(just-in-time,jit)编译器的技术。这种技术可以把程序全部或部分翻译成本地机器码(这本来是jvm的工作),程序运行速度因此得以提升。当需要装载某个类时,文件,然后将该类的字节码装入内存。此时,有两种方案可供选择:
(1)一种就是让即时编译器编译所有代码。但这种做法有两个缺陷:这种加载动作散落在整个程序生命周期内,累加起来要花更多时间;并且会增加可执行代码的长度(字节码要比即时编译器展开后的本地机器码小很多),这将导致页面调度,从而降低程序速度。(2)另一种做法称为惰性评估(lazyevaluation),意思是即时编译器只在必要的时候才编译代码,这样,从不会被执行的代码也许就压根不会被jit所编译。新版jdk中的javahotspot技术就采用了类似方法,代码每次被执行的时候都会做一些优化,所以执行的次数越多,它的速度就越快。
6.访问控制权限java访问权限修饰词:public、protected、包访问权限(默认访问权限,有时也称friendly)和private。
包访问权限:当前包中的所有其他类对那个成员具有访问权限,但对于这个包之外的所有类,这个成员却是private。
protected:继承访问权限。有时基类的创建者会希望有某个特定成员,把对它的访问权限赋予派生类而不是所有类。这就需要protected来完成这一工作。protected也提供包访问权限,也就是说,相同包内的其他类都可以访问protected元素。protected指明“就类用户而言,这是private的,但对于任何继承于此类的导出类或其他任何位于同一个包内的类来说,它却是可以访问的”。比如:基类:
package;publicclasscookie{
publiccookie(){
.println(”cookieconstructor“);
}
voidbite(){//包访问权限,其它包即使是子类也不能访问它.println(”bite“);
}}
子类:
packaget;
import.cookie;
publicclasschocolatechipextendscookie{
publicchocolatechip(){
.println(”chocolatechipconstructor“);
}
publicvoidchomp(){
bite();//error,themethodbite()fromthetypecookieisnotvisible
}}
可以发现子类并不能访问基类的包访问权限方法。此时可以将cookie中的bite指定为public,但这样做所有的人就都有了访问权限,为了只允许子类访问,可以将bite指定为protected即可。
7.组合和继承之间的选择
组合和继承都允许在新的类中放置子对象,组合是显式的这样做,而继承则是隐式的做。
组合技术通常用于想在新类中使用现有类的功能而非它的接口这种情形。即在新类中嵌入某个对象,让其实现所需要的功能,但新类的用户看到的只是为新类所定义的接口,而非所嵌入对象的接口。为取得此效果,需要在新类中嵌入一个现有类的private对象。但有时,允许类的用户直接访问新类中的组合成分是极具意义的,即将成员对象声明为public。如果成员对象自身都隐藏了具体实现,那么这种做法是安全的。当用户能够了解到你正在组装一组部件时,会使得端口更加易于理解。比如car对象可由public的engine对象、wheel对象、window对象和door对象组合。但务必要记得这仅仅是一个特例,一般情况下应该使域成为private。在继承的时候,使用某个现有类,并开发一个它的特殊版本。通常,这意味着你在使用一个通用类,并为了某种特殊需要而将其特殊化。稍微思考一下就会发现,用一个“交通工具”对象来构成一部“车子”是毫无意义的,因为“车子”并不包含“交通工具”,它仅是一种交通工具(is-a关系)。
“is-a”(是一个)的关系是用继承来表达的,而“has-a”(有一个)的关系则是用组合来表达的。
到底是该用组合还是继承,一个最清晰的判断方法就是问一问自己是否需要从新类向基类进行向上转型,需要的话就用继承,不需要的话就用组合方式。
关键字
对final关键字的误解
当final修饰的是基本数据类型时,它指的是数值恒定不变(就是编译期常量,如果是staticfinal修饰,则强调只有一份),而对对象引用而不是基本类型运用final时,其含义会有一点令人迷惑,因为用于对象引用时,final使引用恒定不变,一旦引用被初始化指向一个对象,就无法再把它指向另一个对象。然而,对象其自身却是可以被修改的,java并未提供使任何对象恒定不变的途径(但可以自己编写类以取得使对象恒定不变的效果),这一限制同样适用数组,它也是对象。使用final方法真的可以提高程序效率吗?
将一个方法设成final后,编译器就可以把对那个方法的所有调用都置入“嵌入”调用里。只要编译器发现一个final方法调用,就会(根据它自己的判断)忽略为执行方法调用机制而采取的常规代码插入方法(将自变量压入堆栈;跳至方法代码并执行它;跳回来;清除堆栈自变量;最后对返回值进行处理)。相反,它会用方法主体内实际代码的一个副本来替换方法调用。这样做可避免方法调用时的系统开销。当然,若方法体积太大,那么程序也会变得雍肿,可能受到到不到嵌入代码所带来的任何性能提升。因为任何提升都被花在方法内部的时间抵消了。
在最近的java版本中,虚拟机(特别是hotspot技术)能自动侦测这些情况,并颇为“明智”地决定是否嵌入一个final方法。然而,最好还是不要完全相信编译器能正确地作出所有判断。通常,只有在方法的代码量非常少,或者想明确禁止方法被覆盖的时候,才应考虑将一个方法设为final。
类内所有private方法都自动成为final。由于我们不能访问一个private方法,所以它绝对不会被其他方法覆盖(若强行这样做,编译器会给出错误提示)。可为一个private方法添加final指示符,但却不能为那个方法提供任何额外的含义。
9.策略设计模式与适配器模式的区别
策略设计模式
创建一个能够根据所传递的参数对象的不同而具有不同行为的方法,被称为策略设计模式,这类方法包含所要执行的算法中固定不变的部分,而“策略”包含变化的部分。策略就是传递进去的参数对象,它包含要执行的代码。适配器模式
在你无法修改你想要使用的类时,可以使用适配器模式,适配器中的代码将接受你所拥有的接口,并产生你所需要的接口。
内部类与组合是完全不同的概念,这一点很重要。
为什么需要内部类?—主要是解决了多继承的问题,继承具体或抽象类10.内部类
一般来说,内部类继承自某个类或实现某个接口,内部类的代码操作创建它的外围类的对象。所以可以认为内部类提供了某种进入其外围类的窗口。内部类最吸引人的原因是:每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
如果没有内部类提供的、可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。从这个角度看,内部类使得多重继承的解决方案变得完整。接口解决了部分问题,而内部类有效的实现了“多重继承”。也就是说,内部类允许继承多个非接口类型。
考虑这样一种情形:如果必须在一个类中以某种方式实现两个接口。由于接口的灵活性,你有两种选择:使用单一类或者使用内部类。但如果拥有的是抽象的类或具体的类,而不是接口,那就只能使用内部类才能实现多重继承。
使用内部类,还可以获得其他一些特性:创建内部类对象的时刻并不依赖于外围类对象的创建。
-内部类并没有令人迷惑的is-a关系,它就是一个独立的实体。
类型—不可变
用于string的“+”与“+=”是java中仅有的两个重载过的操作符,而java并不允许程序员重载任何操作符。
考虑到效率因素,编译器会对string的多次+操作进行优化,优化使用stringbuilder操作(javap-cclass字节码文件名命令查看具体优化过程)。这让你觉得可以随意使用string对象,反正编译器会为你自动地优化性能。但编译器能优化到什么程度还不好说,不一定能优化到使用stringbuilder代替string相同的效果。比如:
publicclasswitherstringbuilder{
publicstringimplicit(string[]fields){stringresult=”“;
for(inti=0;i<;i++)result+=fields[i];
returnresult;
}
publicstringexplicit(string[]fields){
stringbuilderresult=newstringbuilder();
for(inti=0;i<;i++)(fields[i]);
returnng();
}}
运行javap-cwitherstringbuilder,可以看到两个方法对应的字节码。implicit方法:
publicimplicit([]);code:0:ldc#16//string2:astore_23:iconst_04:istore_35:goto328:new#18//classjava/lang/stringbuilder11:dup12:aload_213:invokestatic#20//methodjava/lang/f:(ljava/lang/object;)ljava/lang/string;16:invokespecial#26//methodjava/lang/stringbuilder.”<init>”:(ljava/lang/string;)v19:aload_120:iload_321:aaload22:invokevirtual#29//methodjava/lang/pend:(ljava/lang/string;)ljava/lang/stringbuilder;25:invokevirtual#33//methodjava/lang/string:()ljava/lang/string;28:astore_229:iinc3,132:iload_333:aload_134:arraylength35:if_icmplt838:aload_239:areturnpublicimplicit([]);code:0:ldc#16//string2:astore_23:iconst_04:istore_35:goto328:new#18//classjava/lang/stringbuilder11:dup12:aload_213:invokestatic#20//methodjava/lang/f:(ljava/lang/object;)ljava/lang/string;16:invokespecial#26//methodjava/lang/stringbuilder.”<init>”:(ljava/lang/string;)v19:aload_120:iload_321:aaload22:invokevirtual#29//methodjava/lang/pend:(ljava/lang/string;)ljava/lang/stringbuilder;25:invokevirtual#33//methodjava/lang/string:()ljava/lang/string;28:astore_229:iinc3,132:iload_333:aload_134:arraylength35:if_icmplt838:aload_239:areturn可以发现,stringbuilder是在循环之内构造的,这意味着每经过循环一次,就会创建一个新的stringbuilder对象。
explicit方法:
publicexplicit([]);code:0:new#18//classjava/lang/stringbuilder3:dup4:invokespecial#45//methodjava/lang/stringbuilder.”<init>”:()v7:astore_28:iconst_09:istore_310:goto2413:aload_214:aload_115:iload_316:aaload17:invokevirtual#29//methodjava/lang/pend:(ljava/lang/string;)ljava/lang/stringbuilder;20:pop21:iinc3,124:iload_325:aload_126:arraylength27:if_icmplt1330:aload_231:invokevirtual#33//methodjava/lang/string:()ljava/lang/string;34:areturn}可以看到,不仅循环部分的代码更简短、更简单,而且它只生成了一个stringbuilder对象。显式的创建stringbuilder还允许你预先为其指定大小。如果你已经知道最终的字符串大概有多长,那预先指定stringbuilder的大小可以避免多次重新分配缓冲。
####总结
因此,当你为一个类重写tostring()方法时,如果字符串操作比较简单,那就可以信赖编译器,它会为你合理地构造最终的字符串结果。但是,如果你要在tostring()方法中使用循环,那么最好自己创建一个stringbuilder对象,用它来构造最终的结果。
.printf()()方法模仿自c的printf,可以格式化字符串,两者是完全等价的。
java中,ter类处理。
()方法参考了c中的sprintf()方法,以生成格式化的string对象,是一个static方法,()方法一样的参数,但返回一个string对象。当你只需使用format()方法一次的时候,该方法很方便。
import;importter;
publicclasssimpleformat{
publicstaticvoidmain(string[]args){
intx=5;
doubley=5.324667;
.printf(”row1:[%d%f]n“,x,y);.format(”row1:[%d%f]n“,x,y);
formatterf=newformatter();(”row1:[%d%f]n“,x,y);
stringstr=(”row1:[%d%f]n",x,y);.println(str);integer[][]a={
{1,2,3},{4,5,6},{7,8,3},{9,10,6}
};
.println(string(a));
}}
12.序列化控制
当我们对序列化进行控制时,可能某个特定子对象不想让java序列化机制自动保存与恢复。如果子对象表示的是我们不希望将其序列化的敏感信息(如密码),通常会面临这种情况。即使对象中的这些信息是private属性,一经序列化处理,人们就可以通过读取文件或者拦截网络传输的方式来访问到它。有两种办法可以防止对象的敏感部分被序列化:
实现externalizable代替实现serializable接口来对序列化过程进行控制,externalizable继承了serializable接口,同时增添了两个方法:writeexternal()和readexternal()。
两者在反序列化时的区别:
-对serializable对象反序列化时,由于serializable对象完全以它存储的二进制位为基础来构造,因此并不会调用任何构造函数,因此serializable类无需默认构造函数,但是当serializable类的父类没有实现serializable接口时,反序列化过程会调用父类的默认构造函数,因此该父类必需有默认构造函数,否则会抛异常。-对externalizable对象反序列化时,会先调用类的不带参数的构造方法,这是有别于默认反序列方式的。如果把类的不带参数的构造方法删除,或者把该构造方法的访问权限设置为private、默认或protected级别,dexception:novalidconstructor异常,因此externalizable对象必须有默认构造函数,而且必需是public的。
-externalizable的替代方法:如果不是特别坚持实现externalizable接口,那么还有另一种方法。我们可以实现serializable接口,并添加writeobject()和readobject()的方法。一旦对象被序列化或者重新装配,就会分别调用那两个方法。也就是说,只要提供了这两个方法,就会优先使用它们,而不考虑默认的序列化机制。
这些方法必须含有下列准确的签名:
privatevoidwriteobject(objectoutputstreamstream)throwsioexception;
privatevoidreadobject(objectinputstreamstream)
throwsioexception,classnotfoundexception
-可以用transient关键字逐个字段地关闭序列化,它的意思是“不用麻烦你保存或恢复数据—我自己会处理的”。由于externalizable对象在默认情况下不保存它们的任何字段,所以transient关键字只能和serializable对象一起使用。
一基础知识点
1.面向对象程序设计(object-orientedprogrammingoop),uml(unitiedmodellinglanguage统一建模语言)。将对象想像成“服务提供者”,它们看起来像什么?能够提供哪些服务?需要哪些对象?中动态绑定是默认行为。java采用动态内存分配方式,通过new操作在堆(heap)的内存池中动态创建对象。java存储结构类型:1)寄存器2)堆栈,主要存储对象引用3)堆,主要用于存放所有的java对象4)常量存储,也就是程序代码区5)非ram存储,如流对象和持久化对象。基本类型不用new来创建变量,而且这个变量直接存储”值”,并置于堆栈中。eger和bigdecimal的使用。当变量作为类的成员使用时当变量作为类的成员使用时,java才确保给定其默认初当变量作为类的成员使用时始值,但是在方法中定义的变量,它有可能是任意值。面向对象的程序设计可以归纳为“向对象发送消息”。关键字static。c只能为public和protected成员进行文档注释,但是也可以通过-private进行标记注释。javadoc常用方法:@see引用其他类,link#memberlabel},{@{@docroot},{@inheritdoc},@version,@author,@since,@param,@return,@throws,@deprecated。5.整数除法会直接去掉结果的小数位。基本类型的对象如果直接对它们赋值,对象指向同一个常量存储区,但是如果通过对象来初始化则会指向不同的堆的存储区。如:
stringst1=newstring(“a”);stringst2=newstring(“a”);st1==st2falsestringst1=“a”;stringst2=“a”;st1==st2true
6.逻辑操作符:与(&&)、或(||)、非(!),其中与(&&)、或(||)会产生短路现象。&|也支持逻辑也支持逻辑
运算操作。运算操作
7.直接常量中l代表long,f代表float,d代表double。显示二进制形式的话,可以通过integer和long类的静态方法tobinarystring()。如:rystring(10l)。
8.在返回void的方法中没有return语句,那么在该方法的结尾处会有一个隐式的return。
一般情况下每个方法都会有一个显示的return语句。用于强行退出循环,不执行循环中剩余的语句,而continue则停止执行当前的迭代,然后退回循环起始处,开始下一次迭代。goto仍是java的一个保留字,但在语言中并未使用它。break和continue与标签一起使用,可以中断循环,直到标签所在的地方。this用法:publicclassleaf{inti=0;leafincrement(){i++;returnthis;}}
10.回答一个新技术的问题大概思路和步骤是:我们想干什么,怎么干,干的过程中遇到了什么问题,现在用什么方式来解决。答题时,先答是什么,再答有什么作用和要注意什么(这部分最重要,展现自己的心得)。ze的使用:垃圾回收只与内存有关,当“垃圾回收”时,finalize()得到调用。java中的对象都能被垃圾回收器回收,但是在“本地方法”的情况下,有可能在分配内存时采用类似c语言的做法通过malloc()函数来分配存储空间时,这时只能通过free()函数来释放空间,而这些释放操作必须要放到finalize()方法中,垃圾回收器才能正确的释放内存。“垃圾回收”都不保证一定会发生。
12.垃圾回收原理:引用记数引用记数是一种简单但速度很慢的垃圾回收技术。每个对象都含有一个引用记数
java编程思想第四版读书笔记
引用记数器,当有引用连接至对象时,引用计数加1。当引用离开作用域或被置为null时,引用计数减1。垃圾回收器会在含有全部对象的列表上遍历,当发现某个对象的引用计数为0时,就立即释放其占用的空间。定位交互自引用的对象组所需的工作量极大,所以并没有被应用于任何一种java虚拟机中。java虚拟机采用一种自适应自适应的垃圾
回收技术,java虚拟机会自适应进行监视,如果所有对象都很稳定,垃圾回收器的效率降低的话,就切换到“标记-清扫”方式;同样,java虚拟机会跟踪“标记-清扫”的效果,要是堆空间出现很多碎片,就会切换回“停止-复制”方式。“停止-复制”,先暂停程序的运行,然后将所有存活存活的对象从当前存活堆复制到另一个堆,没有被复制的全部都是垃圾。“标记-清扫”,从堆栈和静态存储区出发,遍历所有的引用,进而找出所有存活的对象,每当它找到一个存活对象,就会给对象设一个标记,这个过程中不会回收任何对象,只有全部标记工作完成的时候,清理动作才会开始。在清理过程中,没有标记的对象将被释放,不会发生下任何复制动作。13.初始化顺序:先静态对象,后“非静态”对象,先变量,再构造函数,然后是方法。静态初始化只有在必要时刻才会进行,如果不引用其对象,那么该对象中的静态成员将不会被创建,而且它们只会在第一次被访问时进行初始化,其后不会再次被初始化。14.对象的创建过程:1)当首次创建对象时,或类的静态方法/静态域首次被访问时,java解释器必须查找类路径,文件。2),有关静态初始化的所有动作都会执行。3)当用new创建对象时,在堆上为对象分配存储空间,而且这块存储空间会被清零,也就是说它会自动地将对象中的所有基本类型数据都设置成默认值,而引用是被设置成null。4)执行所有出现于字段定义处的初始化动作。5)执行构造器。15.数组:常用方法的使用。binarysearch(),copyof(),aslist(),copyofrange(),equals(),fill(),sort(),tostring(),hashcode()。可变参数列表:voidf(floati,character„args)。枚举类型:enum,它可以在switch语句内使用。16.类的结构依次为:1)包的注释2)package的设置3)import导入设置4)类的注释5)类的编写。的访问权限:类的访问权限只有public和默认包访问权限,成员和方法有public,protected,默认包访问权限和private。使用类的客户端程序是无法访问包访问权限成员的。包访问权限的类的对象可以由包内任何其他类来创建和使用,但是包外则不行。18.为了继承,一般的规则是将所有的数据成员都指定为private,将所有的方法指定为public或protected。java会自动在导出类的构造器中插入对基类构造器的调用。调用基类的构造器必须是你在导出类构造器中要做的第一件事。19.代理,它是继承与组合之间的中庸之道,因为我们将一个成员对象置于所要构造的类中(就像组合),但与此同时我们在新类中暴露了该成员对象的所有方法(就像继承)。20.清理方法的顺序:首先,执行类的所有特定的清理动作,其顺序同生成顺序相反;然后,调用基类的清理方法。除了内存之外,不能依赖垃圾回收器去做任何事,如果需要进行清理,最好是编写自己的清理方法,但不要使用finalize()。@override注解可以防止在你不想重载时而意外地进行了重载。21.组合与继承之间的选择:组合技术通常用于想在新类中使用现有类的功能而非它的接口,也就是在新类的嵌入某个对象,让其实现所需要的功能,但新类的用户看到的只是为新类所定义的接口,而非所嵌入对象的接口,一般情况下会在新类中嵌入一个现有类的private对象。而继承,它是指使用某个现有类,并开发一个它的特殊版本。“is-a”(是一个)的关系是用继承来表达的,而“has-a”(有一个)的关系则是用组合来表达的。的用法:根据惯例,既是static又是final的域将用大写表示,并使用下划线分隔各个单词。类中所有的private方法都隐式的指定为是final的。final类中所有的方法都隐式指定为是final的。当前用hashmap替代了hashtable,用arraylist替代了vector。
java编程思想第四版读书笔记
中除了static方法和final方法(private方法属于final方法)之外,其他所有的方法都是后期绑定。接口中的属性都是publicstaticfinal的,方法都是public24.多态:只有普通的方法调用可以是多态的。任何域访问操作都将由编译器解析,因此不是多态的。如果某个方法是静态的,它的行为也不具有多态性。25.初始化的实际过程:1)在其他任
何事物发生之前,将分配给对象的存储空间初始化成二进制的零。2)如前所述那样调用基类构造器,此时,调用被覆盖后的方法(要在调用子类构造器之前调用)。3)按照声明的顺序调用成员的初始化方法。4)调用导出类的构造器主体。编写构造器时有一条有效的准则:“用尽可能简单的方法使对象进入正常状态;如果可以的话,避免调用其他方法”。在构造器内唯一能够安全调用的那些方法是基类中的final方法(也适用于private方法,它们自动属于final方法)。这些方法不能被覆盖。26.一条通用的准则是:“用继承表达行为间的差异,并用字段表达状态上的变化”。27.一个内部类的对象能访问其外围对象的所有成员,还拥有其外围类的所有元素的访问权。在内部类中,如果你需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟圆点和this,(outerclassout=)。有时你可能想要告知某些其他对象,去创建其某个内部类的对象,可以在new表达式中提供对其他外部类对象的引用,语法(outerclassout=newouterclass,lassinner=innerclass())。在拥有外部类对象之前是不可能创建内部类对象的,但是,嵌套类(静态内部类)除外。如果定义一个匿名内部类,并希望它使用一个在其外部定义的对象,那么其参数引用必须是final的。匿名类的实例初始化的实际效果就是构造器,而且你不能重载实例初始化方法,它可以扩展类,也可以实现接口,但是实现接口,也只能实现一个接口。28.嵌套类(静态内部类):1)要创建嵌套类的对象,并不需要其外围类的对象;2)不能从嵌套类的对象中访问非静态的外围类对象。30.为什么需要内部类:1)每个内部类都能独立继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。2)内部类可以更好的实现“多重继承”。3)内部类可以有多个实例,每个实例都有自己的状态信息,并且与其外围类对象的信息相互独立。4)在单个外围类中,可以让多个内部类以不同的方式实现同一个接口或继承同一个类。5)创建内部类对象的时刻并不依赖于外围类对象的创建。
6)内部类并没有令人迷惑的“is-a”关系,它是一个独立的实体。31.闭包:它是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。通过内部类提供闭包的功能是优良的解决方案。使用局部内部类而不使用匿名内部类的的理由是需要不止一个该内部类的对象,或者需要一个已命名的构造器。32.内部类的继承:内部类的构造器必须连接到指向其外围类对象的引用,必须在构造器内使用如下语法:();33.容器:list、set、query、map。程序中不应该使用过时的vector,hashtable和stack。常用的类有:collection,collections,arrays,arraylist,linkedlist,hashset,treeset,linkedhashset,hashmap,treemap,linkedhashmap,query,stack,priorityquery迭代器:iteratror,listiterator3
java编程思想第四版读书笔记
34.异常:把当前异常对象重新抛出时,printstacktrace()方法显示原来异常抛出点的调用栈信息,要想更新这个信息,可以调用fillinstacktrace()方法。如:throw(exception)stacktrace()。35.异常链:在捕获一个异常后抛出另一个异常,而且希望把原始异常的信息保存下来。现在所有的throwable的子类在构造器中都可以接受一个cause(因由)对象作为参数。这个cause就用来表示原始异常,这样通过把原始异常传递给新的异常,使得即使在当前位置创建并抛出了新的异常,也能通过这个异常链追踪到异常最初发生的位置。只有三种基本异常类(error、exception、runtimeexception)提供了带cause参数的构造器,其他的异常只能使用initcause()方法。36.当覆盖方法的时候,只能抛出在基类方法的异常说明里列出的那些异常,或者抛出的异常的子类或者不抛出异常。标准异常:throwable这个java类被用来表示任何可以作为异常被抛出的类。throwable对象可分为两种类型(指从throwable继承而得到的类型):error用来表示编译时和系统错误(除
特殊情况外,一般不用你关心);exception是可以被抛出的基本类型,在java类库、用户方法以及运行时故障中都可能抛出exception型异常。所以java程序员关心的基类型通常是exception。eexception:java运行时异常,它属于java的标准运行时检测的一部分,它会自动被java虚拟机抛出,它也被称为“不受检查异常”,这种异常属于错误,将被自动捕获,不用自己进行处理。除此之外的异常需要进行声明,并进行捕获或都向上抛出。只能在代码中忽略runtimeexception(及其子类)类型的异常,其他类型异常的处理都是由编译器强制实施的。runtimeexception代表的编程错误:1)无法预料的错误;2)作为程序同,应该在代码中进行检查的错误。会捕获基类异常本身以及所有从它派生的异常,如果将基类异常放在前面,子类异常放在后面的话,子类异常永远不会被捕获,编辑器会报错。unreachablecatchblockfor
isalreadyhandledbythecatchblockforexception。
40.异常处理的一个重要目标就是把错误处理的代码同错误发生的地点分离。应该在下列情况下使用异常:1)在恰当的级别处理问题。(在知道该如何处理的情况下捕获异常)
2)解决问题并且重新调用产生异常的方法。3)进行少许修被,然后绕过异常发生的地方继续执
java编程思想第四版读书笔记
行。4)用别的数据进行计算,以代替方法预计会返回的值。5)把当前运行环境下能做的事情尽量做完,然后把相同的异常重抛到更高层。6)把当前运行环境下能做的事情尽量做完,然后把不同的异常抛到更高层。7)终止程序。8)进行简化。9)让类库和程序更安全。41.字符器:string,stringbuilder,formatter.格式化语法:%[argument_index$][flags][width][.precision]conversion在默认的情况下,数据是右对齐,不过可以通过使用“-”标志来改变对齐方向。42.正则表达式:意思是“我要插入一个正则表达式的反斜线,所以其后的字符具有特殊的意义”。数字的正则表达式是:d,普通反斜线:,换行:n,制表符:t。要表示“一个或多个之前的表达式”,使用+。“零个或多个”,使用?。(-|+)?:+在正则表达式中有特殊意义,必须使用将其转义。string类有matches()和split()方法处理正则表达式。n类和matcher类来构造正则表达式对象。包,用statice()方法来编译正则表达式生成一个pattern对象,再将你想要检索的字符串传入pattern对象的matcher()方法,它会生成一个matcher对象,最后通过操作matcher对象来实现相关功能。还可以通过scanner来完成正则表达式相关功能。44.在java中,所有的类型转换都是在运行进进行正确性检查的。e()获得class对象的引用,也可以通过object类的getclass()方法来获得。45.使用类的准备工作:1)加载,这是由类加载器执行,该步骤将查找字节码,并从这些字节码中创建一个class对象。2)链接,将验证类中的字节码,为静态域分配存储空间,并且如果必需的话,将解析这个类创建的对其他类的所有引用。3)初始化,如果该类具有超类,则对其初始化,执行静态初始化器和静态初始化块。(构造器隐式地是静态的)。和反射之间的区别:对rtti来说,文件。而对于反射机制来说,.class文件在编译时是不可获取的,文件。47.泛型中t代表类型class,要显式地指明类型,必须在点操作符与方法名之间插入尖括号,然后把类型置于尖括号中;如果是在定义该方法的类的内部,必须在点操作符之前使用this关键字,如果是使用static的方法,必须在点操作符之前加上类名。在泛型代码内部,无法获得任何有关泛型参数类型的信息。在泛型中创建数组,tance()。泛型(
)会擦除出现在方法或类内部的有关实际类型的信息。但是可以引入类型标签来传递类型信息(class
)。
、
superclass>48.任何基本类型都不能作为类型参数,但是可以使用它们的包装类,如不能使用arraylist,但可以使用arraylist49.异常的分类:1)error:称为错误,由java虚拟机生成并抛出,包括动态链接失败、虚拟机错误等,程序对其不做处理;2)exception:所有异常类的父类,其子类对应了各种各样可能出现的异常事件,一般需要用户显示的声明或捕获;
3)runtimeexception:一类特殊的异常,如被0除、数据下标超范围等,其产生比较频繁,处理麻烦,如果显式的声明或捕获将会对程序可读性和运行效率影响很大。因此由系统自动检测并将它们交给缺省的异常处理程序(用户可不必对其处理)。50.使用自定义异常一般步骤:1)ion类声明自己的异常类;2)在方法适当的位置生成自定义异常的实例,并用throw语句抛出;在方法的声明部分用throws3)语句声明该方法可能抛出的异常。
二专项复习
1.容器2.正则表达式
java编程思想第四版读书笔记
3.设计模式4.异常5.泛型6.反射7.多线程
常用包(加粗是抽象类,斜体是接口,普通是类)三j2se常用包(加粗是抽象类,斜体是接口,普通是类)
提供利用java编程语言进行程序设计的基础类。process、processbuilder、runtime、system、string、object、class、classloader、math、compiler、thread、threadgroup、runnable、threadlocal、inheritablethreadlocal、package.utilarraylist、arrays、collection、collections、linkedlist、hashset、treeset、iterator、listiterator、map、hashmap、treemap、comparator历史遗留的类:dictionary、hashtable、properties、stack、vector、enumeration使用迭代函数的步骤:1)通过调用类集的iterator()或listiterator()方法获得对类集头的迭代函数;2)建立一个调用hasnext()方法的循环,只要hasnext()返回true,就进行循环迭代;3)在循环内部,通过调用next()方法来得到每一个元素。gregoriancalendar、timezone、simpletimezone、locale、dateformat、bitset、calendar、simpledateformat、random、observer、observable、timer、timertaskgregoriancalendar定义了两个域:ad和bc。它们代表由公历定义的两个纪元。bc公元前,ad公元后inputstream、outputstreamreader、writeroutputstream、readerwriter、fileinputstream、fileoutputstream、inputstreamoutputstreamreaderwriterbytearrayinputstream、bytearrayoutputstream、filteroutputstream、filterinputstream、bufferedinputstream、bufferedoutputstream、sequenceinputstream、printstream、randomaccessfilefilereader、filewriter、chararrayreader、chararraywriter、bufferedreader、bufferedwriter、printwriterobjectinputstream、izable或alizable接口的对象才能从流读取。inetaddress、url、urlconnectionjava中有两类tcp套接字。一种是服务器端的,另一种是客户端的。serversocket类设计成在等待客户建立连接之前不做任何事的“监听器”。socket类为建立连向服务器套接字以及启动协议交换而设计。或文件。所以,http:///与http:///是相同的。java通过两个类实现udp协议顶层的数据报:datagrampacket对象是数据容器,datagramsocket是用来发送和接受datagrampackets的机制。
这是一份试图提纲挈领的读书笔记,《java编程思想》这本八百多页的书娓娓道来地包含了太多细节,这对读者是非常贴心的,我也强烈建议细细读这本书,如果你希望在短时间内学会java这种语言,那么这本书不是最好的选择,你可以看看谭浩强系列。我把看这本书的过程中,个人觉得每一章中最重要的思想、用整理在这里,希望自己回顾的时候有所参照和提高。也希望大家带着同样的目的来看本篇读书笔记。
第一章对象导论
比起过程型语言编写的程序,用面向对象语言编写的程序更加简单、易于理解、可复用。《c++编程思想》里也有这一章,是一个抛砖引自己的玉的章节,不明白可以跳过,回头再看。
第二章一切都是对象
java语言里面,一切都是对象,并且程序员是通过引用来操纵对象。一个简单的例子,非常轻松地让读者进入java的世界。需要注意的是java数据会储存在5个不同的地方:寄存器、堆栈、堆、常量存储、非ram存储,用new创建的一般对象都放在堆中,而特殊的基本对象放在堆栈中,如果想把基本对象也放在堆中,需要包装基本类型。
第三章操作符
java中的操作符语法类似于c,所以学习起来一点困难也没有。要特别注意两个比较大的整数相加或者相乘的时候的溢出问题,用long或者biginteger解决这个问题。
第四章控制执行流程
我想起《pointeronc》这本书第一章就有这一句话,本书适合那些希望迅速学习一门新语言而不是被“为什么if和for很重要”的弱智问题耽搁进度的读者。呵呵,这一章很不厌其烦地介绍了运算、操作符优先级、类型转换、选择循环等基本特性,有c或者c++编程经验的读者可以大概浏览一下。
第五章初始化和清理
关于初始化:
1.初始化很重要,一定不要忘记。而且java编译器会很好的防止使用未初始化数据的意外,这是比c和c++更优的地方。
2.编译器初始化的顺序为:
a.类首次加载的时候,有关静态初始化的所有动作都会执行。
a1.类的加载包括首次创建该类型的对象,或者该类的静态方法/静态域首次被访问
a2.静态域的初始化在一切初始化之前,即静态变量散布在代码不同的地方,它们也会在任何方法(包括构造器)调用之前被初始化
b.当用newcalssname()创建对象的时候,会在堆上开辟足够的存储空间,这块存储空间被清零,然后执行字段的初始化动作。(这里的字段初始化都是非静态的,因为静态的变量已经在a中执行完毕,而且静态变量存储在不同的地方,静态数据只占用一份存储空间)
c.执行构造器
关于清理
c++关于清理的部分包含很大不确定性。目前需要知道的事情是,正常情况下,我们是不需要调用finalize方法的,而且垃圾回收区会自动回收不再使用的对象,同时我们需要自己注意一些需要关闭的文件。
需要注意的是,用=对数组进行“赋值”的时候,实际上是引用的传递,就是说,二者指向同一堆。
第六章访问权限控制
关于包
你应该有一个自己的域名,这样发布你的java程序的时候,就可以将你的包名设置为你的域名倒转。想要正确让包工作,要正确设置classpath,对于新手来说,这的确是一个挑战。我当初就难到了。
关于访问权限修饰词
值得注意的是,如果两个编译单元放在同一个目录下并且都没有设置包名的话,他们对于对方都是拥有包访问权限的。访问权限修饰词是修饰方法和数据,而不是类。类只有两种访问权限,包访问权限或public访问权限。默认为包访问权限。如果不希望其它任何人对该类拥有访问权限,可以把所有的构造器设置为private。但是有一个例外,可以通过该类自己的static成员内部创建(于是就有了工厂设计模式和单例设计模式)。
第七章复用类
有三种方法复用类:组合,继承,代理。
组合即是在新的类里面放上已经定义的类的对象,然后通过调用它的方法来实现自己的功能。
继承是通过extends关键词继承某一父类,这样就能访问父类的所有public方法(因此为了继承,一般的规则是将父类的所有数据成员都指定为private,将所有的方法都指定为public)。子类的初始化需要注意的是,(当创建了一个子类的对象时,该对象包含一个基类的子对象)java会在子类的构造器中插入对基类默认构造器的调用。但是如果没有默认的基类构造器,或者想调用一个带参数的基类构造器,就必须用关键词super显式地编写调用基类构造器的语句,并且配上适当的参数列表。
代理很有意思,(我们姑且使用导出类和基类这样的字眼,但要清楚我们不是在讨论继承里面的关键词)在导出类里保存一个基类的对象,然后用自己的方法对该基类的种种方法进行包装。
如何决定使用哪种方法复用类呢?is-a就继承,has-a就用组合。而且,组合比继承总体上使用更广泛、代价更小。
向上转型
这个就牛逼了,第八章,第九章,第十章都与此密切相关。看完本书之后印象最深的就是向上转型了。
使用final的原因有很多种,一定要弄清楚为什么使用final,是由于设计还是效率。
final作用于数据的时候:final作用在基本对象比如int上,该值就成为不可改变的,一旦被初始化就无法再被更改,但是作用在普通的对象引用的时候,final使引用恒定不变,但是引用指向的对象是可变的。编译器需要我们确保final对象一定要被初始化,我们可以通过在构造器中初始化他们,以达到相对自由的效果(称为空白final,我认为这个名字容易让人误解)。java允许在参数列表中以声明的方式将参数指明为final,这一特性主要用来向匿名内部类传递数据(这很重要)。
final作用于方法的时候,说明作者想保持该方法在继承的过程中不被改变,并且不被覆盖。同时,被final修饰的方法会被关闭“动态绑定”,这样编译器就会为final方法调用生成“有限”有效的代码。之所以说有限,是因为随着编译器的牛逼,它生成的代码越来越有效。
final作用于类的时候,即是作者声明对该类的设计不允许任何继承。
学习得更深入一些,可能对以下事实感到有兴趣:java中所有的事物都是对象,每个类的编译代码都存在于电脑中的文件夹里(文件夹的层次根据反转域名得到),该文件只有在需要使用程序代码时才被加载。具体的说,就是“类在其任何static成员函数(包括构造函数)被访问时加载”。第八章多态
多态的重要基本原理就是向上转型:继承允许将对象视为它自己本身的类型或其基类型加以处处理。
将一个方法调用和一个方法主题关联起来称为绑定,java中所有的方法都是后期绑定(除了static方法和final方法),所以我们可以编写只与基类打交道的程序代码,并且这些代码对所有的导出类都可以正确运行。
(为什么static不动态绑定:因为static方法的主要用法就是用类名。方法名这样的方式来调用,不存在“发送消息给某个对象,让对象判断自己怎么做”这样的情况。
为什么final不动态绑定:这是早期final的一种用法,由程序员指定某方法为final,意味着程序员明了动态绑定的机制,并且声明该方法不需要动态绑定,这样可以获得更好的性能。这种用法已经很少使用了。)
初始化的时候,导出类的构造函数会自动调用基类的默认构造函数,此过程一直递归到最基本的基类。如果需要调用有参数的构造函数就需要手动执行。反过来,如果需要进行清理工作(大部分时候我们都不需要),务必手动执行基类的清理工作先。比如继承链的每个类都实现dispose()方法,那么执行某个类的清理工作的时候,e()。不过此种情况下,e()之前释放成员对象,清理顺序与执行顺序是相反的。
此外,构造器方面有更加复杂的调用机制,我们不用理它,只需要知道一条有效的准则“用尽可能简单的方法使对象进入正常状态,如果可以的话避免调用其它方法”。
java编译器能够允许向上多态,就是因为java的机制能保存对象的类型信息,即rtti,正因为这种机制,java编译器也允许向下转型,以获得扩展类的“扩展出”的方法。(另,扩展类“扩展”了方法的这种继承不是“纯继承”,这样做好不好?用户自己度量)。向下转型失败的话会抛出一个classcastexception。
虽然这一章都是在讲多态,但是多态并不总是解决问题最好的方案,它有可能使事情不必要地复杂起来,我们应该总是优先考虑更加灵活的组合。
第九章接口
一种专门提供“接口”的类叫抽象类,若含有至少一个abstract方法,该类就必须被声明为abstract的。抽象方法没有方法体,派生类必须实现它,否则派生类也必须被生命为抽象的。
interface关键词使抽象的概念更进了一步:1.这个“类”完全抽象。2.一个类可以向上转型为多种interface。要让一个类遵循某个特定接口,需要使用implement关键字。
在这一章中出现了“策略设计模式”这个词。创建一个能够根据所传递的参数对象的不同而具有不同行为的方法,被称为策略设计模式。
策略设计模式跟适配器设计模式联合使用可以提供非常强大的功能,比如我们遇到了无法更改的类(别人编写的),想要它满足我们的接口然后放到设计模式里面去(当然满足了接口之后的用法就不止如此了),就可以编写一个适配器,包装该类同时产生我所需要的接口。
使用抽象类和接口的两个原因是:1.在多重继承关系中(这真的很常见,看看javaapi就知道了),导出类可以被向上转型为每一个接口。2.防止客户端程序员创建该类的对象。那么我们该使用抽象类还是接口呢?事实上,如果知道某事物应该成为一个基类,那么第一选择应该是使它成为一个接口。
接口之间的继承能够形成很好的体系,更像我们的现实生活。但是要特别注意的是,在不同接口中使用相同的方法名通常会造成代码可读性的混乱,令人不快。
工厂方法设计模式是又一个重要的设计模式。我们在代码中增加额外的间接性,一个重要的原因是想要创建框架。