1,变量初始化顺序
package com.qdu.sun;public class InitialOrderTest {// 静态变量public static String staticField = "静态变量";// 变量public String field = "变量";// 静态初始化块static { System.out.println(staticField); System.out.println("静态初始化块");}// 初始化块{System.out.println(field);System.out.println("初始化块");}// 构造器public InitialOrderTest() {System.out.println("构造器");}public static void main(String[] args) {new InitialOrderTest();}}
执行结果;
静态变量
静态初始化块
变量
初始化块
构造器
但静态变量和静态块,变量和初始化块之间的顺序要看它们在类定义中的次序
package com.qdu.sun;class Parent { // 静态变量 public static String p_StaticField = "父类--静态变量"; // 变量 public String p_Field = "父类--变量"; // 静态初始化块 static { System.out.println(p_StaticField); System.out.println("父类--静态初始化块"); } // 初始化块 { System.out.println(p_Field); System.out.println("父类--初始化块"); } // 构造器 public Parent() { System.out.println("父类--构造器"); } }public class SubClass extends Parent {// 静态变量public static String s_StaticField = "子类--静态变量";// 变量public String s_Field = "子类--变量";// 静态初始化块static {System.out.println(s_StaticField);System.out.println("子类--静态初始化块");}// 初始化块{System.out.println(s_Field);System.out.println("子类--初始化块");}// 构造器public SubClass() {System.out.println("子类--构造器");}// 程序入口public static void main(String[] args) {new SubClass();}}
执行结果:
父类--静态变量
父类--静态初始化块
子类--静态变量
子类--静态初始化块
父类--变量
父类--初始化块
父类--构造器
子类--变量
子类--初始化块
子类--构造器
2,String对象package com.qdu.sun;
public class StringStaticTest {// 常量Apublic static final String A;// 常量Bpublic static final String B;public static final String C = "AB";//常量Bpublic static final String D="CD";static {A = "ab";B = "cd";}public static void main(String[] args) {// 将两个常量用+连接对s进行初始化 String s = A + B;String s2 = C + D;String t = "abcd";String t2 = "ABCD";if (s == t) {System.out.println("s等于t,它们是同一个对象");} else {System.out.println("s不等于t,它们不是同一个对象");}if (s2 == t2) { System.out.println("s2等于t2,它们是同一个对象"); } else { System.out.println("s2不等于t2,它们不是同一个对象"); }}}
执行结果:
s不等于t,它们不是同一个对象(虽然A,B是final的,但不知何时赋初值,相当于变量)
s2等于t2,它们是同一个对象(因为C,D是final的,并有初值,编译时就确定了不会变的)
package com.qdu.sun;public class StringTest { public static void main(String[] args) { String a = "ab";// 创建了一个对象,并加入字符串池中 System.out.println("String a = \"ab\";"); String b = "cd";// 创建了一个对象,并加入字符串池中 System.out.println("String b = \"cd\";"); String c = "abcd";// 创建了一个对象,并加入字符串池中 String d = "ab" + "cd"; // 如果d和c指向了同一个对象,则说明d也被加入了字符串池 if (d == c) { System.out.println("\"ab\"+\"cd\" 创建的对象 \"加入了\" 字符串池中"); } // 如果d和c没有指向了同一个对象,则说明d没有被加入字符串池 else { System.out.println("\"ab\"+\"cd\" 创建的对象 \"没加入\" 字符串池中"); } String e = a + "cd"; // 如果e和c指向了同一个对象,则说明e也被加入了字符串池 if (e == c) { System.out.println(" a +\"cd\" 创建的对象 \"加入了\" 字符串池中"); } // 如果e和c没有指向了同一个对象,则说明e没有被加入字符串池 else { System.out.println(" a +\"cd\" 创建的对象 \"没加入\" 字符串池中"); } String f = "ab" + b; // 如果f和c指向了同一个对象,则说明f也被加入了字符串池 if (f == c) { System.out.println("\"ab\"+ b 创建的对象 \"加入了\" 字符串池中"); } // 如果f和c没有指向了同一个对象,则说明f没有被加入字符串池 else { System.out.println("\"ab\"+ b 创建的对象 \"没加入\" 字符串池中"); } String g = a + b; // 如果g和c指向了同一个对象,则说明g也被加入了字符串池 if (g == c) { System.out.println(" a + b 创建的对象 \"加入了\" 字符串池中"); } // 如果g和c没有指向了同一个对象,则说明g没有被加入字符串池 else { System.out.println(" a + b 创建的对象 \"没加入\" 字符串池中"); } }}
执行结果:
String a = "ab";
String b = "cd";
"ab"+"cd" 创建的对象 "加入了" 字符串池中
a +"cd" 创建的对象 "没加入" 字符串池中
"ab"+ b 创建的对象 "没加入" 字符串池中
a + b 创建的对象 "没加入" 字符串池中
3,变量覆盖
package variableOverride;class ParentClass { public int i = 10;}public class SubClass extends ParentClass { public int i = 30; public static void main(String[] args) { ParentClass parentClass = new SubClass(); SubClass subClass = new SubClass(); System.out.println(parentClass.i + subClass.i); // 40 }}
这个问题虽然简单,但是情况却比较复杂。因为我们不仅要考虑变量、静态变量和常量三种
情况,还要考虑private、friendly(即不加访问修饰符)、protected和public四种访问权限下对属性的不同影响。
package variableOverride;class ParentClass { private String privateField = "父类变量--private"; /* friendly */String friendlyField = "父类变量--friendly"; protected String protectedField = "父类变量--protected"; public String publicField = "父类变量--public"; // private的变量无法直接访问,因此我们给他增加了一个访问方法 public String getPrivateFieldValue() { return privateField; } }public class SubClass extends ParentClass { private String privateField = "子类变量--private"; /* friendly */String friendlyField = "子类变量--friendly"; protected String protectedField = "子类变量--protected"; public String publicField = "子类变量--public"; // private的变量无法直接访问,因此我们给他增加了一个访问方法 public String getPrivateFieldValue() { return privateField; } public static void main(String[] args) { // 为了便于查阅,我们统一按照private、friendly、protected、public的顺序 // 输出下列三种情况中变量的值 // ParentClass类型,ParentClass对象 ParentClass parentClass = new ParentClass(); System.out.println("ParentClass parentClass = new ParentClass();"); System.out.println(parentClass.getPrivateFieldValue()); System.out.println(parentClass.friendlyField); System.out.println(parentClass.protectedField); System.out.println(parentClass.publicField); System.out.println(); // ParentClass类型,SubClass对象 ParentClass subClass = new SubClass(); System.out.println("ParentClass subClass = new SubClass();"); System.out.println(subClass.getPrivateFieldValue()); System.out.println(subClass.friendlyField); System.out.println(subClass.protectedField); System.out.println(subClass.publicField); System.out.println(); // SubClass类型,SubClass对象 SubClass subClazz = new SubClass(); System.out.println("SubClass subClazz = new SubClass();"); System.out.println(subClazz.getPrivateFieldValue()); System.out.println(subClazz.friendlyField); System.out.println(subClazz.protectedField); System.out.println(subClazz.publicField); }}
执行结果:
ParentClass parentClass = new ParentClass();父类变量--private父类变量--friendly父类变量--protected父类变量--publicParentClass subClass = new SubClass();子类变量--private父类变量--friendly父类变量--protected父类变量--publicSubClass subClazz = new SubClass();子类变量--private子类变量--friendly子类变量--protected子类变量--public
private的变量与其它三种访问权限变量的不同,这是由于方法的重写(override)而引起的。
分析上面的输出结果就会发现,变量的值取决于我们定义的变量的类型,而不是创建的对象
的类型。
当变量类型是父类(ParentClass)时,不管我们创建的对象是
父类(ParentClass)的还是子类(SubClass)的,都不存在属性覆盖的问题
package variableOverride;class ParentClass { public static String staticField = "父类静态变量"; public final String finalField = "父类常量"; public static final String staticFinalField = "父类静态常量"; }public class SubClass extends ParentClass {public static String staticField = "子类静态变量";public final String finalField = "子类常量";public static final String staticFinalField = "子类静态常量";public static void main(String[] args) {SubClass subClass = new SubClass();System.out.println(SubClass.staticField);// 注意,这里的subClass变量,不是SubClass类System.out.println(subClass.finalField);System.out.println(SubClass.staticFinalField);}}
子类静态变量
子类常量
子类静态常量
虽然上面的结果中包含“子类静态变量”和“子类静态常量”,但这并不表示父类的“静态
变量”和“静态常量”可以被子类覆盖,因为它们都是属于类,而不属于对象。
总结:1. 由于private变量受访问权限的限制,它不能被覆盖。
2. 属性的值取父类还是子类并不取决于我们创建对象的类型,而是取决于我们定义的变
量的类型。
3. friendly、protected和public修饰符并不影响属性的覆盖。
4. 静态变量和静态常量属于类,不属于对象,因此它们不能被覆盖。
5. 常量可以被覆盖。
6. 对于基本类型和对象,它们适用同样的覆盖规律。
4,final,finally,finalize
1. 在定义的时候初始化。
2. final变量可以在初始化块中初始化,不可以在静态初始化块中初始化。
3. 静态final变量可以在静态初始化块中初始化,不可以在初始化块中初始化。
4. final变量还可以在类的构造器中初始化,但是静态final变量不可以。
当final用来定义一个方法时,会有什么效果呢?正如大家所知,它表示这个方法不可以被
子类重写,但是它这不影响它被子类继承。
package variableOverride;class ParentClass { public final void TestFinal() { System.out.println("父类--这是一个final方法"); } }public class SubClass extends ParentClass {/*** 子类无法重写(override)父类的final方法,否则编译时会报错,但是子类可以继承父类的final方法*/// public void TestFinal() {// System.out.println("子类--重写final方法");// }public static void main(String[] args) {SubClass sc = new SubClass();sc.TestFinal();//父类--这是一个final方法}}
具有private访问权限的方法也可以增加final修饰,但是由于子类
无法继承private方法,因此也无法重写它。编译器在处理private方法时,是按照final方法来对待的,这样可以提高该方法被调用时的效率。不过子类仍然可以定义同父类中的
private方法具有同样结构的方法,但是这并不会产生重写的效果,而且它们之间也不存在必
然联系。
final类不允许被继承,编译器在处理时把它的所有方法都当作
final的,因此final类比普通类拥有更高的效率。而由关键字abstract定义的抽象类含有必须由继承自它的子类重载实现的抽象方法,因此无法同时用final和abstract来修饰同一个类。同样的道理,final也不能用来修饰接口。 final的类的所有方法都不能被重写,但这并不表示final的类的属性(变量)值也是不可改变的,要想做到final类的属性值不可改变,必须给它增加final修饰.
public final class FinalTest {int i = 10;public static void main(String[] args) {FinalTest ft = new FinalTest();ft.i = 99;System.out.println(ft.i); //99}}
finally是处理异常时用的,不管是否发生异常,都会执行的语句,break,continue也不例外
package variableOverride;public final class FinallyTest { // 测试return语句 public ReturnClass testReturn() { try { return new ReturnClass(); } catch (Exception e) { e.printStackTrace(); } finally { System.out.println("执行了finally语句"); } return null; } // 测试continue语句 public void testContinue() { for (int i = 0; i < 3; i++) { try { System.out.println(i); if (i == 1) { continue; } } catch (Exception e) { e.printStackTrace(); } finally { System.out.println("执行了finally语句"); } } } // 测试break语句 public void testBreak() { for (int i = 0; i < 3; i++) { try { System.out.println(i); if (i == 1) { break; } } catch (Exception e) { e.printStackTrace(); } finally { System.out.println("执行了finally语句"); } } } public static void main(String[] args) { FinallyTest ft = new FinallyTest(); // 测试return语句 ft.testReturn(); System.out.println(); // 测试continue语句 ft.testContinue(); System.out.println(); // 测试break语句 ft.testBreak(); }}class ReturnClass { public ReturnClass() { System.out.println("执行了return语句"); }}
执行结果:
执行了return语句执行了finally语句0执行了finally语句1执行了finally语句2执行了finally语句0执行了finally语句1执行了finally语句
很明显,return、continue和break都没能阻止finally语句块的执行。从输出的结果来看,return语句似乎在 finally语句块之前执行了,事实真的如此吗?我们来想想看,return语句的作用是什么呢?是退出当前的方法,并将值或对象返回。如果 finally语句块是在return语句之后执行的,那么return语句被执行后就已经退出当前方法了,finally语句块又如何能被执行呢?因此,正确的执行顺序应该是这样的:编译器在编译return new ReturnClass();时,将它分成了两个步骤,new ReturnClass()和return,前一个创建对象的语句是在finally语句块之前被执行的,而后一个return语句是在finally语句块之后执行的,也就是说finally语句块是在程序退出方法之前被执行的。同样,finally语句块是在循环被跳过(continue)和中断(break)之前被执行的。
finalize()方法是Object类中定义的方法,是在GC清理它所从属的对象时被调用的,如果执行它的过程中抛出了无法捕
获的异常(uncaught exception),GC将终止对改对象的清理,并且该异常会被忽略;直到下一次GC开始清理这个对象时,它的finalize()会被再次调用。
package variableOverride;public final class FinallyTest {// 重写finalize()方法protected void finalize() throws Throwable {System.out.println("执行了finalize()方法");}public static void main(String[] args) {FinallyTest ft = new FinallyTest();ft = null;System.gc(); //等价于Runtime.getRuntime().gc();}}
执行结果:执行了finalize()方法
调用它们的作用只是建议垃圾收集器(GC)启动,清理无用的对象释放内存空间,但是GC
的启动并不是一定的,这由JAVA虚拟机来决定。直到 JAVA虚拟机停止运行,有些对象的
finalize()可能都没有被运行过,那么怎样保证所有对象的这个方法在JAVA虚拟机停止运行之前一定被调用呢?答案是我们可以调用System类的另一个方法:
public static void runFinalizersOnExit(boolean value) {//other code}
传入true为参数,即可保证finalize方法一定执行,但该方法是不安全的,不建议使用。
5,java中参数传递
package variableOverride;public class ParamTest { // 初始值为0 protected int num = 0; // 为方法参数重新赋值 public void change(int i) { i = 5; } // 为方法参数重新赋值 public void change(ParamTest t) { ParamTest tmp = new ParamTest(); tmp.num = 9; t = tmp; //这里的t也是局部变量,和实参指向同一个对象,但现在该引用指向了新创建的tmp } // 改变方法参数的值 public void add(int i) { i += 10; //这里的i为局部变量 } // 改变方法参数属性的值 public void add(ParamTest pt) { pt.num += 20; //pt和实参指向同一个对象,对pt指向对象值的改变就是对源对象值改变 } public static void main(String[] args) { ParamTest t = new ParamTest(); System.out.println("参数--基本类型"); System.out.println("原有的值:" + t.num); // 为基本类型参数重新赋值 t.change(t.num); System.out.println("赋值之后:" + t.num); // 为引用型参数重新赋值 t.change(t); System.out.println("运算之后:" + t.num); System.out.println(); t = new ParamTest(); System.out.println("参数--引用类型"); System.out.println("原有的值:" + t.num); // 改变基本类型参数的值 t.add(t.num); System.out.println("赋引用后:" + t.num); // 改变引用类型参数所指向对象的属性值 t.add(t); System.out.println("改属性后:" + t.num); }}
执行结果:
参数--基本类型
原有的值:0
赋值之后:0
运算之后:0
参数--引用类型
原有的值:0
赋引用后:0
改属性后:20
String的长度实际上就是它的属性--char型数组value的长度。数组是没有length()方法的,
大家知道,在JAVA中,数组也被作为对象来处理,它的方法都继承自Object类。数组有一
个属性length,这也是它唯一的属性,对于所有类型的数组都是这样。
6,java中的字符
一个中文汉字可以保存在一个char变量里呢?因为在JAVA中,一
个char是2个字节(byte),而一个中文汉字是一个字符,也是2个字节。而英文字母都是
一个字节的,因此它也能保存到一个byte里,一个中文汉字却不能
字符串反串最简单的方法是使用java api本身自带的函数
public class StringReverse {public static void main(String[] args) {// 原始字符串String s = "A quick brown fox jumps over the lazy dog.";System.out.println("原始的字符串:" + s);System.out.print("反转后字符串:");StringBuffer buff = new StringBuffer(s);// java.lang.StringBuffer类的reverse()方法可以将字符串反转System.out.println(buff.reverse().toString());}}