动手动脑3
1.
早期我们经常这样定义变量“int value=100;”,而前面的示例中这样定义变量“MyClass obj=new MyClass();”,这两种方式定义的变量是一样的吗?
不一样。
前者为“原始数据类型”,诸如int,float之类的变量;
后者为“引用类型”变量,“引用”一个对象的变量称为“引用类型”的变量,有时又简称为“对象变量”。
从变量与内存分配上讲,当声明一个对象类型的变量时,实际上并没有创建一个对象,此变量=null。定义一个原始类型的变量时,会马上给其分配内存。
从变量的初始化上讲,在定义原始变量时,Java要求必须显式初始化变量;而对象变量的初始化:对象变量如果不引用一个真实的对象,则必须声明为null。
而且引用对象后的对象变量,如果不再使用此变量,JVM会回收MyClass对象所占用的内存(垃圾回收)。
2.
.对于原始数据类型的变量(比如int),可以直接使用“==”判断两变量值是否相等,那么对象变量也可以使用“==”判断两变量值是否相等吗?
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Foo obj1 = new Foo();
Foo obj2 = new Foo();
System.out.println(obj1==obj2);
}
}
class Foo{
int value=100;
}
运行结果:

结论:不可以,类似于字符串的比较大小,使用equals()进行比较,用法与其相同。
注意:
当“==”施加于“原始数据类型”变量时,是比较变量所保存的数据是否相等
当“==”施加于“引用类型”变量时,是比较这两个变量是否引用同一对象。
引用代表地址,所以“==”实际上相当于比较两个引用类型变量中保存的对象地址是否相同。
3.
验证相关程序:
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Foo obj1 = new Foo();
Foo obj2 = new Foo();
System.out.println(obj1==obj2);
}
}
class Foo{
int value=100;
}
运行结果:

4.
以下代码为何无法通过编译?哪儿出错了?
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Foo obj1 = new Foo();
}
}
class Foo{
int value;
public Foo(int initValue){
value=initValue;
}
}
结论:如果类提供了一个自定义的构造方法,将导致系统不再提供默认构造方法。所以可以将“Foo obj1 = new Foo();”改为“Foo obj1 = new Foo(4);”。
5.
解决问题
如果一个类中既有初始化块,又有构造方法,同时还设定了字段的初始值,谁说了算?
如:
在“public int field = 100;”在“{field=200;}”之前时,是“{field=200;}”说了算;在之后时,是“public int field = 100;”说了算。也就是谁比较靠后就是谁初始化起作用。
注:执行类成员定义时指定的默认值或类的初始化块,到底执行哪一个要看哪一个“排在前面”。执行类的构造函数。类的初始化块不接收任何的参数,而且只要一创建类的对象,它们就会被执行。因此,适合于封装那些“对象创建时必须执行的代码”。
具体程序如下:
public class InitializeBlockDemo {
/**
* @param args
*/
public static void main(String[] args) {
InitializeBlockClass obj=new InitializeBlockClass();
System.out.println(obj.field);
obj=new InitializeBlockClass(300);
System.out.println(obj.field);
}
}
class InitializeBlockClass{
//下面这句在初始化块之前与之后,会影响到field字段的初始值
public int field=100;//在之前
{
field=200;
}
//public int field=100;//在之后
public InitializeBlockClass(int value){
this.field=value;
}
public InitializeBlockClass(){
}
}
运行结果:
①“public int field = 100;”在“{field=200;}”之前:

②“public int field = 100;”在“{field=200;}”之后:

6.使用上页幻灯片中定义的类,以下代码输出结果是什么?


规律:所有类的变量都默认初始化null,数字类的原始数据类型默认初始化为0。
执行类的成员定义的默认值或类的初始化块,到底执行哪个要看哪一个排在前面。类的初始化块不接收任何的参数,而且只要一创建类的对象,它们就会被执行。因此,适合于封装那些“对象创建时必须执行的代码”。
7.请运行TestStaticInitializeBlock.java示例,观察输出结果,总结出“静态初始化块的执行顺序”。
实验测试:
class Root
{
static{
System.out.println("Root的静态初始化块");
}
{
System.out.println("Root的普通初始化块");
}
public Root()
{
System.out.println("Root的无参数的构造器");
}
}
class Mid extends Root
{
static{
System.out.println("Mid的静态初始化块");
}
{
System.out.println("Mid的普通初始化块");
}
public Mid()
{
System.out.println("Mid的无参数的构造器");
}
public Mid(String msg)
{
//通过this调用同一类中重载的构造器
this();
System.out.println("Mid的带参数构造器,其参数值:" + msg);
}
}
class Leaf extends Mid
{
static{
System.out.println("Leaf的静态初始化块");
}
{
System.out.println("Leaf的普通初始化块");
}
public Leaf()
{
//通过super调用父类中有一个字符串参数的构造器
super("Java初始化顺序演示");
System.out.println("执行Leaf的构造器");
}
}
public class TestStaticInitializeBlock
{
public static void main(String[] args)
{
new Leaf();
}
}
运行结果:

