Cream.icend

导航

java se的那些细节

局部变量:方法体内或语句块内,不能有修饰符

成员变量:与类的对象共存,可以有修饰符

类属性:与类共存,可以有修饰符

 

一、局部变量:必须先赋值,才能使用,如果不赋初值,则会报错,即没有默认的始使值,而基本数据类型的类属性或成员变量则不会,他们通过编译以后被赋与默认值。

程序1:

public class Test{
    public static void main(String[] args){
        float f;
        System.out.println(f);
    }
}

image

 

被赋与的值如下:

局部变量: 没有默认值,必须存在值才能使用
属性: javac编译完成后,指定默认值

整数 :0
浮点数:0.0
字符型: \u0000 ''
布尔型: false
引用类型: null

程序2:

public class Test5{
    static int a ;
   
    public static void main(String[] args){
        float f=0;
        System.out.println(f);
        System.out.println(a);
        new Test();
    }
}

class Test6{
    int a;
    public void Test6(){
        System.out.println(a);
    }
}

image

 

 

二、尽量不要使用小数比较,原因如下

程序:

public class Test{
    public static void main(String[] args){
        float f = 0.6f;
        double d = 0.6;
        System.out.println(f==d);
    }
}

image

double是双精度小数,float是单精度小数,所以得出来的二进制数肯定是不相同的,所以结果为两个相同的数字不相同,如果一定要比较,则按同一类型的小数进行比较。

float和double类型数据可以使用科学记数法表示,如

float f = 3.14e3f---3.14X10的3次方

double f1 = 3.14e-3---3.14X10的负3次方

 

 

 

三、编译期优化:

编译期优化,能够确定结果直接给定结果,不能确定运行确定结果

比如:int a = 3+4;

int b = a+3;

在编译期,a的值就会直接计算出来,而b的值则不会被计算出来,在编译期,只检查第二行的语法是否正确,在运行时才能得出结果。

 

四、特别容易出错的地方:

short a=3;

short b=3;

short c = (short)(a+b);

这里一定要有强制转换,如果没有强制转换,则会提示错误

image

第三行即使写成short c = (short)a+(short)b;也是不行的,也会报上面的错误

同理,char和byte也是一样要进行强制转换的。

 

 

五、八进制,十六进制

八进制:int a = 033;(在数字前面加个0-零,表示是八进制数)

十六进制:int a = 0x33;(在数字前面加0x-零x,x大小写都可)

 

 

六、转义字符(没有办法直接搞出来的)

char 类型用来表示在Unicode编码表中的字符。占两个字节

\\表示的是\

\”表示的是”

\’表示的是’

\n表示的是换行

\t表示的是tab

 

额外,

public class TestChar {

    /**
     * @param args
     */
    public static void main(String[] args) {
        char ch='中';
        String s = "中国";
//        System.out.println(ch+'\''+"...");
//        System.out.println(s+ch);
        System.out.println(ch+6+"");
        System.out.println(""+ch+6);
       
    }

}

image

原因:虽然有String类型的时候,输出结果一定为String类型,但是第一个输出的String类型在后面,程序执行前面的时候还是int类型,把结果计算出来以后再往后面执行的时候碰到””才内部强制转换为String类型,而第二个输出则是在开始的时候就碰到String,所以在这以后都是String类型的拼接。这在输出大范围数和小范围数中也同样适用,要把大范围数提前,这样可以避免多次类型转换,也可以防止当前面多个的小范围数据运算时数据溢出。

 

七、数学余数问题

public class TestYuShu {

    /**
     * @param args
     */
    public static void main(String[] args) {
        System.out.println(5%(-2));
        System.out.println((-5)%2);
        System.out.println((-5)%(-2));
        System.out.println(5%2);
    }

}

输出结果为:image

规律:余数的正负与被除数相同,虽然不知道为什么,但还是记下吧,以备以后会用到。

 

八:a++与++a的问题

程序:

public class TestA {

    /**
     * @param args
     */
    public static void main(String[] args) {
        int a = 3;
        a=a++;
        System.out.println(a);
       
        a=3;
        a=++a;
        System.out.println(a);
    }

}

