John Jiang

a cup of Java, cheers!
https://github.com/johnshajiang/blog

   :: 首页 ::  :: 联系 :: 聚合  :: 管理 ::
  131 随笔 :: 1 文章 :: 530 评论 :: 0 Trackbacks
Java Weed
平时在学习、应用Java的过程中,遇到的一些小知识,将它们收集到这里。杂草(weed)也不能丢弃嘛。(2009.09.16最后更新)

Primitive Data Type
 boolean(2)  
 byte(8)  
 char(16)  short(16) 
 int(32)   float(32)
 long(64)  double(64) 


All Possible Combinations of Features and Modifers
 Modifer  Class  Variable Method  Constructor  FreeFloating Block 
 public
yes yes yes  yes  no
protected  no yes yes yes no
(default)
yes yes yes yes yes
private  no yes yes yes no
final  yes yes yes no no
abstract  yes no yes no no
 static no  yes  yes  no  yes 
native  no no yes no no
transient  no yes no no no
volatile  no yes no no no
synchronized  no no yes no yes

Priority of Operators
 一  +  -  ++  --  !  ~
 元 new  (type) 
 二 *  /  % 
 | +  - 
 | <<  >>  >>> 
 |  <  >  <=  >=
  |
 ==  !=
  |
  |
 ^
  |
 |
  |
 &&
 元  ||
 三元  ? :
 赋  =  *=  /=  %=  +=  -=  <<=
 值  >>=  >>>=  &=  ^=  |=

Object中的equals方法用于比较两个对象是否在同一个地址。但Object的子类会重载这个方法,所以其它类中的equals方法的功能可能就会不一样了。

初始化(Initialization)
阐述对象被创建时的若干步骤,假设以类Dog为例。
[1]当首次创建类型Dog的对象时(构造器可以看成静态方法),或者Dog类的静态方法/静态字段首次被访问时,Java解释器必须查找类路径,以定位Dog.class文件。
[2]然后载入Dog.class(这将创建一个Class对象),有关静态初始化的所有动作都会执行。故,静态初始化只在Class对象首次加载的时候进行一次。
[3]当用new Dog()创建对象的时候,首次将在堆上为Dog对象分配足够的存储空间。
[4]这块存储空间被清零,就自动地将Dog对象中的所有基本数据都设置成了缺省值,而引用则被设置成了null。
[5]执行所有出现于字段定义处的初始化动作。
[6]执行构造器。

多态(Polymophsim)
private方法属于final方法。只有非private方法才可以被覆盖;在子类中,对于其基类中的private方法,最好采用不同的名字。
类X可以从它的直接父类(接口)中继承它的所有non-private的,并且没有被类X覆盖(override)和隐藏的方法(无论它是不是abtract)。
构造器并不具有多态性,它实际上是static方法。除了在构造器内,禁止在其它地方调用构造器。

构造器调用顺序
[1]在任何事情发生之前,将分配给对象的存储空间初始化为二进制的零。
[2]调用该类的父类构造器。这个步骤会不断地反复递归下去,首先是调用这种层次结构的根的构造器,然后是下一层子类,...,直到最低层的子类。
[3]按声明顺序调用该类的成员的初始化方法。
[4]调用该类的构造器的主体。

编写构造器的一条有效准则
用尽可能简单的方法使对象进入正常状态;如果可以的话,避免调用其他方法。在构造器中唯一能够安全调用的方法是基类中的final,private(自动属于fianl方法),因为这些方法不会被覆盖。
class Dad {
    String name = "Dad";
}
class Son extends Dad {
    String name = "Son";
}
Son 的每一个对象将会有两个field "name",一个是类Dad中的"name",另一个是类Son中的"name"。具体用哪个一个"name",则将由引用变量的类型来决定。即,对于 Dad x = new Son(); x.name引用的是类Dad中的"name "("Dad");如果是Son x = new Son(); x.name,很显然引用的是类Son中的"name"。而对于方法,则仅用override原理来处理即可。

Set 的使用
实际上Set就是Collection,只是行为不同。这是继承与多态的典型应用:表现不同的行为。
使用HashSet
必须为类定义equals()方法和hashCode()方法; 使用TreeSet时必须为类定义equals()方法。但作为一种编程风格,在覆盖equals()时,也要覆盖hashCode()。

正则表达式 (Regular Expression)
lookingAt()和matches()只有在输入的最开始处就与RE匹配时才会成功(true);matches()只有在整个输入都与RE匹配时 才会成功,而lookingAt()只要求输入的第一部分与RE匹配就会成功。(Bruce认为这几个方法名不是很直观!)

如何使JTable中的列不能被移动?
使用方法JTable.getTableHeader().setReorderingAllowed(false),即可使用户不能拖动表中的各个列。

改变GUI的Look&Feel后,需要更新GUI组件
SwingUtilities.updateComponentTreeUI(java.awt.Component)

