问:char型变量中能不能存储一个中文汉字?
char型变量是用来存储Unicode编码的字符的,Unicode编码字符集中包含了汉字,所以,char型变量中当然可以存储汉字。
但是如果有某个特殊的汉字没有被包含在unicode编码字符集中,那么char型变量中就不能存储这个特殊的汉字。
Unicode编码占用两个字节,所以char型变量也是占用两个字节。
问:用最有效率的方法算出2乘以8?
2<<3
因为将一个数左移n位,就相当于乘以2的n次方。又因为CPU直接支持位运算,所以使用移位计算2乘以8是最有效的方法。
问:使用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变?
使用final关键字修饰一个变量时,是指引用变量不能变,引用变量所指的对象中的内容还是可以改变的。例如对于如下语句:
final StringBuffer a = new StringBuffer(“immutable”);
执行如下语句的时候会报告编译错误:
a=new StringBuffer(“”);
但是执行如下语句则可以通过编译:
a.append(“broken!”);
final关键字
可能用到final关键字的3中情况:数据、方法和类。
1.final数据
程序中可能有些数据是永远都不会变的,这些数据有可能是编译时常量,也有可能是程序在运行时被初始化的值。对于编译期常量这种情况,编译器可以将改常量值带入任何可能用到它的计算式中,也就是说可以在编译时执行计算式,这减轻了一些程序运行时的负担。
在Java中编译时常量必须是基本数据类型,并且以关键字final表示,在对这个常量进行定义的时候,必须对其赋值。
一个既是static有是final的域只占据一段不能改变的存储空间。
当对对象引用而不是基本数据类型运用final时,其含义会有些令人迷惑。对于基本类型,final使数值恒定不变;而用于对象引用。final使引用恒定不变。一旦引用被初始化指向指向一个对象,就无法把它改变指向另一个对象。然而,对象自身其自身是可以被修改的。Java并没有提供使任何对象恒定不变的途径(但是可以自己编写类,以实现其对象恒定不变的效果)。这一限制同样适用于数组也就是对象。
注意,在编程时带有恒定初始值的final static基本类型全用大写字母命名,并且字与字之间用下划线隔开。
我们不能因为某数据是final的就认为在编译时就可以知道它的数值。
空白final
Java允许生成“空白final”所谓“空白final”是指声明为final但是未给定初值的域。无论什么情况,编译器都确保空白final在使用前必须被初始化。但是,空白final在关键字final的使用上提供了更大的灵活性,为此,一个类中的final域就可以做到根据对象而有所不用,却有保持了其恒定不变的特性。
class Poppet{
private int i;
Poppet(int i){this.i=i;}
}
public class TestNew{
private final int i=0;
private final int j;//空白final
private final Poppet p;//空白final引用
public TestNew(){
j=1;
p=new Poppet(1);
}
public TestNew(int x){
j=x;
p=new Poppet(x);
}
public static void main(String[] args){
new TestNew();
new TestNew(47);
}
}
必须在域的定义处或者每个构造器中用表达式对final进行赋值,这正是final域在使用前总是被初始化的原因所在。
final参数
java允许在参数列表中以声明的方式将参数指明为final。这意味着你无法在方法中改变参数引用所指向的对象(注意,并不是说不能改变指向的对象本身!)
class Gizmo{
public void spin(){}
}
public class TestNew{
void with(final Gizmo g){
//! g=new Gizmo();
}
void without(Gizmo g){
g=new Gizmo();
g.spin();
}
void f(final int x){
//!x++;//cant change
}
public static void main(String[]args){
TestNew t=new TestNew();
t.with(null);
t.without(null);
}
}
2.final方法
使用final的原因有两个。第一个原因是想把方法锁定,以防任何继承类修改它的含义。这是出于设计的考虑,想要确保在继承中使方法行为保持不变,并且不会被覆盖。第二个原因是基于效率,在Java的早期实现中,如果将一个方法声明为final,就是同意编译器将针对该方法的所有调用都转为内嵌调用,这样会消除方法调用的开销。但是,如果一个方法很大,那么程序代码就会膨胀,因为可能看不到内嵌带来的任何性能提高,因为,所带来的性能提高会因为花费于方法内的时间量而被缩减。
final和private关键字
类中所有的private方法都隐式的指定为是final的。由于无法取用private方法,所以也就无法覆盖它。可以对private方法添加final修饰词,但这并不能给该方法添加任何额外的意义。
这一问题会造成混淆。因为如果你试图覆盖一个private方法(隐含是final的),似乎是奏效的,而且编译器是不会给出错误信息的:
class WithFinals{
private final void f(){System.out.println("WithFinal.f()");}
private void g(){System.out.print("WithFinal.g()");}
}
class OverridingPrivate extends WithFinals{
private final void f(){
System.out.println("OverridingPrivate.f()");
}
private void g(){
System.out.println("OverridingPrivate.g()");
}
}
class OverridingPrivate2 extends OverridingPrivate{
public final void f(){
System.out.println("OverridingPrivate2.f()");
}
public void g(){
System.out.println("OverridingPrivate2.g()");
}
}
public class TestNew{
public static void main(String[]args){
OverridingPrivate2 op2=new OverridingPrivate2();
op2.f();
op2.g();
//you can upcast
OverridingPrivate op=op2;
//But you cant call the methons
//! op.f();
//! op.g();
WithFinals wf=op2;
//! wf.f();
//! wf.g();
}
}
“覆盖”只有在某方法是基类的接口的一部分的时候才会出现。也就是,必须能将一个对象向上转型为它的基本类型并调用相同的方法。
如果某方法为private。它就不是某类的接口的一部分。它仅是一些隐藏在类中的程序代码,只不过是具有相同名称而已。
但如果在导出类中以相同的名称生成一个public、protected或包访问权限的话,该方法就不会产生在基类中出现的”仅具有相同名称”的情况。此时你并没有覆盖该方法,仅仅是生成了一个新的方法。由于private方法无法触及而且能有效隐藏,所以除了把它看成是因为它所归属的类的组织结构的原因而存在之外,其他任何事物都不需要考虑到它。
final类
当将某个类的整体定义为final时(通过将关键字final置于它的定义之前),就表明了你不打算继承该类,而且不允许别人这么做。换句话说,出于某种考虑,你对该类的设计用不需要做任何的变动,或者出于安全的考虑,你不需要子类。
class SmallBrain{}
final class Dinosaur{
int i=7;
int j=1;
SmallBrain x=new SmallBrain();
void f(){}
}
public class TestNew{
public static void main(String[]args){
Dinosaur n=new Dinosaur();
n.f();
n.i=48;
n.j++;
}
}
需要注意的是,final类的域可以根据个人的意愿选择为是或者不是final。不论类是否被定义为final,相同的规则都适用于定义为final的域,然而,由于final类禁止继承,所以final类中所有的方法都隐式指定为是final的,因为无法覆盖它们。在final类中可以给方法添加final修饰词,但是这不会增添任何意义。