第一篇:Java读书笔记
读书笔记
1、类和对象:类是用来定义一组对象共同具有的状态和行为的模版。而对象是现实世界中个体或事物的抽象表示,并且封装了它们的属性和行为。
2、为了防止命名冲突,Java采用了管理类文件的机制,即类包机制。(package)定义包名的语法格式:
“package包名;”
Java命名规则要求包的命名需要使用小写字母。
3、成员变量和成员方法:
1成员变量是在类体中定义的变量即全局变量,○成员变量一般用于定义对象的状态。成员变量是给对象使用的,每个对象被创建后都会有属于自己的属性,即成员变量。通过修改这些属性,从而改变对象的某个状态。
2成员方法是对象行为的描述。○
定义成员方法的语法格式:
“[修饰符] 返回值类型方法名([形参表]){
······//方法体
}”
修饰符:可以是:publicprivateprotactedstaticfinall等;
返回值类型:如果方法需要返回值,必须这里声明方法的返回值类型,可以是基
本数据类型(int short double booleanvoid等),也可以是对象
类型如:数组、字符串等。
形参表:(可选部分)说明方法被调用时应该向它传递的数据。形参表可以有一
个也可以有多个,当有多个形参时每个形参之间要用“,”隔开。
1创建对象:创建对象使用到new语句。
4、对象:○
声明并创建对象的语法格式如下:
“类名对象名=new 类构造方法()”
构造方法:构造方法是类创建对象是必须执行的方法,用于构造一个新的对象并
初始化对象属性。
2访问对象的属性: ○
语法格式:
“对象名.属性”
3执行对象的行为:○对象的行为就是对象的成员方法,通常说调用或执行对象的某个方法。
语法格式:
“对象名.成员方法名();”
4对象的销毁:Java提供了垃圾回收机制,对不再使用的对象会自动销毁,也可○
以在程序中显式的为某个对象赋null值,使对象不再被使用。
垃圾回收机制会找到并销毁它,释放该对象所占用的资源。
语法格式:
“对象名=null”
1创建类:Java中使用class关键字来创建类。
5、类:○
声明并创建类的语法格式:
“权限修饰符 class类名{
类体;
}”
权限修饰符:(可选项)可以使用public protected private 或者省略这3者。类体主要由成员变量和方法两部分组成。
2成员变量:在Java中对象的属性也称为成员变量。○
声明并创建成员变量的语法格式:
“权限修饰符数据类型成员变量名;”
(用static关键字定义的成员变量被称为静态成员变量即类变量。静态成员变量不是分配给每个对象的,而是属于累的变量。它在内存中是唯一的,可以直接使用“类名.成员变量名”的格式去访问,他在内存中的位置是固定的,是该类的所有实例对象所共享的存储单元。)
3成员方法:在Java语言中使用成员方法对应与类对象的行为。○
定义成员方法的语法格式:
“权限修饰符返回值类型方法名(参数类型参数名){
·······//方法体
}”
(同类变量一样,由static关键字定义的成员方法被称为类方法,类方法可以不必创建对象而由类直接访问,静态方法不可以直接调用非静态方法)4局部变量:○在方法体中声明的变量为局部变量,局部变量的有效范围为方法体结束。
5this关键字:在Java语言中用this关键字来代表类对象的引用,this关键字被○
隐式地用于引用对象的成员变量和方法。(在类方法中不可以使用this关键字。)this关键字和和对象都可以调用成员变量和方法,两者的区别:
事实上,this引用的就是本类的一个对象,在局部变量或方法参数覆盖了成员变量时,就要添加this关键字明确引用的是类成员还是局部变量的方法参数。6类的构造方法:构造方法与类同名。○
7类的主方法:主方法是类的入口点,它定义了程序从何处开始;主方法提供对○
程序流向的控制,Java编译器通过主方法来执行程序。
1)主方法是静态的,所以如果要直接在主方法体中
调用其他方法,则该方法必须也是静态的。
2)主方法没有返回值。
3)主方法的形参为数组。其中args[0]~args[n]分别
代表程序的第一个参数到第n个参数,可以使用
args.length获取参数的个数。
Java中完整的类声明格式:
“权限修饰符class类名{
权限修饰符数据类型成员变量名1;
权限修饰符数据类型成员变量名2;
·······
权限修饰符数据类型成员变量名n;
权限修饰符返回值类型成员方法名1(形参类型形参变量){ 方法体
}
权限修饰符返回值类型成员方法名2(形参类型形参变量){ 方法体
}
······
权限修饰符返回值类型成员方法名n(形参类型形参变量){ 方法体
} }”
6.由于类的主方法是静态方法所以不可以调用类中的非静态方法所以当需要调用非静态方法时必须创建对象来调用。在输入不同类型的数据时要使用不同的Scanner类对象,否则只能实现输入一种数据类型。
7.abstract关键字是定义抽象类的关键字。使用abstract定义的发放成为抽象方法。抽象方法没有方法体。抽象方法的唯一意义就是被重载。
第二篇:java高级Inside_JVM读书笔记
本文首先介绍一下Java虚拟机的生存周期,然后大致介绍JVM的体系结构,最后对体系结构中的各个部分进行详细介绍。
(首先这里澄清两个概念:JVM实例和JVM执行引擎实例,JVM实例对应了一个独立运行的java程序,而JVM执行引擎实例则对应了属于用户运行程序的线程;也就是JVM实例是进程级别,而执行引擎是线程级别的。)
一、JVM的生命周期
JVM实例的诞生:当启动一个Java程序时,一个JVM实例就产生了,任何一个拥有public static void main(String[] args)函数的class都可以作为JVM实例运行的起点,既然如此,那么JVM如何知道是运行class A的main而不是运行class B的main呢?这就需要显式的告诉JVM类名,也就是我们平时运行java程序命令的由来,如java classA hello world,这里java是告诉os运行Sun java 2 SDK的java虚拟机,而classA则指出了运行JVM所需要的类名。
JVM实例的运行:main()作为该程序初始线程的起点,任何其他线程均由该线程启动。JVM内部有两种线程:守护线程和非守护线程,main()属于非守护线程,守护线程通常由JVM自己使用,java程序也可以标明自己创建的线程是守护线程。
JVM实例的消亡:当程序中的所有非守护线程都终止时,JVM才退出;若安全管理器允许,程序也可以使用Runtime类或者System.exit()来退出。
二、JVM的体系结构
粗略分来,JVM的内部体系结构分为三部分,分别是:类装载器(ClassLoader)子系统,运行时数据区,和执行引擎。
下面将先介绍类装载器,然后是执行引擎,最后是运行时数据区
1,类装载器,顾名思义,就是用来装载.class文件的。JVM的两种类装载器包括:启动类装载器和用户自定义类装载器,启动类装载器是JVM实现的一部分,用户自定义类装载器则是Java程序的一部分,必须是ClassLoader类的子类。(下面所述情况是针对Sun JDK1.2)
动类装载器:只在系统类(java API的类文件)的安装路径查找要装入的类
用户自定义类装载器:
系统类装载器:在JVM启动时创建,用来在CLASSPATH目录下查找要装入的类
其他用户自定义类装载器:这里有必要先说一下ClassLoader类的几个方法,了解它们对于了解自定义类装载器如何装载.class文件至关重要。
protected final Class defineClass(String name, byte data[], int offset, int length)
protected final Class defineClass(String name, byte data[], int offset, int length, ProtectionDomain protectionDomain);
protected final Class findSystemClass(String name)
protected final void resolveClass(Class c)
defineClass用来将二进制class文件(新类型)导入到方法区,也就是这里指的类是用户自定义的类(也
就是负责装载类)
findSystemClass通过类型的全限定名,先通过系统类装载器或者启动类装载器来装载,并返回
Class对象。
ResolveClass: 让类装载器进行连接动作(包括验证,分配内存初始化,将类型中的符号引用解析为
直接引用),这里涉及到java命名空间的问题,JVM保证被一个类装载器装载的类所引用的所有类都被这个类装载器装载,同一个类装载器装载的类之间可以相互访问,但是不同类装载器装载的类看不见对方,从而实现了有效的屏蔽。
2,执行引擎:它或者在执行字节码,或者执行本地方法
要说执行引擎,就不得不的指令集,每一条指令包含一个单字节的操作码,后面跟0个或者多个
操作数。
(一)指令集以栈为设计中心,而非以寄存器为中心
这种指令集设计如何满足Java体系的要求:
平台无关性:以栈为中心使得在只有很少register的机器上实现java更便利
compiler一般采用stack向连接优化器传递编译的中间结果,若指令集以stack为基础,则有利于运行
时进行的优化工作与执行即时编译或者自适应优化的执行引擎结合,通俗的说就是使编译和运行用的数据结构统一,更有利于优化的开展。
网络移动性:class文件的紧凑性。
安全性:指令集中绝大部分操作码都指明了操作的类型。(在装载的时候使用数据流分析期进行一
次性验证,而非在执行每条指令的时候进行验证,有利于提高执行速度)。
(二)执行技术
主要的执行技术有:解释,即时编译,自适应优化、芯片级直接执行
其中解释属于第一代JVM,即时编译JIT属于第二代JVM,自适应优化(目前Sun的HotspotJVM
采用这种技术)则吸取第一代JVM和第二代JVM的经验,采用两者结合的方式
自适应优化:开始对所有的代码都采取解释执行的方式,并监视代码执行情况,然后对那些经常调
用的方法启动一个后台线程,将其编译为本地代码,并进行仔细优化。若方法不再频繁使用,则取消编译过的代码,仍对其进行解释执行。
3,运行时数据区:主要包括:方法区,堆,java栈,PC寄存器,本地方法栈
(1)方法区和堆由所有线程共享
堆:存放所有程序在运行时创建的对象
方法区:当JVM的类装载器加载.class文件,并进行解析,把解析的类型信息放入方法区。
(2)Java栈和PC寄存器由线程独享,在新线程创建时间里
(3)本地方法栈: 存储本地方法调用的状态
上边总体介绍了运行时数据区的主要内容,下边进行详细介绍,要介绍数据区,就不得不说明JVM
中的数据类型。
JVM中的数据类型:JVM中基本的数据单元是word,而word的长度由JVM具体的实现者来决定
数据类型包括基本类型和引用类型,(1)基本类型包括:数值类型(包括除boolean外的所有的java基本数据类型),boolean(在JVM中使
用int来表示,0表示false,其他int值均表示true)和returnAddress(JVM的内部类型,用来实现
finally子句)。
(2)引用类型包括:数组类型,类类型,接口类型
前边讲述了JVM中数据的表示,下面让我们输入到JVM的数据区
首先来看方法区:
上边已经提到,方法区主要用来存储JVM从class文件中提取的类型信息,那么类型信息是如何存储的呢?众所周知,Java使用的是大端序(big—endian:即低字节的数据存储在高位内存上,如对于1234,12是高位数据,34为低位数据,则java中的存储格式应该为12存在内存的低地址,34存在内存的高地址,x86中的存储格式与之相反)来存储数据,这实际上是在class文件中数据的存储格式,但是当数据倒入到方法区中时,JVM可以以任何方式来存储它。
类型信息:包括class的全限定名,class的直接父类,类类型还是接口类型,类的修饰符(public,等),所有直接父接口的列表,Class对象提供了访问这些信息的窗口(可通过Class.forName(“”)或instance.getClass()获得),下面是Class的方法,相信大家看了会恍然大悟,(原来如此)
getName(), getSuperClass(), isInterface(), getInterfaces(), getClassLoader();
static变量作为类型信息的一部分保存
指向ClassLoader类的引用:在动态连接时装载该类中引用的其他类
指向Class类的引用:必然的,上边已述
该类型的常量池:包括直接常量(String,integer和float point常量)以及对其他类型、字段和方法的符号引用(注意:这里的常量池并不是普通意义上的存储常量的地方,这些符号引用可能是我们在编程中所接触到的变量),由于这些符号引用,使得常量池成为java程序动态连接中至关重要的部分
字段信息:普通意义上的类型中声明的字段
方法信息:类型中各个方法的信息
编译期常量:指用final声明或者用编译时已知的值初始化的类变量
class将所有的常量复制至其常量池或者其字节码流中。
方法表:一个数组,包括所有它的实例可能调用的实例方法的直接引用(包括从父类中继承来的)除此之外,若某个类不是抽象和本地的,还要保存方法的字节码,操作数栈和该方法的栈帧,异常表。举例:
class Lava{
private int speed = 5;
void flow(){}
}
class Volcano{
public static void main(String[] args){
Lava lava = new Lava();
lava.flow();
}
}
运行命令java Volcano;
(1)JVM找到Volcano.class倒入,并提取相应的类型信息到方法区。通过执行方法区中的字节码,JVM
执行main()方法,(执行时会一直保存指向Vocano类的常量池的指针)
(2)Main()中第一条指令告诉JVM需为列在常量池第一项的类分配内存(此处再次说明了常量池并非
只存储常量信息),然后JVM找到常量池的第一项,发现是对Lava类的符号引用,则检查方法区,看Lava类是否装载,结果是还未装载,则查找“Lava.class”,将类型信息写入方法区,并将方法区Lava类信息的指针来替换Volcano原常量池中的符号引用,即用直接引用来替换符号引用。
(3)JVM看到new关键字,准备为Lava分配内存,根据Volcano的常量池的第一项找到Lava在方法区的位置,并分析需要多少对空间,确定后,在堆上分配空间,并将speed变量初始为0,并将lava对象的引用压到栈中
(4)调用lava的flow()方法
好了,大致了解了方法区的内容后,让我们来看看堆
java对象的堆实现:
java对象主要由实例变量(包括自己所属的类和其父类声明的)以及指向方法区中类数据的指针,指向方法表的指针,对象锁(非必需),等待集合(非必需),GC相关的数据(非必需)(主要视GC算法而定,如对于标记并清除算法,需要标记对象是否被引用,以及是否已调用finalize()方法)。
那么为什么java对象中要有指向类数据的指针呢?我们从几个方面来考虑
首先:当程序中将一个对象引用转为另一个类型时,如何检查转换是否允许?需用到类数据
其次:动态绑定时,并不是需要引用类型,而是需要运行时类型,这里的迷惑是:为什么类数据中保存的是实际类型,而非引用类型?这个问题先留下来,我想在后续的读书笔记中应该能明白
指向方法表的指针:这里和C++的VTBL是类似的,有利于提高方法调用的效率
对象锁:用来实现多个线程对共享数据的互斥访问
等待集合:用来让多个线程为完成共同目标而协调功过。(注意Object类中的wait(),notify(),notifyAll()方法)。
Java数组的堆实现:数组也拥有一个和他们的类相关联的Class实例,具有相同dimension和type的数组是同一个类的实例。数组类名的表示:如[[Ljava/lang/Object 表示Object[][],[I表示int[],[[[B表示byte[][][]
至此,堆已大致介绍完毕,下面来介绍程序计数器和java栈
程序计数器:为每个线程独有,在线程启动时创建,若thread执行java方法,则PC保存下一条执行指令的地址。
若thread执行native方法,则Pc的值为undefined
Java栈:java栈以帧为单位保存线程的运行状态,java栈只有两种操作,帧的压栈和出栈。
每个帧代表一个方法,java方法有两种返回方式,return和抛出异常,两种方式都会导致该方法对应的帧出栈和释放内存。
帧的组成:局部变量区(包括方法参数和局部变量,对于instance方法,还要首先保存this类型,其中方法参数按照声明顺序严格放置,局部变量可以任意放置),操作数栈,帧数据区(用来帮助支持常量池的解析,正常方法返回和异常处理)。
本地方法栈:依赖于本地方法的实现,如某个JVM实现的本地方法借口使用C连接模型,则本地方法栈就是C栈,可以说某线程在调用本地方法时,就进入了一个不受JVM限制的领域,也就是JVM可以利用本地方法来动态扩展本身。
第三篇:Java编程思想读书笔记
这是一份试图提纲挈领的读书笔记,《java编程思想》这本八百多页的书娓娓道来地包含了太多细节,这对读者是非常贴心的,我也强烈建议细细读这本书,如果你希望在短时间内学会java这种语言,那么这本书不是最好的选择,你可以看看谭浩强系列。我把看这本书的过程中,个人觉得每一章中最重要的思想、用整理在这里,希望自己回顾的时候有所参照和提高。也希望大家带着同样的目的来看本篇读书笔记。
第一章 对象导论
比起过程型语言编写的程序,用面向对象语言编写的程序更加简单、易于理解、可复用。《c++编程思想》里也有这一章,是一个抛砖引自己的玉的章节,不明白可以跳过,回头再看。
第二章 一切都是对象
java语言里面,一切都是对象,并且程序员是通过引用来操纵对象。一个简单的例子,非常轻松地让读者进入java的世界。需要注意的是java数据会储存在5个不同的地方:寄存器、堆栈、堆、常量存储、非ram存储,用new创建的一般对象都放在堆中,而特殊的基本对象放在堆栈中,如果想把基本对象也放在堆中,需要包装基本类型。
第三章 操作符
java中的操作符语法类似于c,所以学习起来一点困难也没有。要特别注意两个比较大的整数相加或者相乘的时候的溢出问题,用long或者biginteger解决这个问题。
第四章 控制执行流程
我想起《pointer on c》这本书第一章就有这一句话,本书适合那些希望迅速学习一门新语言而不是被“为什么if和for很重要”的弱智问题耽搁进度的读者。呵呵,这一章很不厌其烦地介绍了运算、操作符优先级、类型转换、选择循环等基本特性,有c或者c++编程经验的读者可以大概浏览一下。
第五章 初始化和清理
关于初始化:
1.初始化很重要,一定不要忘记。而且java编译器会很好的防止使用未初始化数据的意外,这是比c和c++更优的地方。
2.编译器初始化的顺序为:
a.类首次加载的时候,有关静态初始化的所有动作都会执行。
a1.类的加载包括首次创建该类型的对象,或者该类的静态方法/静态域首次被访问
a2.静态域的初始化在一切初始化之前,即静态变量散布在代码不同的地方,它们也会在任何方法(包括构造器)调用之前被初始化
b.当用new calssname()创建对象的时候,会在堆上开辟足够的存储空间,这块存储空间被清零,然后执行字段的初始化动作。(这里的字段初始化都是非静态的,因为静态的变量已经在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()方法,那么执行某个类的清理工作的时候,需要手动调用super.dispose()。不过此种情况下,务必在执行super.dispose()之前释放成员对象,清理顺序与执行顺序是相反的。
此外,构造器方面有更加复杂的调用机制,我们不用理它,只需要知道一条有效的准则“用尽可能简单的方法使对象进入正常状态,如果可以的话避免调用其它方法”。
java编译器能够允许向上多态,就是因为java的机制能保存对象的类型信息,即rtti,正因为这种机制,java编译器也允许向下转型,以获得扩展类的“扩展出”的方法。(另,扩展类“扩展”了方法的这种继承不是“纯继承”,这样做好不好?用户自己度量)。向下转型失败的话会抛出一个classcastexception。
虽然这一章都是在讲多态,但是多态并不总是解决问题最好的方案,它有可能使事情不必要地复杂起来,我们应该总是优先考虑更加灵活的组合。
第九章 接口
一种专门提供“接口”的类叫抽象类,若含有至少一个abstract方法,该类就必须被声明为abstract的。抽象方法没有方法体,派生类必须实现它,否则派生类也必须被生命为抽象的。
interface关键词使抽象的概念更进了一步:1.这个“类”完全抽象。2.一个类可以向上转型为多种interface。要让一个类遵循某个特定接口,需要使用implement关键字。
在这一章中出现了“策略设计模式”这个词。创建一个能够根据所传递的参数对象的不同而具有不同行为的方法,被称为策略设计模式。
策略设计模式跟适配器设计模式联合使用可以提供非常强大的功能,比如我们遇到了无法更改的类(别人编写的),想要它满足我们的接口然后放到设计模式里面去(当然满足了接口之后的用法就不止如此了),就可以编写一个适配器,包装该类同时产生我所需要的接口。
使用抽象类和接口的两个原因是:1.在多重继承关系中(这真的很常见,看看java api就知道了),导出类可以被向上转型为每一个接口。2.防止客户端程序员创建该类的对象。那么我们该使用抽象类还是接口呢?事实上,如果知道某事物应该成为一个基类,那么第一选择应该是使它成为一个接口。
接口之间的继承能够形成很好的体系,更像我们的现实生活。但是要特别注意的是,在不同接口中使用相同的方法名通常会造成代码可读性的混乱,令人不快。
工厂方法设计模式是又一个重要的设计模式。我们在代码中增加额外的间接性,一个重要的原因是想要创建框架。
第四篇:读书笔记《Java邮件开发详解》
读书笔记《Java邮件开发详解》
DNS
MX
自动回复 不填from 避免循环回复。
Pop3:可以查邮件总数、已占空间。
电子邮件的RFC822格式:
Return-Path:代表邮件的回复地址,该字段的内容由接收邮件的SMTP服务器填写,接收邮件的SMTP服务器从邮件发送程序发出的mail form命令中获得内容。
Received:通常格式是:Received from A by B for C,A为发送方,B为接收方,C为收件人的邮箱地址。常常被用来追踪邮件传输的路线和分析邮件的来源。From:
To:
Subject:
Date:
Cc:
Bcc:
邮件的头字段可以扩充成MIME类型。Content-Type: multipart/mixed;头字段指定编码方法。另外,也定义了邮件头字段的内涵。
MIME消息的头字段具体资源的数据类型和组合消息的组合关系,都是通过消息头中的Content-Type头字段来指定的,格式为“主类型/子类型”的形式出现,主类型有text、image、audio、application、multipart、message等;每个主类型下面又都有好多子类型。
MIME消息可以有三种组合关系:
Multipart/mixed 混合组合 内容可以是文本、附件等等。
Multipart/related 关联组合 如邮件正文要使用HTML代码引用内嵌的图片资源。意思是说某些资源要关联引用另外的资源。
Multipart/alternative 选择组合这三种关系是可以随意嵌套的,比如你寄一个复杂的盒子出去,这个盒子里面可以有好多好多小盒子,而每个盒子都可以看作一个MIME。
其他的头字段都是顾名思义的,略!
设置内容的另一种方法:
setContent(Object object, String type)
setContent(body, “text/html;charset=gb2312”);
另外一种客户端的发信方式:SMTP服务器功能的邮件发送程序
具有SMTP服务器功能的邮件发送程序会根据收件人地址的域名,直接连接到该域的SMTP服务器和进行邮件发送。根本用不到„„
JNDI:命名式:把对象绑定到context中;目录式:保存对象的属性信息实际应用中命名式和对象式常常结合使用。
Tomcat可以通过配置文件自己创建javax.mail.Session
META-INF/context.xml
auth=”Container” type=”javax.mail.Session” mail.smtp.host=”smtp.sina.com.cn” mail.transport.protool=”smtp” mail.smtp.auth=”true” /> 在web.xml中说明一下,有资源是被tomcat创建的,如果换了别的应用服务器也要创建。 不知道书上为什么费那么大劲找个Session出来,还是个没有校验的Session,收发邮件时都得再写用户名密码给服务器。为什么不随便new一个或者从System中调一个呢?感觉基本用不着,我自己写一个properties文件岂不是更好? JAF 对于通过JAF封装的数据,应用程序通过JAF提供的接口可以完成如下功能:。访问数据源中的数据 。获知数据源的数据类型 。获知可对数据源进行的各种操作 。用户对数据源执行某种操作时,自动创建执行该操作的软件部件的实例对象 好像是用来封装数据源的(比如:声音文件,java文件),这样的好处是?可以识别文件类型对文件直接操作?不知道,回头再看看。好像是附件用的,ms。 邮件搜索 用于创建逻辑组合关系的类AND、OR、NOT、Comparison 用于创建具体搜索条件的类DATE、CONTENT、HEADER 例:SearchTerm st = new AndTerm(new FromStringTerm(“zx@sina.com”),new ReceivedDateTerm(ComparisonTerm.EQ, new Date())); 这里主要说明ComparisonTerm类,常用于日期类的比较其中使用留个常量来标识六种不同的操作:EQ(=)、GE(>=)、GT(>)、LE(<=)、LT(<)、NE(!=)邮件的解析与显示 如果Message的类型是“text/*”直接显示之。如果Message的类型 (getContentType())是“multipart/mixed”,调getContent()方法得到Multipart,对其调getCount()看看其中有多少个BodyPart,遍历之;当MIME类型为“text/*”显示之,当MIME是图片、声音或附件等二进制时,调 getDataHandler方法得到封装数据的对象,然后调其getInputSteam„„.;当MIME类型为“mutlipart/mixed”再次遍历得到其中的BodyPart。 注意:Message.isMimeType(“multipart/*”)这个方法居然可以写通配附?!看看API的解释:If the subType of mimeType is the special character '*', then the subtype is ignored during the comparison.。 处理内嵌类型的multipart(multipart/related)直接交给IE浏览器去显示就好了: response.setContentType(”message/rfc822”); message.writeTo(response.getOutputStream()); 总结: 总的来说这本书讲的很基础,前半部分告诉我们什么是email、email是如何收发的及email的格式、着重说明了smtp协议和pop3协议,甚至告诉大家怎么使用协议的命令收发邮件,这些对于使用API做web开发的程序员来说用处不是很大,但第3章邮件的组织结构讲述的还是比较好的;后半部分主要对Javamail进行说明,但是我个人觉得不如IBM的在线教程介绍的实用,主要是例子写的别扭,呵呵。不过里面有一些细节可以起到拓宽知识面的作用。 此书对开发webmail用处不是很大,比如对于pop3协议来说,如何标记邮件,如何建立文件夹,如何移动邮件,甚至通讯录什么的都一点也没有提到,讲邮件搜索的时候没有说邮件过滤能不能通过搜索实现或者还是有更好的办法。更深入一点的比如用工厂模式解耦合、使用线程池提高性能等更是只字未提。哎~看来还得再找找,实在不行就操作数据库,那样麻烦很多但是应该什么都能实现。最近看webmail看的很不爽,网上这样的东西也很少,感觉没有一个套路...郁闷~~ 一 基础知识点 1.面向对象程序设计(Object-oriented Programming OOP),UML(Unitied Modelling Language 统一建模语言)。将对象想像成“服务提供者”,它们看起来像什么?能够提供哪 些服务?需要哪些对象? 2.Java中动态绑定是默认行为。Java采用动态内存分配方式,通过new操作在堆(Heap)的内 存池中动态创建对象。Java存储结构类型:1)寄存器2)堆栈,主要存储对象引用3)堆,主要用于存放所有的Java对象4)常量存储,也就是程序代码区5)非RAM存储,如流对象 和持久化对象。基本类型不用new来创建变量,而且这个变量直接存储”值”,并置于堆栈中。3.BigInteger和BigDecimal的使用。当变量作为类的成员使用时 当变量作为类的成员使用时,Java才确保给定其默认初 当变量作为类的成员使用时 始值,但是在方法中定义的变量,它有可能是任意值。面向对象的程序设计可以归纳为“向 对象发送消息”。关键字Static。4.Javadoc只能为public和protected成员进行文档注释,但是也可以通过-private进行标记注 释。Javadoc常用方法: @see 引用其他类,link package.class#member label},{@ {@docRoot},{@inheritDoc},@version,@ author,@since,@param,@return,@throws,@deprecated。5.整数除法会直接去掉结果的小数位。基本类型的对象如果直接对它们赋值,对象指向同 一个常量存储区,但是如果通过对象来初始化则会指向不同的堆的存储区。如: String st1 = new String(“A”);String st2 = new String(“A”);st1==st2 false String st1 = “A”;String st2 = “A”;st1==st2 true 6.逻辑操作符:与(&&)、或(||)、非(!),其中与(&&)、或(||)会产生短路现象。& |也支持逻辑 也支持逻辑 运算操作。运算操作 7.直接常量中L代表Long,F代表Float,D代表Double。显示二进制形式的话,可以通过Integer 和Long类的静态方法toBinaryString()。如:Long.toBinaryString(10L)。 8.在返回void的方法中没有return语句,那么在该方法的结尾处会有一个隐式的return。 一 般情况下每个方法都会有一个显示的return语句。9.Break用于强行退出循环,不执行循环中剩余的语句,而continue则停止执行当前的迭代,然后退回循环起始处,开始下一次迭代。goto仍是Java的一个保留字,但在语言中并未使用它。Break和continue与标签一起使用,可以中断循环,直到标签所在的地方。This用法: public class Leaf { int I = 0;Leaf increment(){ i++;return this;} } 10.回答一个新技术的问题大概思路和步骤是:我们想干什么,怎么干,干的过程中遇到了 什么问题,现在用什么方式来解决。答题时,先答是什么,再答有什么作用和要注意什么(这 部分最重要,展现自己的心得)。11.finalize的使用:垃圾回收只与内存有关,当“垃圾回收”时,finalize()得到调用。Java中 的对象都能被垃圾回收器回收,但是在“本地方法”的情况下,有可能在分配内存时采用类 似C语言的做法通过malloc()函数来分配存储空间时,这时只能通过free()函数来释放空间,而这些释放操作必须要放到finalize()方法中,垃圾回收器才能正确的释放内存。“垃圾回收” 都不保证一定会发生。 12.垃圾回收原理:引用记数 引用记数是一种简单但速度很慢的垃圾回收技术。每个对象都含有一个 引用记数 Java 编程思想第四版 读书笔记 引用记数器,当有引用连接至对象时,引用计数加1。当引用离开作用域或被置为null时,引用计数减1。垃圾回收器会在含有全部对象的列表上遍历,当发现某个对象的引用计数为0 时,就立即释放其占用的空间。定位交互自引用的对象组所需的工作量极大,所以并没有被 应用于任何一种Java虚拟机中。Java虚拟机采用一种自适应 自适应的垃圾 回收技术,Java虚拟机会 自适应 进行监视,如果所有对象都很稳定,垃圾回收器的效率降低的话,就切换到“标记-清扫” 方式;同样,Java虚拟机会跟踪“标记-清扫”的效果,要是堆空间出现很多碎片,就会切 换回“停止-复制”方式。“停止-复制”,先暂停程序的运行,然后将所有存活 存活的对象从当前 存活 堆复制到另一个堆,没有被复制的全部都是垃圾。“标记-清扫”,从堆栈和静态存储区出发,遍历所有的引用,进而找出所有存活的对象,每当它找到一个存活对象,就会给对象设一个 标记,这个过程中不会回收任何对象,只有全部标记工作完成的时候,清理动作才会开始。在清理过程中,没有标记的对象将被释放,不会发生下任何复制动作。13.初始化顺序:先静态对象,后“非静态”对象,先变量,再构造函数,然后是方法。静态 初始化只有在必要时刻才会进行,如果不引用其对象,那么该对象中的静态成员将不会被创 建,而且它们只会在第一次被访问时进行初始化,其后不会再次被初始化。14.对象的创建过程:1)当首次创建对象时,或类的静态方法/静态域首次被访问时,Java 解释器必须查找类路径,以定位.class文件。2)载入.class,有关静态初始化的所有动作都会 执行。3)当用new 创建对象时,在堆上为对象分配存储空间,而且这块存储空间会被清零,也就是说它会自动地将对象中的所有基本类型数据都设置成默认值,而引用是被设置成 null。4)执行所有出现于字段定义处的初始化动作。5)执行构造器。15.数组:java.util.Arrays常用方法的使用。binarySearch(),copyOf(),asList(),copyOfRange(),equals(),fill(),sort(),toString(),hashCode()。可变 参数列表:void f(float i,Character„ args)。枚举类型:enum,它可以在switch语句内使用。16.类的结构依次为:1)包的注释2)package的设置3)import导入设置4)类的注释5)类的编 写。17.Java的访问权限:类的访问权限只有public和默认包访问权限,成员和方法有 public,protected,默认包访问权限和private。使用类的客户端程序是无法访问包访问权限成员 的。包访问权限的类的对象可以由包内任何其他类来创建和使用,但是包外则不行。18.为了继承,一般的规则是将所有的数据成员都指定为private,将所有的方法指定为public 或protected。Java会自动在导出类的构造器中插入对基类构造器的调用。调用基类的构造器 必须是你在导出类构造器中要做的第一件事。19.代理,它是继承与组合之间的中庸之道,因为我们将一个成员对象置于所要构造的类中(就像组合),但与此同时我们在新类中暴露了该成员对象的所有方法(就像继承)。20.清理方法的顺序:首先,执行类的所有特定的清理动作,其顺序同生成顺序相反;然后,调用基类的清理方法。除了内存之外,不能依赖垃圾回收器去做任何事,如果需要进行清理,最好是编写自己的清理方法,但不要使用finalize()。@Override注解可以防止在你不想重载 时而意外地进行了重载。21.组合与继承之间的选择:组合技术通常用于想在新类中使用现有类的功能而非它的接口,也就是在新类的嵌入某个对象,让其实现所需要的功能,但新类的用户看到的只是为新类所 定义的接口,而非所嵌入对象的接口,一般情况下会在新类中嵌入一个现有类的private对象。而继承,它是指使用某个现有类,并开发一个它的特殊版本。“is-a”(是一个)的关系是用 继承来表达的,而“has-a”(有一个)的关系则是用组合来表达的。22.final的用法:根据惯例,既是static又是final的域将用大写表示,并使用下划线分隔各个单 词。类中所有的private方法都隐式的指定为是final的。final类中所有的方法都隐式指定为是 final的。当前用HashMap替代了Hashtable,用ArrayList替代了Vector。 Java 编程思想第四版 读书笔记 23.Java中除了static方法和final方法(private方法属于final方法)之外,其他所有的方法都是 后期绑定。接口中的属性都是public static final的,方法都是public 24.多态:只有普通的方法调用可以是多态的。任何域访问操作都将由编译器解析,因此不 是多态的。如果某个方法是静态的,它的行为也不具有多态性。25.初始化的实际过程:1)在其他任 何事物发生之前,将分配给对象的存储空间初始化成二 进制的零。2)如前所述那样调用基类构造器,此时,调用被覆盖后的方法(要在调用子类 构造器之前调用)。3)按照声明的顺序调用成员的初始化方法。4)调用导出类的构造器主 体。编写构造器时有一条有效的准则: “用尽可能简单的方法使对象进入正常状态;如果可 以的话,避免调用其他方法”。在构造器内唯一能够安全调用的那些方法是基类中的final方 法(也适用于private方法,它们自动属于final方法)。这些方法不能被覆盖。26.一条通用的准则是: “用继承表达行为间的差异,并用字段表达状态上的变化”。27.一个内部类的对象能访问其外围对象的所有成员,还拥有其外围类的所有元素的访问权。在内部类中,如果你需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟圆点和 this,(OuterClass out = OuterClass.this)。有时你可能想要告知某些其他对象,去创建其某个 内部类的对象,可以在new表达式中提供对其他外部类对象的引用,需要使用.new语法(OuterClass out = new OuterClass, OuterClass.InnerClass inner = out.new InnerClass())。在拥 有外部类对象之前是不可能创建内部类对象的,但是,嵌套类(静态内部类)除外。如果定 义一个匿名内部类,并希望它使用一个在其外部定义的对象,那么其参数引用必须是final 的。匿名类的实例初始化的实际效果就是构造器,而且你不能重载实例初始化方法,它可以 扩展类,也可以实现接口,但是实现接口,也只能实现一个接口。28.嵌套类(静态内部类):1)要创建嵌套类的对象,并不需要其外围类的对象;2)不能从嵌 套类的对象中访问非静态的外围类对象。30.为什么需要内部类:1)每个内部类都能独立继承自一个(接口的)实现,所以无论外围 类是否已经继承了某个(接口的)实现,对于内部类都没有影响。2)内部类可以更好的实 现“多重继承”。3)内部类可以有多个实例,每个实例都有自己的状态信息,并且与其外围 类对象的信息相互独立。4)在单个外围类中,可以让多个内部类以不同的方式实现同一个 接口或继承同一个类。5)创建内部类对象的时刻并不依赖于外围类对象的创建。 6)内部类 并没有令人迷惑的“is-a”关系,它是一个独立的实体。31.闭包:它是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。通过内部类提供闭包的功能是优良的解决方案。使用局部内部类而不使用匿名内部类的的理 由是需要不止一个该内部类的对象,或者需要一个已命名的构造器。32.内部类的继承:内部类的构造器必须连接到指向其外围类对象的引用,必须在构造器内 使用如下语法:enclosingClassReference.super();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)e.fillInStackTrace()。35.异常链:在捕获一个异常后抛出另一个异常,而且希望把原始异常的信息保存下来。现 在所有的Throwable的子类在构造器中都可以接受一个cause(因由)对象作为参数。这个cause 就用来表示原始异常,这样通过把原始异常传递给新的异常,使得即使在当前位置创建并抛 出了新的异常,也能通过这个异常链追踪到异常最初发生的位置。只有三种基本异常类(Error、Exception、RuntimeException)提供了带cause参数的构造器,其他的异常只能使用 initCause()方法。36.当覆盖方法的时候,只能抛出在基类方法的异常说明里列出的那些异常,或者抛出的异 常的子类或者不抛出异常。37.Java标准异常: Throwable这个Java类被用来表示任何可以作为异常被抛出的类。Throwable 对象可分为两种类型(指从Throwable继承而得到的类型):Error用来表示编译时和系统错误(除 特殊情况外,一般不用你关心);Exception是可以被抛出的基本类型,在Java类库、用 户方法以及运行时故障中都可能抛出Exception型异常。所以Java程序员关心的基类型通常是 Exception。38.RuntimeException:Java运行时异常,它属于Java的标准运行时检测的一部分,它会自动被 Java虚拟机抛出,它也被称为“不受检查异常”,这种异常属于错误,将被自动捕获,不用 自己进行处理。除此之外的异常需要进行声明,并进行捕获或都向上抛出。只能在代码中忽 略RuntimeException(及其子类)类型的异常,其他类型异常的处理都是由编译器强制实施 的。RuntimeException代表的编程错误:1)无法预料的错误;2)作为程序同,应该在代码 中进行检查的错误。39.catch会捕获基类异常本身以及所有从它派生的异常,如果将基类异常放在前面,子类异 常放在后面的话,子类异常永远不会被捕获,编辑器会报错。Unreachable catch block for RuntimeException.It is already handled by the catch block for Exception。 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()方法处理正则表达式。43.我们通过java.util.regex.Pattern类和Matcher类来构造正则表达式对象。导入java.util.regex 包,用static Pattern.compile()方法来编译正则表达式生成一个Pattern对象,再将你想要检索 的字符串传入Pattern对象的matcher()方法,它会生成一个Matcher对象,最后通过操作Matcher 对象来实现相关功能。还可以通过Scanner来完成正则表达式相关功能。44.在Java中,所有的类型转换都是在运行进进行正确性检查的。通过Class.forName()获得 Class对象的引用,也可以通过Object类的getClass()方法来获得。45.使用类的准备工作:1)加载,这是由类加载器执行,该步骤将查找字节码,并从这些字 节码中创建一个Class对象。2)链接,将验证类中的字节码,为静态域分配存储空间,并且 如果必需的话,将解析这个类创建的对其他类的所有引用。3)初始化,如果该类具有超类,则对其初始化,执行静态初始化器和静态初始化块。(构造器隐式地是静态的)。46.RTTI和反射之间的区别:对RTTI来说,编译器在编译时打开和检查.class文件。而对于反 射机制来说,.class文件在编译时是不可获取的,所以是在运行时打开和检查.class文件。47.泛型中T代表类型Class,要显式地指明类型,必须在点操作符与方法名之间插入尖括号,然后把类型置于尖括号中;如果是在定义该方法的类的内部,必须在点操作符之前使用this 关键字,如果是使用static的方法,必须在点操作符之前加上类名。在泛型代码内部,无法 获得任何有关泛型参数类型的信息。在泛型中创建数组,推荐使用Array.newInstance()。泛 型( SuperClass> 48.任何基本类型都不能作为类型参数,但是可以使用它们的包装类,如不能使用 ArrayList,但可以使用ArrayList 49.异常的分类:1)Error:称为错误,由Java虚拟机生成并抛出,包括动态链接失败、虚拟 机错误等,程序对其不做处理;2)Exception:所有异常类的父类,其子类对应了各种各样可 能出现的异常事件,一般需要用户显示的声明或捕获; 3)Runtime Exception:一类特殊的异常,如被0除、数据下标超范围等,其产生比较频繁,处理麻烦,如果显式的声明或捕获将会对 程序可读性和运行效率影响很大。因此由系统自动检测并将它们交给缺省的异常处理程序(用户可不必对其处理)。50.使用自定义异常一般步骤:1)通过继承java.lang.Exception类声明自己的异常类;2)在 方法适当的位置生成自定义异常的实例,并用throw语句抛出; 在方法的声明部分用throws 3)语句声明该方法可能抛出的异常。 二 专项复习 1.容器 2.正则表达式 Java 编程思想第四版 读书笔记 3.设计模式 4.异常 5.泛型 6.反射 7.多线程 8.IO 常用包(加粗是抽象类,斜体是接口,普通是类)三 J2SE 常用包(加粗是抽象类,斜体是接口,普通是类) 1.java.lang 提供利用 Java 编程语言进行程序设计的基础类。Process、ProcessBuilder、Runtime、System、String、Object、Class、ClassLoader、Math、Compiler、Thread、ThreadGroup、Runnable、ThreadLocal、InheritableThreadLocal、Package 2.java.util ArrayList、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、TimerTask GregorianCalendar定义了两个域:AD和BC。它们代表由公历定义的两个纪元。BC公元前,AD公元后 3.java.io InputStream、OutputStream Reader、Writer OutputStream、Reader Writer、FileInputStream、FileOutputStream、InputStream OutputStream Reader Writer ByteArrayInputStream、ByteArrayOutputStream、FilterOutputStream、FilterInputStream、BufferedInputStream、BufferedOutputStream、SequenceInputStream、PrintStream、RandomAccessFile FileReader、FileWriter、CharArrayReader、CharArrayWriter、BufferedReader、BufferedWriter、PrintWriter ObjectInputStream、ObjectOutputStream序列化的类必须实现java.io.Serializable 或 java.io.Externalizable 接口的对象才能从流读取。4.java.net InetAddress、URL、URLConnection Java中有两类TCP套接字。一种是服务器端的,另一种是客户端的。ServerSocket类设 计成在等待客户建立连接之前不做任何事的“监听器”。Socket类为建立连向服务器套接 字以及启动协议交换而设计。或 index.htm 文件。所以,http:/// 与http:///index.htm 是相同 的。Java通过两个类实现UDP协议顶层的数据报:DatagramPacket对象是数据容器,DatagramSocket是用来发送和接受DatagramPackets的机制。5.java.rmi第五篇:Java编程思想第四版_读书笔记