创建java.util.Date对象
由于该类中的方法不利于日期的国际化,所以它的很多构造函数与方法都被deprecated了。这些相应的功能已经由java.util.Calendar提供。一般可以使用如下方法来创建java.util.Date对象:
java.util.Calendar calendar = new java.util.Calendar();
calendar.set(int year, int month, int date);
java.util.Date date = calendar.getTime();


UnmarshalException
曾经在使用RMI时,遇到过抛该异常的情况。当时是由于我的Remote类中的一个方法的返回值是“不可序列化”的。
具体情况就是,在设计的远程接口(该接口继承自java.rmi.Remote)中有一个方法的返回值是java.sql.ResultSet,但ResultSet对象是不可序列化的。因为ResultSet没有继承Serializable,而ResultSet的实现类又没有实现
Serializable 接口,那么ResultSet对象自然就不可序列化。
解决方法就是,将返回值更换成可被序列化的对象,如String。

从jar中读文件
要读取jar中的文件,不能使用一般的创建InputStream实例之类的方法,因为InputStream没有这个能力。而需要将这个文件作为“资源”进行读取,即使用方法Class.getResourceAsStream(String name),请参见该方法的API文档
。下面会使用一个例子来描述。
假设有一个Eclipse Java工程Test,它的目录结构如下所示(Test是工程的根目录,src是源代码目录,bin是编译后的class文件的输出目录):
Test
  |--src
      |--test
          |--in
              |--files
                  |--file.txt
              |--FileInJar.java
  |--bin
      |--test
          |--in
              |--files
                  |--file.txt
              |--FileInJar.class
之所以使用这种工程目录布局,就为了在程序开发的阶段,就造成一种包结构的假象。即bin下的文件将可能会被打包到jar中,所以对于bin中的文件,Eclipse将会把它作为jar中的文件对待。如果src中存在非Java文件(此处是file.txt),Eclipse就会按该文件在src中的目录结构将它直接拷贝到bin目录中。FileInJar.java的完整内容如下:
package test.in;
import java.io.InputStream;

public class FileInJar {

    
public static void main(String[] args) throws Exception {
        FileInJar path 
= new FileInJar();

        String path
= "files/file.txt";
        InputStream in = path.getClass().getResourceAsStream(path);

        
int c;
        
while ((c = in3.read()) != -1) {
            System.out.print((
char) c);
        }
    }
}
该程序就是将file.txt中的内容读出,然后显示到标准输出流(控制台)中。
请大家一定要注意path变量的值,它关系到Class.getResourceAsStream(String name)是否能够找到该文件,否则它返回的InputStream将为null。
现在将重点讨论文件路径的写法,在讨论之前必须要看看API文档中关于路径算法的内容:
* If the name begins with a '/'  ('\u002f'), then the absolute name of the resource is the portion of the name following the '/'.
* Otherwise, the absolute name is of the following form:

    modified_package_name/name
          
Where the modified_package_name is the package name of this object with '/' substituted for '.' ('\u002e').

Class.getResourceAsStream(String name)是通过name(即示例程序中的path)值来找资源的。对于name的值的格式,存在两种情况:[1]以'/'(它的Unicode值为'\u002f')开头,那么这个路径就是相对于jar文件的根目录,而与class文件(如示例中的FileInJar.class)在jar文件中的位置无关;[2]对于其它情况,这个路径将相对于class文件在jar文件中的位置,实际上也就是包名后再加给出的name值(
modified_package_name/name )。 对这两种路径格式的总结:[1]以'/'开头,路径就是指定文件在jar中的绝对路径;[2]不以'/'开头,路径就是指定文件在jar中针对class文件的相对路径
针对上述描述,原程序中的路径还有另一种以'/'开头的写法: /test/in/files/file.txt。这种格式与原path的格式完全一样:FileInJar.class在包test.in('.'将被转换为'/')中,而给出的name(即path)值为files/file.txt,根据文档中的算法 modified_package_name/name 示例程序中getResourceAsStream方法实际上仍然是根据路径/test/in/files/file.txt来查找资源的(废话! ^_^)。
关于使用使用ClassLoader.getResourceAsStream(String name)
细心的朋友可以发现,在ClassLoader中也有一个getResourceAsStream方法,而且它的功能同样也是根据给定的name值来查找资源并返回一个InputStream对象。其实Class.getResourceAsStream(String)是
ClassLoader.getResourceAsStream(String)的代理方法,但它会对name值做一些处理再传递给ClassLoader。如果直接将name值传递给ClassLoader中的这个方法,可能会找不到资源(尽管你的路径没有写错)。因为Class会对name值作一些处理(其实就是按前面所讲的路径算法进行处理),但ClassLoader并不会怎么做。对于这一点,JDK文档中没有明确的描述。
注意:不建议直接使用ClassLoader中的相应方法
另外可以尝试一下java.util.jar,该包用于读/写jar内文件。

