java 类的基本语法大都与其他语言一样,就不再赘述了,将那些不太一样的地方列为零碎要点,如下。其他的高级部分,接下去会另起一篇。
零碎要点
1、使用 final 来创建常量。
虽然 Java 保留了 const 关键字,但暂未使用。
2、变量的参数传递一直都是值传递。
测试如下:
package com.go.java.test;
public class Val {
private int a;
public Val(int v)
{
a = v;
}
public int GetVal()
{
return a;
}
}
package com.go.java.test;
public class Test {
public static void main(String[] args) {
Val a = new Val(1);
System.out.println(a.GetVal());
}
static void Fun(Val v) {
Val b = new Val(2);
v = b;
}
}
输出为 1
。
显然,这值得批评,java 的效率,估计被这个拖累不少。
3、不合法的规则:
native 方法可以绕过 Java 语言的存取控制机制,进行某些操作。
如 System 类里的 in、out、err 声明分别为:
public final static InputStream in = null;
public final static PrintStream out = null;
public final static PrintStream err = null;
但在 set 函数中,却对 final 变量的值进行了修改,况且还是用与 Java 不和的引用参数方式进行修改:
public static void setIn(InputStream in) {
checkIO();
setIn0(in);
}
public static void setOut(PrintStream out) {
checkIO();
setOut0(out);
}
public static void setErr(PrintStream err) {
checkIO();
setErr0(err);
}
这种做法,我只能说请尽量不要这样用,除非。。。
Java 的 System 类是不允许实例化的,声明为 public final class
,相比而言,C# 的做法更为合理,独立出一个 Console 类,将 Console 类定义为 static 类,成员也都定义为 static,不允许继承又不能实例化。
Java 6 之后也引入了 Console 类,但只是用于密码读取。
4、static 的使用与 C# 基本一致,例外的是类的 static 成员可以用类对象调用。
又一个不合理的设计。还是那句话,建议不要使用。
5、Java 与 C# 相同,每个类都可以有 主函数,多个类都有主函数时,需要指定其中一个为入口函数。在 java 里这样做比较方便,所以在类的主函数里进行类的单元测试,和在外面进行单元测试的写法是差不多的。
在 C# 习惯用 NUnit,在 Java 里还真不习惯用这种写法,还是使用 JUnit 吧。
6、this 的使用:
(1)当成员函数的参数与类成员变量名冲突时,用于区分。
(2)在构造函数中调用另一个构造函数。
比如有构造函数 A(int x, int y);
和 A(int x)
,
那么我们可以在第二个构造函数中使用 this(x, 0);
这样的方式来简化代码。
当然这种方式也可以提取出一个私有函数来实现。
7、初始化块:
无论是 C++ 或 C#,我们都可以在 成员变量定义时直接赋予一个不涉及到其他变量的值,在 Java 里,可以在独立到初始化块中执行这个赋值操作。
Java 里,有普通变量初始化块和静态变量初始化块,允许所有可以在函数内部出现的语法功能。
package com.go.java.test;
import java.util.Scanner;
public class Init {
int a;
static int b;
{
a = 1;
}
static
{
Scanner in = new Scanner(System.in);
System.out.println("set b = ?");
b = in.nextInt();
System.out.println(b);
in.close();
}
}
package com.go.java.test;
public class Test {
public static void main(String[] args) {
new Init();
// Val a = new Val(1);
// System.out.println(a.GetVal());
}
static void Fun(Val v) {
Val b = new Val(2);
v = b;
}
}
控制台显示
set b = ?
2
2
如果我们直接使用 Java Init调用上面的类 Init,上面控制台那些也会照样执行,不过,会提示 “main is not define”。解决的方式是在 static 块的代码最后加上 System.exit(0);
。
这不是与那些借助漏洞实现那些没有主函数的 C 代码是同种性质吗!
还是那句:不要使用这种写法。
8、对象的析构处理:
Java 可以为任何一个类提供 finalize 方法,这样,在垃圾回收该对象前,会先调用这个方法。然而,并不推荐使用这个方法来关闭文件或释放句柄等,因为我们并不能确定该方法会在什么时候被调用。
另外还有个名为 System.runFinalizersOnExit(true);
的方法能够确保 finalize 方法在 Java 退出前被调用,但该方法并不安全,也不推荐使用。
有一种替代的方法:
使用方法 Runtime.addShutdownHook(Thread hook)
添加关闭钩,该方法会在 Java 程序退出前调用,
package com.go.java.test;
public class Test {
public static void main(String[] args) {
Thread one = new Thread() {
public void run() {
System.out.println("shut down thread one");
}
};
Runtime.getRuntime().addShutdownHook(one);
System.out.println("start thread one");
// new Init();
// Val a = new Val(1);
// System.out.println(a.GetVal());
}
static void Fun(Val v) {
Val b = new Val(2);
v = b;
}
}
然而有些资源需要立即释放,这时,我们就需要使用一个 close() 方法来执行,使用的方法有两种,一种是在 try-catch-finally 的 finally 块中调用 close() 方法,另一种是 实现 Closeable 接口,这样就无需 finally 块了(不过此时如果 close 函数执行出错,抛出的是 IOException),在try 块执行完成或出错时,都会执行 close() 方法。
相比而言,Go 使用 defer 来执行类似的工作显得比较简便、灵活。
9、最后,再说说 Java 的问题:类的默认成员属性居然是 protected,这又和 C++ / C# 不同了。
访问属性的规则:
private:对本类可见。
public:对所有类可见。
protected:对本包可见。默认。
虽然一直强调不要省去访问属性关键字,但还有一些人总会遗漏。正如 《Java核心技术 · 基础知识》里提到的,Java 自己的库中都存在这种问题:
/**
* This represents the warning message that is
* to be displayed in a non secure window. ie :
* a window that has a security manager installed that denies
* {@code AWTPermission("showWindowWithoutWarningBanner")}.
* This message can be displayed anywhere in the window.
*
* @serial
* @see #getWarningString
*/
String warningString;
为什么说这样做有问题呢,下面是 sublime text 的搜索结果,warningString 并不可以直接被赋值。
1380 public final String getWarningString() {
1381: return warningString;
1382 }
1383
1384 private void setWarningString() {
1385: warningString = null;
1386 SecurityManager sm = System.getSecurityManager();
1387 if (sm != null) {
....
1392 // for getting the property! We don't want the
1393 // above checkPermission call to always succeed!
1394: warningString = AccessController.doPrivileged(
1395 new GetPropertyAction("awt.appletWarning",
1396 "Java Applet Window"));
况且所有使用到 warningString 的外部代码都是通过 get 和 set 方法实现的,java 真该把它改为 private。
不过还好,看了本地化代码,C++的,就没有这个问题
private:
//some other codes
WCHAR * warningString;