结论(静态初始化块的执行顺序):静态初始化块只执行一次。创建子类型的对象时,也会导致父类型的静态初始化块的执行。
8.
Integer类的装箱和拆箱是怎么样实现的?
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package ketangkehsi3;public class BoxAndUnbox { /** * @param args */ public static void main(String[] args) { int value=100; Integer obj=value; //装箱 int result=obj*2; //拆箱 }} |
所谓装箱,就是把基本类型用他们相对应的引用类型包装起来,使他们可以具有对象的特质,如我们可以把int型包装成Integer类的对象,或者把double包装成Double等等。
所谓拆箱,就是跟装箱的方向相反,将Integer及Double这样的引用类型的对象重新简化为值类型的数据。
9.


原因:在通过valueof方法创建Integer类型对象时,取值范围为[-128,127],数值在这个区间里,指针指向IntegerCache.cache中已经存在的对象引用,当数值超出这个范围,就会创建一个新的对象。而对对象使用“==”是判断两个对象是否是同一个,所以i1 == j1,i2 != j2。
10.
public class Zuoye {
public static int number;
public Zuoye()
{
number++;
System.out.println("你创建了"+number+"个对象");
}
public static void main(String[] args){
number=0;
Zuoye a=new Zuoye();
Zuoye b=new Zuoye();
Zuoye c=new Zuoye();
}
}

11.有趣的问题
java 静态与非静态的区别
这里的静态,指以static关键字修饰的,包括类,方法,块,字段。
非静态,指没有用static 修饰的。
静态有一些特点:
1.全局唯一,任何一次的修改都是全局性的影响
2.只加载一次,优先于非静态
3.使用方式上不依赖于实例对象。
4.生命周期属于类级别,从JVM 加载开始到JVM卸载结束。
可参考 :http://blog.csdn.net/zhandoushi1982/article/details/8453522/。
关于静态内部类(嵌套类)和非静态内部类的区别,可参考:
http://www.jb51.net/article/74838.htm
(1)内部静态类不需要有指向外部类的引用。但非静态内部类需要持有对外部类的引用。
(2)非静态内部类能够访问外部类的静态和非静态成员。静态类不能访问外部类的非静态成员。他只能访问外部类的静态成员。
(3)一个非静态内部类不能脱离外部类实体被创建,一个非静态内部类可以访问外部类的数据和方法,因为他就在外部类里面。
/* 下面程序演示如何在java中创建静态内部类和非静态内部类 */
class OuterClass{
private static String msg = "GeeksForGeeks";
// 静态内部类
public static class NestedStaticClass{
// 静态内部类只能访问外部类的静态成员
public void printMessage() {
// 试着将msg改成非静态的,这将导致编译错误
System.out.println("Message from nested static class: " + msg);
}
}
// 非静态内部类
public class InnerClass{
// 不管是静态方法还是非静态方法都可以在非静态内部类中访问
public void display(){
System.out.println("Message from non-static nested class: "+ msg);
}
}
}
class Main
{
// 怎么创建静态内部类和非静态内部类的实例
public static void main(String args[]){
// 创建静态内部类的实例
OuterClass.NestedStaticClass printer = new OuterClass.NestedStaticClass();
// 创建静态内部类的非静态方法
printer.printMessage();
// 为了创建非静态内部类,我们需要外部类的实例
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.new InnerClass();
// 调用非静态内部类的非静态方法
inner.display();
// 我们也可以结合以上步骤,一步创建的内部类实例
OuterClass.InnerClass innerObject = new OuterClass().new InnerClass();
// 同样我们现在可以调用内部类方法
innerObject.display();
}
}

浙公网安备 33010602011771号