结果:image

将在等号前面的a看成是另外一个int类型的数值b,然后再按照相同的思维把b再符赋值给a,就可以得出上面的结果了。

 

 

九:扩展运算符:+= -= *= /= %=

优点:简化了代码,对代码进行了优化:

程序:

public class TestExtend {

    /**
     * @param args
     */
    public static void main(String[] args) {
        byte c1=34;
        byte c2=56;
        c2+=c1;
        System.out.println(c2);
       
        c1=34;
        c2=56;
        c2=(byte)(c2+c1);
        System.out.println(c2);
    }

}

结果:

image

从上面的程序可以看出,使用扩展运算符的代码不能进行强制转换了,jdk内容会自动进行转换的,但是如果不用的话,则自己必须进行强制转换,否则编译通不过。

 

 

十:逻辑运算符:操作数只能为boolean类型

 

 

位运算符(按位运算):& | ~ ^ >> >>> <<<
& | --> 与&& || 区别:
1、普通与 或 所有的表达式都会参与运算
2、可以使用在 int |boolean 上

~ : 取反
^ : 异或 相同为0 ,不同为1
    任何整数 ^ 自己 =0

 

 

十一、代码的优化:

把当前的工作做好后,看看是否可以对其进行优化,比如减少代码的行数等,或者考虑性能方面的优化。

 

十二、对象什么时候存在

代码:

Cat cat = null;

Cat cat = new Cat();

对于上面两行代码,第一行并不产生一个对象,只有new了以后才会产生新的对象,因为第一行的代码在只在内存的栈中多了一个引用,这个引用里面的地址为空址,内存的堆中并没有产生对象,只有第二行的代码产生对象。

java内存分析:

栈:用来存放局部变量,引用类型的地址,和基本变量值

堆:存放new出来的对象的

方法区(特殊的堆):用来存储常量,字节码,字符串常量池---即声明的字符串实际存放的位置,声明的任意一个字符串都存在这里。

 

十三、

构造器是初使化对象信息的特殊方法,不是用来构造对象的,而是给对象里的属性等赋值用的。

例如:

Cat cat = new Cat(“tom”);

在这句话中先在内存的栈中压入cat,占四个字节,然后遇到了new,就在堆中创建了对象,对象里的属性都为默认值,创建好对象以后,碰到了Cat(“tom”),发现它是一个方法,则又在栈中开辟了空间,将这个方法的参数压入栈中,这个参数存的是在方法区(code area)中创建好的字符串tom的地址,然后进行方法里面的操作,这些操作通常是给对象赋值,比如,将tom赋值给新创建对象的属性name等,操作完了以后将其弹出栈,将cat中存入堆中创建对象的地址。

在一个类中,构造方法构成的重载中,如果一个构造器调用了另一个构造器,则调用语句this(…)必须放在这个构造器的第一行,这是语法必须。

 

十四、protected

java的访问权限:

private

default

protected

public

private只能在自己的类或对象里调用,default则只能在自己的包中调用,不同包的子类也不能调用,protected受保护的则是针对于不同包而言的,同包下可以使用,不同包下的子类也可以使用,public则是不同包下的类也可以访问

但是protected中,对子类可以访问的理解可能会有偏差,类中protected方法是指子类用父类的方法,而不是在子类中父类的对象调用父类。程序如下:

父类:

package com.jll.bjsxt.javase.sixth.homework;

public class Father {
   
    protected void print(){
        System.out.println("我是父类打印出来的");
    }

}

子类:

package com.jll.bjsxt.javase.sixth.classwork;

import com.jll.bjsxt.javase.sixth.homework.Father;

public class Child extends Father{   
    public static void main(String[] args){
        Child c = new Child();
        c.print();
    }

}

这样访问是可以的,但是如下访问是不可以的

父类是不变的,子类变化如下:

package com.jll.bjsxt.javase.sixth.classwork;

import com.jll.bjsxt.javase.sixth.homework.Father;

public class Child extends Father{

    void p(){
        this.print();
    }
    }

test类如下:

package com.jll.bjsxt.javase.sixth.classwork;

import com.jll.bjsxt.javase.sixth.homework.Father;