final变量的初始化
一般情况下,final变量都是在它的声明处就进行初始化,如下所示:
class Foo {
   
final int F = 10;
    void bar() {
        final int BAR = 1;
        System.out.println(BAR);
}
注:与一般的类成员变量不同,此处的变量F并不会进行默认的初始化(如果是默认初始化,F的值应该为0)。
对类中的final成员变量,除上面的初始化方式,还可以在构造器中对final变量进行初始化,如下所示:
class Foo {
   
final int F;
    Foo() {
        F 
= 10;
    }
}
在使用上述方式时,如果有多个构造器,那么每个构造器都必须对final成员变量进行初始化,如下所示:
class Foo {
   
final int F;
    Foo() {
        F 
= 10;
    }
    Foo(
int f) {
        F 
= f;
    }
    Foo(String str) {
        F 
= 0;
        System.out.println(str);
    }
}

Bob Lee创新的一种Singleton实现方式

    public class Singleton {
        static class SingletonHolder {
            static Singleton instance = new Singleton();
        }
        public static Singleton getInstance() {
            return SingletonHolder.instance;
        }
    }


解决使用JSplitPane.setDividerLocation(double d)无效的问题
public class BaseSplitPane extends JSplitPane {

    
private boolean isPainted = false;

    
private boolean hasProportionalLocation = false;

    
private double proportionalLocation = 0.0D;

    
public BaseSplitPane() {
        
super();
    }

    
public BaseSplitPane(int newOrientation, boolean newContinuousLayout,
            Component newLeftComponent, Component newRightComponent) {
        
super(newOrientation, newContinuousLayout, newLeftComponent,
                newRightComponent);
    }

    
public BaseSplitPane(int newOrientation, boolean newContinuousLayout) {
        
super(newOrientation, newContinuousLayout);
    }

    
public BaseSplitPane(int newOrientation, Component newLeftComponent,
            Component newRightComponent) {
        
super(newOrientation, newLeftComponent, newRightComponent);
    }

    
public BaseSplitPane(int newOrientation) {
        
super(newOrientation);
    }

    
public void setDividerLocation(double proportionalLocation) {
        
if (!isPainted) {
            hasProportionalLocation 
= true;
            
this.proportionalLocation = proportionalLocation;
        } 
else
            
super.setDividerLocation(proportionalLocation);
    }

    
public void paint(Graphics g) {
        
if (!isPainted) {
            
if (hasProportionalLocation)
                
super.setDividerLocation(proportionalLocation);
            isPainted 
= true;
        }
        
super.paint(g);
    }
}

FlowLayout与ScrollPane不能正常协作的问题
在一个只允许上下滚动(HORIZONTAL_SCROLLBAR_NEVER)的ScrollPane中有一个Container,它使用FlowLayout,那么在默认情况下,当该container中各组件宽度之和已超出了ScrollPane的宽度时,并不会自动换行。这算是JDK的一个Bug,但在Sun官方论坛中给出了一种解决方案
public class ScrollableFlowPanel extends JPanel implements Scrollable {

    
private static final long serialVersionUID = -7723152015485080501L;

    
public ScrollableFlowPanel(int alignment) {
        
super(new FlowLayout(alignment));
    }

    
public ScrollableFlowPanel() {
        
this(FlowLayout.CENTER);
    }

    
public void setBounds(int x, int y, int width, int height) {
        
super.setBounds(x, y, getWidth(), height);
    }

    
public Dimension getPreferredSize() {
        
return new Dimension(getWidth(), getPreferredHeight());
    }

    
public Dimension getPreferredScrollableViewportSize() {
        
return super.getPreferredSize();
    }

    
public int getScrollableUnitIncrement(Rectangle visibleRect,
            
int orientation, int direction) {
        
int hundredth = (orientation == SwingConstants.VERTICAL ? getParent()
                .getHeight() : getParent().getWidth()) 
/ 100;
        
return (hundredth == 0 ? 1 : hundredth);
    }

    
public int getScrollableBlockIncrement(Rectangle visibleRect,
            
int orientation, int direction) {
        
return orientation == SwingConstants.VERTICAL ? getParent().getHeight()
                : getParent().getWidth();
    }

    
public boolean getScrollableTracksViewportWidth() {
        
return true;
    }

    
public boolean getScrollableTracksViewportHeight() {
        
return false;
    }

    
private int getPreferredHeight() {
        
int rv = 0;
        
for (int k = 0, count = getComponentCount(); k < count; k++) {
            Component comp 
= getComponent(k);
            Rectangle r 
= comp.getBounds();
            
int height = r.y + r.height;
            
if (height > rv)
                rv 
= height;
        }
        rv 
+= ((FlowLayout) getLayout()).getVgap();
        
return rv;
    }
}

updating...
posted on 2006-08-12 09:04 John Jiang 阅读(796) 评论(3)  编辑  收藏 所属分类: JavaSEJava

评论

# re: Java Weed 2007-06-14 13:50 天天
从jar中读文件
终于找你了!太感谢了!!!
希望能以后继续向牛人学习!!
联系方式:QQ:59477844  回复  更多评论
  

# re: Java Weed 2007-06-14 13:51 天天
说的很详细啊!!
再顶~  回复  更多评论
  

# re: Java Weed 2007-06-14 15:46 Sha Jiang
谢谢 :-D  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: