[Java]Java学习笔记(二)——类的基本语法

来源:互联网 发布:大数据2017 热门技术 编辑:程序博客网 时间:2024/05/16 00:59

  • 零碎要点

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 = ?22

如果我们直接使用 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;
0 0