public class Test {

    /**
     * @param args
     */
    public static void main(String[] args) {
        Child tp = new Child();
        tp.print();
    }

}

tp.print()这句话是会出错的,

image
上面两个例子说明,protected方法子类访问时,只能在子类中访问,不能在不同包的非子类里声明一个子类的对象对它进行访问。

把上面的例子中的child类加一个方法,在这个方法里面调用父类的protected方法,这样,在不同包的非子类中就可以访问这个protected方法了。

 

 

 

十五、方法的重写:

如何找重写
1、程序上
1)、@Override
2)、父类编写方法调用,调用成功 就是重写,否则延续
2、手动上 (面试)    在合理范围 提供子类方法的可见性
1)、前提: 继承 方法签名相同 
2)、<=
   a)、方法的返回类型
        基本类型 |void   ==
        引用类型  <=
    b)、异常<=
    每一个方法隐式对外声明一个RuntimeException
    子类方法的异常 <=父类方法的异常
   c)、可见性 >=
    访问修饰符 >=父类 修饰符
四、不会发生重写
1、属性,定义了同名的属性而已,属性就近(从属类)原则
2、以下方法不会重写
1)、静态方法不会重写 ,父类为静态,子类只能为静态,否则编译错误
2)、final修饰方法    否则编译错误
3)、私有方法不会重写

 

 

 

十六、final关键字:

final加在变量前面时,表示这个变量一旦被赋值就不可被改变,如果是final  F f =new F()则表示f里面存的地址不可变,f里面的内容是可以改变的,即如果final修饰的是引用类型,则表示引用类型存的地址不可变,引用类型里面的内容可以改变。

如果在一个类中定义了一个成员变量为final,则它的值必须被赋值,赋值的方法有三种:(final int AGE)

1、直接赋值 final int AGE = 10;

2、在构造块中赋值

fina int AGE;

{

AGE = 10;

}

这是因为构造块优先于构造器执行,且每次创建对象的时候都会执行

3、在构造方法中赋值,但是要注意,如果在构造方法中赋值,则必须所有的构造方法中都对其进行赋值,否则编译不通过。原因是不管调用哪个构造器创建对象,这个对象的 final属性都必须有值。如果只对其中一个构造器进行了赋值,则创建对象调用的不是这个,那么就会编译出错

如果在一个类中定义了静态成员变量,也必须对其进行赋值,赋值的二两方法:

1、直接赋值

2、在静态块中赋值,这是因为静态块只在类第一次加载时执行

 

 

 

十七、instanceof

instanceof这个关键字不可乱用,必须用在继承链一条链上使用(不是继承体系),否则编译错误

 

 

 

十八、抽象类

抽象类不能实例化,可以有构造器,这是因为它必须得有子类,当子类调用子类的构造器时,先调用父类的构造器中的一个,任何类都有构造器

 

 

十九、java.lang.Cloneable

如果某个类的对象想要被克隆,必须实现java.lang.Cloneable 这个接口,如果不实现这个接口,会报 CloneNotSupportedException运行时异常,这个接口是空的,它的作用是做为克隆的一个标识,如果没有它,则不会被克隆,类似的接口还有一个java.io.Serializable

 

 

二十、this super

this与super不能同时存在一个方法中,也不能被用在static静态方法中,原因是this与super都要放在方法的第一行,如果同时存在满足不了它们的要求,而静态方法中是类的对象共享的,不存在对象,当然也不存在this和super.

 

 

二十一、

三者联系与区别
* String              不可改变的字符序列      产生大量的垃圾对象            线程安全                    equals 比较内容
* StringBuilder     可改变的字符序列         不会产生大量垃圾对象    线程不安全 效率高               没有重写equals 比较地址
* StringBuffer      可改变的字符序列         不会产生大量垃圾对象    线程安全 效率相对低下          没有重写equals 比较地址

StringBuilder和StringBuffer若要比较地址,则要先用toString方法,转换成String,然后再用equals进行比较。


 

 

 

 

 

posted on 2014-06-25 21:48  Cream.icend  阅读(246)  评论(0编辑  收藏  举报