Java与Internet
客户/服务器系统核心思想:
- 系统具有一个中央信息存储池,用来存储某种数据。
- 它通常存在于数据库中,根据需要将它分发给某些人员或机器集群。
- 信息存储池、用于分发信息的软件和信息与软件所驻留的机器或机群总称为服务器。
- 主流在用户机器上的软件与服务器进行通信,疑惑去信息处理信息。
- 然后将它们显示在成为客户机的用户机器上。
为了将响应延迟最小化:
- 程序员努力减轻处理任务的负载,通常是分散给客户端机器处理。
- 但有时也会使用中间件将负载分散给服务器端的其他机器。
- 中间件也被用来提高可维护性。
客户端编程技术:
- 插件(plug-in):通过这种方式,程序员可以下载一段代码,并将其插入到浏览器中适当的位置,以此为浏览器添加新功能。
- 脚本语言:代表为JavaScript,在Web浏览器之间不需要任何插件的情况下就可以得到支持。
- Java: 通过applet和Java Web Start进行客户端编程。
- .NET和C#:.NET平台相当于JVM和Java类库,C#对应Java。
服务器端编程技术:
- 大部分对服务器的请求只是请求某个文件。
- 更复杂的对服务器的请求通常涉及数据库事务。常见情形是复杂的数据库搜索请求。
- 然后服务器将结果进行格式编排,使其成为一个HTML页面发回给客户端。
- 常用Java编写成为servlet的程序来实现服务器端编程。
- servlet及其衍生物JSP,是许多开发网站的公司迁移到Java上的两个主要原因。
一切都是对象
操作数据元素在Java中得到了简化:
- 一切都被视作对象,因此可采用单一固定的语法。
- 尽管一切都看作对象,但操作的标识符实际上是对象的一个引用。
- 使用new操作符来创建一个对象,并可创建一个引用和该对象相关联。
String s = new String("asdf");
关于基本类型:
- 基本类型的创建是特例:int,char等,同C/C++创建自动变量方法相同。
- 基本类型中所有的数值类型都有正负号。
- 基本类型的大小是确定的,不像C/C++那样随硬件平台架构变化。
- char 2Byte,short 2Byte,int 4Byte,float 4Byte,double 8Byte,long 8Byte。
- 这种所占存储空间大小的不变性使得Java可移植性更强。
- 基本类型具有包装器类,使得可以在堆中创建一个非基本对象,用来表示对应的基本类型。
- JVM提供自动包装功能将基本类型转换为包装器类型,并可以反向转换。
例:
char c = 'x'; Character ch = new Character(c);
或
Character ch = new Character('x');
转换方法
Character ch = 'x'; char c = ch;
另外,Java提供了两个用于高精度计算的类,BigInterger和BigDecimal。没有对应的基本类型。
关于数组:
- Java中确保数组会被初始化,且不能在它的范围之外被访问。
- 这种范围检查是以数组上的少量内存开销和运行时的下标检查为代价的。
以下代码在C/C++中合法,但在Java中不能这样书写:
{ int x = 12; { int x = 96; // Illegal } }
C/C++中将以个较大作用域隐藏的做法,在Java中是不允许的。
Example01. Swap in JAVA is not easy.
在Java中:
- 永远不需要销毁对象。
- 对象占用内存的释放工作交给Java虚拟机的垃圾回收器完成。
基本成员的默认值:
- 若类的某个成员是基本数据类型,即使没有进行初始化,Java也会确保它活得一个默认值。
- boolean默认为false,int/short默认为0,char默认为'\u0000'(null),long默认为0L
- 然而上述确保初始化的方法并不适用于“局部”变量,即(并非某个类的字段)。
名字可见性:import指示编译器导入一个包,也就是一个类库。
注释和嵌入式文档:
- javadoc是用于提取注释的工具。
- 采用了Java编译器的某些技术,查找程序内的特殊标签。
- 它不仅解析由这些标签标记的信息,也将毗邻注释的类名或方法名抽取出来。
- javadoc输出的是一个HTML文件,可以用Web浏览器查看。
javadoc语法介绍:
- 所有的javadoc命令只能在"/**"注释中出现,和通常一样,注释结束于"*/"。
- 使用javadoc的方式主要有两种。
- 嵌入HTML
- 使用文档标签:是一些以"@"字符开头的命令。
- 共有三种类型的注释文档,分别对应与注释位置后面的三种元素:类、域和方法。
<-- 注意:javadoc只能为public和protected成员进行文档注释。(用-private标记,才会包含private注释)
部分标签示例:
- @see:引用其他类。
- @author:描述作者信息。
- @param:用于描述参数列表中的标识符。
- @return:用于描述返回值的含义。
操作符
Java操作符使用:
- 几乎所有的操作符都只能操作"基本类型"。
- 例外的操作符是"=","==","!=",这些操作符能操作所有的对象。
- 除此之外,String类支持"+"和"+="。
优先级:
- 当编译器观察到一个String后面紧跟一个"+"而"+"后紧跟一个非String元素时。
- 就会尝试将这个非String类型的元素转换为String。
对象赋值:
- 对对象进行操作时,我们真正操作的是对这个对象的引用。
- 倘若"将一个对象赋值给另一个对象",实际上是将"引用"从一个地方复制到另一个地方。
- 假若对对象使用c=d,那么c和d都指向原本只有d指向的那个对象。
Example02. Assignment for Objects.
比较操作:
- 基本类型直接使用"=="和"!="比较内容即可。
- 但两个对象用"=="和"!="比较,实际比较的是对象的引用。
- 如果想比较两个对象的实际内容是否相同,必须使用所有对象都适用的特殊方法equals()。
- 但是equals()的默认行为是比较引用,所以对于自己定义的类,需要在新类中覆盖equals()方法。
- 大多数Java类库都实现了equals()方法,以便用来比较对象的内容,而非比较对象的引用。
Example03. Comparation for Objects.
逻辑操作:
- "与","或","非"操作只可应用于布尔值。
- 与C/C++不同的是,不可将一个非布尔值当作布尔值在逻辑表达式中使用。
- 另外如果在应该使用String值的地方使用了布尔值,布尔值会自动转换成适当的文本形式。
直接常量:
- 直接常量的后缀字符标志了它的类型。
- L/l代表long型,F/f代表float型,D/d代表double型。
- 16进制数使用于所有的整数数据类型,以前缀0x/0X后跟0~9以及a-f表示。
- 如果试图将一个变量初始化成超出自身表示范围的值,编译器会像我们报告一条错误信息。
- C/C++或Java中,二进制数没有直接常量表示方法,通过使用Integer和Long类的静态方法toBinaryString()可以实现这一点。
移位操作符:
- 左移操作"<<",有符号右移操作">>"。
- Java中增加了一种"无符号"右移操作,无论正负数高位都补0,这一操作C/C++中是没有的。
关于操作符还需要注意:
- Java程序员不能像C++和C#那样实现自己的重载操作符。
- 对于字符串,如果表达式以一个字符串起头,那么后续所有操作数都必须是字符串型。
类型转换:
- Java允许我们把任何基本数据类型转换为别的基本数据类型,布尔型除外。
- 布尔型数据根本不允许进行任何类型的转换处理。
- "类"数据类型不允许进行类型转换,为了将一种类转换成另一种,需要采用特殊的方法。
- 对象可以在其所属类型的类族之间进行类型转换。
数据的截尾和舍入:
- 将float或double转型为整型值时,总是对该数字进行截尾。
- 如果想要得到舍入的结果,就需要使用java.lang.Math中的round()方法。
Java没有sizeof:
- C/C++中,需要使用sizeof的最大原因是为了"移植"。
- Java不需要sizeof()操作符来满足这方面的需要。
- 因为所有的数据类型在所有机器中的大小都是相同的,它已经被设计在语言中了。
控制执行流程
Foreach语法:
- Java SE5引入的一种新的更加简洁的for语法用于数组和容器。
- 不必创建变量去对由访问项构成的序列进行计数,foreach将自动产生每一项。
float f[] = new float[10]; for(int i = 0; i < 10; i++) { f[i] = rand.nextFloat(); } for(int x:f) { System.out.println(x); }
<-- 其余流控语句与C/C++用法相同。
Example04. Usage of Foreach
初始化与清理
构造器:
- 通过提供构造器,可确保每个对象都会得到初始化。
- 创建对象时,会在用户有能力操作对象之前自动调用相应的构造器,保证初始化进行。
- 不接收任何参数的构造器叫做默认构造器。
- Java中"初始化"和"创建"捆绑在一起,两者不能分离。
- 构造器是一种特殊类型的方法,没有返回值。
注意:
- 没有返回值与返回值为空(void)不同。
- 对于空返回值尽管方法本身不会自动返回什么,但仍可选择让它返回别的东西。
- 构造器则不会返回任何东西。
重载:
- 重载机制使同名方法可以有不同实现。
- 前提是每个重载的方法必须有独一无二的参数类型列表。
- 传入数据类型小于方法参数类型,实际数据类型就会提升。
- 传入数据类型方法大于参数类型,必须进行类型转换,否则会报错。
思考:为什么通过返回值来区分重载方法是行不通?
void f() {} int f() {return 1;} ... f();
默认构造器:
- 如果类中没有构造器,则编译器会自动创建一个默认构造器。
- 如果已经定义了一个构造器,编译器就不会自动创建默认构造器。
this关键字:
- 只能在方法内部使用。表示"调用方法的那个对象"的引用。
- 常用于需要返回当前对象引用时。
- this关键字对于将当前对象传递给其他方法也很有用。
- this关键字的另一个用法是构造器中调用构造器。
构造器中调用构造器:
- 构造器中,如果为this添加了参数列表,就有了不同的含义。
- 这将产生对符合此参数列表的某个构造器的明确调用。
- 这种用法尽管可以用this调用一个构造器,但却不能调用两个。
- 此外,必须将构造器调用置于起始处,否则编译会报错。
Example05. Using this in Constructor
finalize方法:
- Java允许在类中定义一个名为finalize()的方法。
- 其工作原理假定为:
- 一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize()方法。
- 并在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。
- 之所以需要finalize方法往往由于在分配内存时采用了new以外的方式分配堆内存。
- 而Java中不允许创建局部对象,必须使用new创建对象。
- 所以这种new以外的分配方式主要发生在使用"本地方法"的情况下。在Java中调用非Java代码分配内存。
- finalize另一个用法是对终结对象条件的验证。
成员初始化:
- Java尽力保证所有变量在使用前都会得到初始化。
- 对局部变量,Java以编译时错误的形式来贯彻这种保证。
- Java可以在定义类成员变量的地方为成员赋值。(C++只有在C++ 11中才支持)
初始化顺序:
- 初始化顺序是先静态对象,而后是非静态对象。
- 类得内部,变量定义的先后顺序决定了初始化的顺序。
- 即使变量定义散布于方法定义之间,也会在包括构造器在内的任何方法被调用之前得到初始化。
- 静态成员初始化仅在第一个对象创建或第一次访问静态数据时才会进行,此后不会再次被初始化。
显式初始化:
- Java允许将多个静态初始化动作组织成一个特殊的静态子句。
- 用花括号括起来跟在static关键字之后。
- 这种代码也仅在首次生成类对象或首次访问类静态数据成员时执行一次。
- 对于非静态变量也可用语句块的方式用花括号括起来,不带static关键字,在创建对象时执行。
Example06. Order of Initialization
数组:
- 要定义一个数组,只需在类型名后加方括号。例:int[] a; 或 int a[];
- 所有数组都又一个固有成员length,记录组内元素个数,不能对其更改。
- 数组访问下标范围1~length-1,超出边界会直接出现运行时错误。
- 数组元素中的基本数据类型会自动初始化为空值。
- 可以用花括号括起来的列表来初始化对象数组。
数组创建时需要注意:
- 如果创建了一个非基本类型的数组,实际创建的就是一个引用数组。
- 创建之后必须通过创建新的对象并把对象赋值给引用,初始化才算结束。
- 如果忘记创建对象并试图使用数组中的空引用,就会在运行时产生异常。
<-- C/C++中数组越界会被默默接受,允许访问任何内存,许多错误由此产生。
Java提供可变参数列表方便使用
static void printArray(Object... args) { for(Object obj : args) System.out.print(obj + " "); System.out.println(); }
可变参数列表中可使用任何类型的参数,包括基本类型。
<-- Java中提供了枚举变量enum,用法同C/C++类似,略。