`
足至迹留
  • 浏览: 485364 次
  • 性别: Icon_minigender_1
  • 来自: OnePiece
社区版块
存档分类
最新评论

我的烂笔头 -- java常识、常见问题

 
阅读更多
1.Integer.MAX_VALUE
  Integer max = Integer.MAX_VALUE;
		Integer begin = max - 1;
		int count = 0;

		for (; begin <= max; begin++)
		{
			count++;
		}
		
		System.out.print("count: " + count);


输出结果是什么:死循环。这里循环条件如果是begin<max,那结果就是1了,但有了<=,当begin达到了Integer.MAX_VALUE之后再继续加1就导致溢出,成了负数,继续加,开始无限循环。
扩展,int和Integer之间的比较:
①无论如何,Integer与new Integer不会相等。不会经历拆箱过程。
②两个都是非new出来的Integer,如果数在[-128,127]之间,则是true,否则为false
③两个都是new出来的,都为false
④int和Integer相同数值比较(无论new否)比,都为true,因为会把Integer自动拆箱为int再去比。
再看一个例子:
Integer a=1;
		Integer b=2;
		Integer c=3;
		Integer d=3;
		Integer e=321;
		Integer f=321;
		Long g=3L;
		System.out.println(c==d);
		System.out.println(e==f);
		System.out.println(c==(a+b));
		System.out.println(c.equals((a+b)));
		System.out.println(g==(a+b));
		System.out.println(g.equals((a+b)));

输出结果:
true
false
true
true
true
false
分析:
(1)包装类的“==”没有遇到运算符时不会自动解包。
(2)对最后一个false结果解释下:
g.equals(a+b)会转成g.equals(Integer.valueOf(3)), 然后equls方法会调用Long的equals方法:
public boolean equals(Object obj) {
	if (obj instanceof Long) {
	    return value == ((Long)obj).longValue();
	}
	return false;
    }


补充:我们知道int的范围是-2147483648~2147483647, 4个字节。Math类有个abs()取绝对值的方法,当调用Math.abs(Integer.MIN_VALUE);时,范围本来应该是|-2147483648|,但由于int的范围问题,所以绝对值还是Integer.MIN_VALUE,这点jdk有说明。如果想要获得正确的绝对值,则可以Math.abs(Long.valueOf(Integer.MIN_VALUE))。

2.list的循环中删除问题
public void testListDelete()
	{
		List list = new ArrayList<String>();
		list.add("1");
		list.add("2");
		list.add("3");
		list.add("4");
		
		for (int i = 0; i < list.size(); i++)
		{
			list.remove(i);
		}
		
		for (int j = 0; j < list.size(); j++)
		{
			System.out.print(list.get(j));
		}
	}


输出结果是:24
这个没有达到预期的结果是因为list.size()是动态变化的,list中元素的位置也跟原始的索引不一致,可一步一步跟踪结果。

如果把第一个循环改成:
for (Object element:list)
		{
			if("2".equals(element))
			{
				list.remove(element);
			}
		}

则会抛异常:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)

正确的做法只能是用迭代器来循环,用迭代器来删除:
Iterator<String> it = list.iterator();
		
		while(it.hasNext())
		{
			// 如果不先调用next()也不能进行删除操作。
			String element = it.next();
			
			// 使用迭代器循环,但这里如果还用list.remove(element)方法仍然会异常。
			it.remove();
			//list.remove(element);
		}


另外:ConcurrentHashMap迭代的弱一致性
在迭代方面,ConcurrentHashMap使用了一种不同的迭代方式。在这种迭代方式中,当iterator被创建后集合再发生改变就不再是抛出ConcurrentModificationException,取而代之的是在改变时new新的数据从而不影响原有的数据,iterator完成后再将头指针替换为新的数据,这样iterator线程可以使用原来老的数据,而写线程也可以并发的完成改变。
http://blog.csdn.net/madun/article/details/6326337
http://www.cnblogs.com/zhuawang/p/4779649.html
在遍历过程中,如果已经遍历的数组(ConcurrentHashMap内部是分段锁,每个分段有一个数组)上的内容变化了,迭代器不会抛出ConcurrentModificationException异常。如果未遍历的数组上的内容发生了变化,则有可能反映到迭代过程中。这就是ConcurrentHashMap迭代器弱一致的表现。
http://ifeve.com/concurrenthashmap-weakly-consistent/

3.类中的main函数既可以调用类的私有函数,也可以通过类对象调用私有成员变量(这一点很奇怪,其实所有的类方法里都可以通过本类的对象调用私有变量,跟用this一样)。
main函数中是无法使用this的,因为main是static的,没有this传进来,所以也无法直接调用非static变量。

如在Hotel类中的main函数中下面的调用是对的。
public static void main(String args[])
	{
                // 私有构造函数
		Hotel hotel = new Hotel("1",1,"2");

                // 私有成员变量
		System.out.println(hotel.hotelPrice);
	}


4.String的经典比较==、 equals。
如:
String t1 = "goodMorning";
String t2 = "good";
String t3 = "good" + "Morning";
String t4 = t2 + "Morning";
String t5 = new String("goodMorning");
String t6 = t5.intern();

t1 == t3 // true
t1 == t4 // false
t1 == t5 // false
t1 == t6 // true
还可以参考:
http://sarin.iteye.com/blog/603684/

5.运算符优先级
参考:
http://wenku.baidu.com/view/b311a14bcf84b9d528ea7a0c.html

优先级高->低:
后缀(. [] ++ --) > 前缀(++ -- ~ !) >  算术 >  移位 > 关系(< > ==) > 位操作(& |) > 逻辑(&& ||) > 三目操作符 > 赋值(= += *=...)

注:
1)只有无符号右移操作符>>>,没有无符号左移<<<
2)逻辑操作符表达式里并不是所有项都会计算,如 exp1 || exp2,如果exp1是true,那么exp2就不会计算了。会提前跳出。如同switch的case一样,一个匹配上如果不break,则一直执行下去,包括default。
3)java的boolean值不能与int转换,c/c++中非0表示true,0表示false,java中不是。


6.内部类,嵌套类与外部类的调用
可以在一个类的内部定义另一个类,这种类称为嵌套类(nested classes),它有两种类型:
静态嵌套类和非静态嵌套类。静态嵌套类使用很少,最重要的是非静态嵌套类,也即是被称作为内部类(inner)。
其中inner类又可分为三种:
    其一、在一个类(外部类)中直接定义的内部类;
    其二、在一个方法(外部类的方法)中定义的内部类;
    其三、匿名内部类

6.1 普通内部类(非静态嵌套类)不能定义static成员。

普通内部类没有被声明为外部类的static成员,因此必须在外部类的上下文中才有效,因此在创建外部类对象之前不能创建任何内部类对象。如果在外部要创建内部类对象,(1)必须使用外部类的名称作为限定名(2)如果是在外部类的非static方法里创建则可以不用外部类的限定名,因为自动用this来限定了。这也说明了不能在static方法里创建内部类对象。

6.2 为了让嵌套类对象独立于包围类对象,可以将嵌套类声明为静态的。不必再必须有外部类对象才能创建静态嵌套类对象,但必须使用由外部类类名限定的嵌套类类名作为要创建对象的类型。如: Outer.InnerStatic inn = new Outer.InnerStatic();静态嵌套类能拥有静态成员,而非静态嵌套类不能拥有。



1)外部类非static方法里(注意main可以当作一个普通的static方法):
构造内部类对象:new Outer().new Inner();或 new Outer.Inner();或new Inner();可以直接创建,是因为非static方法里有this,已经限定了内部类的范围。
访问内部类成员变量:int ss = new Outer().new Inner().inner_pri;
    在外部类可以通过Outer.Inner.this获得Inner的this,但获得this不能调用任何成员, 必须通过对象调用。对静态嵌套类也是这样。
    对静态嵌套类就可以直接访问Outer.InnerStatic.inner_mem;
访问内部类成员方法:new Test().new Inner().inner_pri();

访问静态嵌套类:
生成对象:new Outer.InnerStatic(); 或直接生成new InnerStatic(), 但不能new Outer().new InnerStatic(),即不能通过外部对象生成。
访问成员变量:静态成员,Outer.InnerStaitc.inner_staticMem;
              非静态成员,内部对象.inner_men;

2)普通内部类方法里(可以访问外部类所有成员。包括static变量,private变量):
构造外部类对象:Outer o = new Outer();
访问外部类成员:Outer.this.outer_mem;或 new Outer().outer_mem;或 直接访问变量(不与内部类重名的情况下)int i = outer_mem;
访问外部类方法:Outer.this.outer_fun();或 new Outer().outer_fun();或 直接访问方法(不与内部类重名的情况下)outer_fun();

3)静态嵌套类可以定义静态成员,只能直接访问外部类的静态成员变量和方法。但可以通过外部类对象访问非静态成员变量和方法。
Outer.out_static_mem; Outer.out_static_fun();
new Outer().out_mem;  new Outer().out_fun();

6.3 静态嵌套类
从技术上讲,静态嵌套类不属于内部类。因为内部类与外部类共享一种特殊关系,更确切地说是对实例的共享关系。而静态嵌套类则没有上述关系。它只是位置在另一个类的内部,因此也被称为顶级嵌套类。 静态的含义是该内部类可以像其他静态成员一样,没有外部类对象时,也能够访问它。

还可以参考:
http://www.xl7788.com/zt/computerprogram/JavaInClass.html
http://blog.csdn.net/acumengrass/article/details/537686

7.short s = 1;
  s = s + 1;// 编译错误,不能从int转型为short,要强转。
  s += 1; // 编译正确。

8.jdk1.6之前,switch (exp)里的exp表达式只能是能转成int的类型,也就是只支持int,short,char,byte,及四种对应的包装类,因为能自动解包还有枚举类型。jdk1.7之后支持String。long是不支持的。

9.abstract class的定义:
public abstract class abstractTest
{
    // 变量可以随便定义
    public static String aa;
    private String bb;

    // 方法只能定义abstract的或有实现体的非abstract方法。
    abstract void cc();
    void dd(){}

    // 下面的都不可以
    private abstract void ss(); // 方法就是用来被实现的,private怎么可以!同理,final也不可以!只能有一个public 或 protected 或默认修饰符。
    void dd();
    abstract static ee();
    abstract native ff();
}

interface里的变量和方法默认都是public static final的,不能定义其他限制符的成员变量或方法。如果要定义变量必须初始化,因为是final的,参见下面第11条。
interface 本身也只能由public或abstract修饰,public加不加都是public的。

10.
局部变量前不能放置任何访问修饰符 (private,public,和protected)。final可以用来修饰局部变量
如:
class dd
{
    public void sdf()
    {
        private int ss; // 不可以!
        final int sf;// 可以
    }
}

11.static,final成员变量初始化

class Something {
final int i;
public void doSomething() {
System.out.println("i = " + i);
}
}
这个例子是错的,final成员变量必须初始化。
在java中,普通变量系统是自动初始化的,数值变量自动初始化为0,其余类型变量自动初始化为空。
但是final类型的变量必须显示初始化,且初始化的方法必须是在声明时或非静态块或者在构造方法中初始化,而不能通过调用函数赋值。初始化之后不能再赋值。

static:
static变量不必须初始化,但如果初始化必须声明时初始化或声明时不初始化,可以在static块,非static块(通常不这样做)或构造函数里初始化。虽然在非static块里初始化是合法的,但一定不要那样做,因为非静态块只有在实例化的时候才会调用,静态变量通常会在实例化之前使用。
还可以参考:http://blog.csdn.net/darxin/article/details/5293427

static final同时修饰的变量必须初始化,而且必须在声明时初始化或static块里初始化(实质上两者是一样的,声明的同时初始化,编译器也是先声明,然后自动添加一个static块来完成初始化)。不能在非静态初始块或构造函数里初始化(两者其实是一样的,构造函数的代码会放到非静态初始块里合并)初始化之后不能再赋值。

关于构造方法重载的问题,如果程序中没有显示的说明构造方法,那么就有默认的构造方法。如果程序中显示的声明了构造方法,那么默认的构造方法就不再存在。
可以参考:
http://hi.baidu.com/wjx_5893/item/fecaef0c2ec72c7fbee97ee0

【注意】
如果final的是String, int等编译期就能确定的值,则无论怎么修改,对于任何引用的地方都没有任何影响,因为对于常量池里确定的常量,在编译期都内联进引用的class里了,已经跟引用的变量没有关系了,必须重新编译再启动; 如果final修饰的是object,那还是可以修改的,只需要重启。无论是基本类型还是对象,这里说的都是修改本身,不是里面的属性。


12.类的定义
此处,Something类的文件名叫OtherThing.java
class Something {
private static void main(String[] something_to_do) {
System.out.println("Do something ...");
}
}
这样定义是没有错的,只有public的类才要求一定要跟文件名一样。
所以一个java文件里只有有一个public类(也可以没有),类名要跟文件名一样,其余还可以定义好多类,只要不是public的就行。

13.继承的类和实现的接口有一个相同的成员变量,子类该怎么取值?
interface A{
int x = 0;
}
class B{
int x =1;
}
class C extends B implements A {
public void pX(){
System.out.println(x);
}
public static void main(String[] args) {
new C().pX();
}
}
答案:错误。在编译时会发生错误(错误描述不同的JVM有不同的信息,意思就是未明确的x调用,两个x都匹配(就象在同时import java.util和java.sql两个包时直接声明Date一样)。对于父类的变量,可以用super.x来明确,而接口的属性默认隐含为 public static final.所以可以通过A.x来明确。

如果是实现两个接口,都有相同的一个方法,也是可以的。如果两个接口都有相同的变量,也是可以的但使用时要指明是哪个接口的,否则编译报错,ambious.

补充:
interface 可以extends interface, 不能implements interface,也不能extends (abstract)class;
(abstract) class 可以implements interface, 不能extends interface;

14.在java中普通函数可以和构造函数同名,但是必须带有返回值;

15.& 按位‘与’运算,1 & 0 = 0, 1&1=1. 3 & 1 = 1, 4 & 1 = 0, 3 & 3 = 3.取的是二进制然后每一位进行与运算。 boolean类型的true & false = false, true & true = true,结果仍是boolean.

16.synchronized
1)对static变量或static方法或Test.class或Class.forNmae("Test")加synchronized方法效果都是一样的,都是对类对象加锁。注意,跟Test t = new Test(); t.getClass()加锁不一样!
2)当对对象obj加了锁,然后修改了obj则这个锁就无效了,而且obj.notify()时会异常,因为根本就不拥有obj的锁。如 Integer i = 2; synchronized(i){i--; i.notify()}
3)synchronized的对象不能是null。

17.静态导入不能导入类,只能是方法或属性。 import static com.....Test.test;或 import static com...Test.*;导入所有静态成员。

18.异常不是方法签名的一部分。也不会被继承。父类方法抛出异常,子类方法只能抛出比父类范围小的异常或不抛异常。

19.InputStream.read().  在从数据流里读取数据时,为图简单,经常用InputStream.read()方法。这个方法是从流里每次只读取读取一个字节,效率会非常低。更好的方法是用InputStream.read(byte[] b)或者InputStream.read(byte[] b,int off,int len)方法,一次读取多个字节。
还有更好的方法BufferedInputStream.read().前者的两个方法每次读都伴随着一次磁盘写,后者是有缓存的,缓存满或强制刷新才会写磁盘。

20.Array constants can only be used in initializers
如:
public class CopyOnWriteTest
{
    public Object[] org = {"a", "b"}; //正确
    public final Object[] snapshot = org;
    
    public static void main(String[] args)
    {
    	CopyOnWriteTest cow = new CopyOnWriteTest();
    	System.out.println(cow.snapshot.length);
    	cow.org = new Object[1];
    	//cow.org = {"c"}; 这一句是错的,这种赋值只能发生在初始化的时候。
    	System.out.println(cow.snapshot.length);
    }
}


21.for-each与iterator区别

for-each is syntactic sugar for using iterators (approach 2).

You might need to use iterators if you need to modify collection in your loop. First approach will throw ConcurrentModificationException.

for (String i : list) {
    System.out.println(i);
    list.remove(i); // throws exception
}

Iterator it=list.iterator();
while (it.hasNext()){
    System.out.println(it.next());
    it.remove(); // valid here
}

22. Math.ceil(double),Math.floor(double)
向上取整:Math.ceil(0.99) 返回 1.0;注意参数是double型,
如果Math.ceil(99/100) 则返回0,并不是没有向上取整,而是99/100返回的是0.需要Math.ceil(99/100.0)才会返回1.

向下取整:Math.floor(0.99) = 0。注意点跟ceil一样。

23.ArrayList: public <T> T[] toArray(T[] a)
能返回指定类型参数的数组,如果数组大小超过list的大小,则多余的赋值null。如果数组大小小于list的大小,则取list的a.length个元素赋值。

如果使用public Object[] toArray()则会出问题,这个方法只能返回Object类型的数组,不能返回指定类型。如果转换后强制类型转换,则编译通过,但运行时会异常ClassCastException。
例:
调用String[] newText = (String[])list.toArray();  会异常:
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;

24,有序map,LinkedHashMap
LinkedHashMap是链表形式存储key-value,可以按照存入顺序存储或访问顺序,而且还可以提供限定容量大小的功能,可以用来实现简易LRU缓存,还可以参考:http://blog.csdn.net/wo6925371/article/details/7921394

TreeMap虽然也是有序的,但是默认是按照降序顺序存储,本人用的很少。

map的遍历,通常有四种方法:
public static void main(String[] args) {


  Map<String, String> map = new HashMap<String, String>();
  map.put("1", "value1");
  map.put("2", "value2");
  map.put("3", "value3");
 
  //第一种:普遍使用,二次取值
  System.out.println("通过Map.keySet遍历key和value:");
  for (String key : map.keySet()) {
   System.out.println("key= "+ key + " and value= " + map.get(key));
  }
 
  //第二种
  System.out.println("通过Map.entrySet使用iterator遍历key和value:");
  Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
  while (it.hasNext()) {
   Map.Entry<String, String> entry = it.next();
   System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
  }
 
  //第三种:推荐,尤其是容量大时
  System.out.println("通过Map.entrySet遍历key和value");
  for (Map.Entry<String, String> entry : map.entrySet()) {
   System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
  }

  //第四种
  System.out.println("通过Map.values()遍历所有的value,但不能遍历key");
  for (String v : map.values()) {
   System.out.println("value= " + v);
  }
}

参考:http://www.cnblogs.com/kristain/articles/2033566.html


25.bytes[] 与 String
    String str = "abc";
  byte[] byte1 = str.getBytes();
  String str1 = new String(byte1);

  byte[] byte2 = str1.getBytes();
  String str2 = new String(byte2);

  System.out.println("str<<<" + str);
  System.out.println("byte1<<<" + byte1);
  System.out.println("str1<<<" + str1);
  System.out.println("byte2<<<" + byte2);
  System.out.println("str2<<<" + str2); 


输出:
    str<<<abc
  byte1<<<[B@192d342
  str1<<<abc
  byte2<<<[B@6b97fd
  str2<<<abc
分析:
其实这是byte[] 的toString方法不能直接还原成String,而是要通过String的构造方法。
同时还可以使用BASE64Encoder和BASE64Decoder来完成互相转换。

26.clone()方法需要显式调用才行,默认对象赋值是不使用clone方法的。

27.comparable接口和comparator接口
public interface Comparable<T> {
    public int compareTo(T o);
}

一般是类实现Comparable接口,使对象本身具有自比较功能。

public interface Comparator<T> {
    int compare(T o1, T o2);
    boolean equals(Object obj);
}

而Comparator是一个专用的比较器,当这个对象不支持自比较或者自比较函数不能满足你的要求时,你可以写一个比较器来完成两个对象之间大小的比较。

可以参看jdk里Collections.sort()和Arrays.sort()方法的实现,默认比较的对象是实现Comparable接口的,也可以指定Comparator来比较。

还可以参考:
http://pengcqu.iteye.com/blog/490149
http://www.cnblogs.com/nktblog/articles/2517095.html

28.未经初始化的局部变量不能被引用,编译错误。The local variable ** may not have been initialized
public static void main(String[] args)
    {
    	int i;
    	System.out.println("i=" + i);
    }


29.运行一个java程序其实就是启动一个jvm进程。

30.new File(filepath);
如果文件路径中有空格则file.exists()会返回false.

31.线程池中如果有线程异常,则会创建新线程替代。
public class Test
{
    static int i = 0;
    public static void main(String[] args)
    {
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        for (i = 0; i < 10; i++)
        {
            executorService.execute(new Runnable()
            {
                @Override
                public void run()
                {
                    System.out.println("__________:" + Thread.currentThread().getName());
                   throw new RuntimeException("aa");
                }
            });
        }
    }
}

结果显示,如果抛异常,则会创建10个线程,如果不抛异常,则只会用3个线程来调度10次调用。

32. java的方法参数本质都是值传递
1)基本类型参数在方法内被修改不会改变方法外基本类型的值。
2)引用类型参数在方法内可以修改引用对象里的字段,但不能修改引用到另外的对象,否则外部引用地址也不会改变。

二、ecliplse相关
1.eclipse中使用alt+/进行代码自动补全。比如输入main然后快捷键即可。

2.给jar包关联源码:
在项目上点右键-->build path-->configure Build path-->libraries-->展开要关联的jar包-->source attachment 指定源码路径就ok了,可以选择jar形式的源码也可以是解压的。

三、...
1.service层应该尽量避免同级依赖,一个service调用多个dao才是常态。

四、JVM
1.在catalina.bat里设置jvm内存
set JAVA_OPTS=-server -Xms800m -Xmx800m -XX:PermSize=256m -XX:MaxPermSize=256m
参考http://blog.chinaunix.net/uid-26602509-id-4110244.html
设置的位置没那么绝对,能启动jvm前加载即可。

最好把Xms和Xmx, PermSize和MaxPermSize设置成一样大小,尤其后者,每次扩容都会导致FullGC。

五、名词解释
1. 一方库、二方库、三方库说明:
一方库:本工程中的各模块之前的相互依赖
二方库:公司内部的依赖库,一般指公司内部的其他项目发布的jar包
三方库:公司之外的开源库, 比如apache、ibm、google等发布的依赖
  • 大小: 56.9 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics