java程序员面试笔试宝典4.4基本类型与运算

1.java提供了哪些基本数据类型?

java语言提供了8中原始的数据类型
byte,short,int,long,float,double,char,boolean
1,2,4,8,4,8,2,1
这些数据类型不是对象,被创建后立即在栈上创建空间
引用类型:类,接口,数组,这类对象在声明时不会被分配内存空间,只是存储了一个内存地址。
此外java语言还提供了原始类型的封装类,(Character,Boolean)
注意:
1)java中的数值类型都是有符号的,不存在无符号的数
2)取值范围固定,不会随硬件环境改变
3)第9中基本类型void,封装类java.lang.void;只是无法直接进行操作
封装类型与原始类型的不同点:
1)原始类型传递时按值传递,封装类型按引用传递
2)用作某个类的实例数据时,默认数据不同:封装类型为null,原始类型与类型有关。
public strictfp class Test{
    String s;
    int i;
    float f;

    public static void main(String[] args) {
        Test t = new Test();
        System.out.println(t.s==null);
        System.out.println(t.i);
        System.out.println(t.f);
    }
}

结论:
true
0
0.0
注意:
1)在java语言中,默认声明的小数是double类型的,故在对float类型的变量进行初始化时需要进行类型转换。
float类型初始化方式:
float f = 1.0f;
float f = (float)1.0;
2)相似的,直接写的整形数字是int类型的
long l = 26012402244L
引申:
1)java语言中,null是什么;在内存中是什么?
null不是一个合法的Object实例,编译器并没有分配内存,仅仅用来标识该引用目前没有指向任何对象。
2)如何理解String x = null;
定义了一个变量x,x中存放的是String引用,此处为null

2.什么是不可变类?

即创建了这个类的实例后就不允许修改它的值了
例如所有基本类型的包装类,此外String也是不可变类
public strictfp class Test{

    public static void main(String[] args) {
        String s = "Hello";
        s +=" World";
        System.out.println(s);
    }
}
结果:
Hello World

表面上看是改变了s的值,实际上原来的字符串"Hello"还存在内存中,并没有被改变,只是s指向了地址。
创建一个不可变类要遵循以下几个步骤:
1)类中所有成员变量被private修饰
2)类中没有写或者修改成员变量的方法;例如setXXX,只提供构造函数,一次生成,永不改变。
3)确保类中所有方法不会被子类覆盖,可以通过把类定义为final或把类中的方法定义为final
4)如果一个类成员不是不可变量,那么在成员初始化或者使用get方法获取该成员变量时,需要通过clone方法来确保类的不可变性。
5)如果有必要,可使用覆盖Object类的equals方法和hashCode方法
在equals方法中,根据对象的属性值来比较两个对象是否相等,并且保证equals方法判断为相等的两个对象的hashCode方法的返回值也相等。
这可以确保这些对象能被正确的放到HashMap或HashSet集合中
注意:
由于类的不可变性,在创建对象时就需要初始化所有成员变量,因此最好提供一个带参数的构造函数来初始化这些成员变量。
1:错误示范

import java.util.Date;

class ImmutableClass{
    private Date d;
    public ImmutableClass(Date d){
        this.d = d;
    }

    public void printState(){
        System.out.println(d);
    }
}

public class TestImmutable {
    public static void main(String[] args) {
        Date d = new Date();
        ImmutableClass im = new ImmutableClass(d);
        im.printState();
        d.setMonth(5);
        im.printState();
    }
}

结果:
Wed May 23 10:56:32 CST 2018
Sat Jun 23 10:56:32 CST 2018

Date对象的状态被改变,而ImmutableClass 保存了Date类型对象的引用,当被引用的对象的状态改变时会导致ImmutableClass 对象状态的改变。

例2:正确示范
import java.util.Date;

class ImmutableClass{
    private Date d;
    public ImmutableClass(Date d){
        this.d = (Date) d.clone();//解除了引用关系
    }

    public void printState(){
        System.out.println(d);
    }

    public Date getDate(){
        return (Date) d.clone();
    }
}

public class TestImmutable {
    public static void main(String[] args) {
        Date d = new Date();
        ImmutableClass im = new ImmutableClass(d);
        im.printState();
        d.setMonth(5);
        im.printState();
    }
}

结果:
Wed May 23 11:03:11 CST 2018
Wed May 23 11:03:11 CST 2018
使用不可变类的优缺点:
优点:
使用简单,线程安全,节省内存
缺点:
不可变类的对象会因为值得不同而产生新的对象,导致无法预料的问题。

3.值传递和引用传递的区别

1)值传递:形参与实参有相同的值,不同的存储单元
2)引用传递:形参与实参指向同一块存储单元。
java中,原始数据类型在传递参数时是值传递,而包装类型在传递参数时是引用传递。
1public class Test{
    public static void testPassParameter(StringBuffer ss1,int n){
        ss1.append(" World");//引用
        n = 8;//值
    }

    public static void main(String[] args) {
        int i = 1;
        StringBuffer s1 = new StringBuffer("Hello");
        testPassParameter(s1, i);
        System.out.println(s1);
        System.out.println(i);
    }
}
结果:
Hello World
12public class Test{

    public static void changeStringBuffer(StringBuffer ss1,StringBuffer ss2){
        ss1.append(" World");
        ss2 = ss1;
    }

    public static void main(String[] args) {
        Integer a = 1;
        Integer b = a;
        b++;
        System.out.println(a);
        System.out.println(b);

        StringBuffer s1 = new StringBuffer("Hello");
        StringBuffer s2 = new StringBuffer("Hello");
        changeStringBuffer(s1, s2);
        System.out.println(s1);
        System.out.println(s2);
    }
}

结果:
1
2
Hello World
Hello

说明:
Integer是不可变量,b++之后,此时会创建一个新值为2的Integer赋值给b
引用也是按值传递的。
调用方法ss1.append("World")时,会修改ss1所指向的字符串的值
调用ss2==ss1时候,只会修改了局部变量ss2的值,而对s2毫无影响,因此s2的值在调用前后保持不变。

call by value:按值传递
call by reference:按引用传递

4.不同数据类型的转换有哪些规则?

java语言中,当参与运算的两个变量的数据类型不同时,就需要进行隐式的数据类型转换,转换的规则为:从低精度向高精度转换。
即优先级满足:byte<short<char<int<long<folat<double
即short类型能自动转换为int,int类型能自动转换为float类型
反之,则需要通过强制类型转换来实现
类型自动转换需要注意:
1)char类型转换为高级类型,会转换为其对应的ASCII码
2)byte,short,char类型的数据在参与运算时会自动转换为int类型,但是当使用“+=”运算时,就不会产生类型的转换。
3)在java语言中,基本数据类型与boolean类型是不能相互转换的。
总之,当有多种数据类型混合运算时,系统会先自动的将所有的数据转换为容量最大的那一种数据类型,然后再进行计算。
强制类型转换:
当需要从高级数据类型转换为低级数据类型时,就需要进行强制类型转换。
需要注意的是:在进行强制类型转换的时候可能会损失精度。
1public static void main(String[] args) {
        int i = 1;
        if(i){//xxxx编译错误
            System.out.println("true");
        }
        else {
            System.out.println("false");
        }
    }

说明:if条件只能接受boolean类型的值,int类型不能被隐式地转换为boolean类型。


例2:
对于下述代码结果强制类型转换后,变量a和b的值分别为()
    public static void main(String[] args) {
        short a = 128;
        byte b = (byte) a;
        System.out.println(a);
        System.out.println(b);
    }
}
结果:
128
-128

说明:short类型变量占两个字节,a对应的二进制为0000000010000000,
由于byte只占一个字节,在强制转换为byte的时候只截取低字节:10000000,10000000是-128的补码,因此b的值为-128.

5.强制类型转换的注意事项有哪些?

1:错误写法
    public static void main(String[] args) {
        short s1 = 1;
        s1 = s1+1;//xxxx编译错误
    }

例2:正确写法
    public static void main(String[] args) {
        short s1 = 1;
        s1 = (short) (s1+1);
    }

而“+=”运算,java编译器会对其进行特殊处理,不会报错
例3public static void main(String[] args) {
        short s1 = 1;
        s1 +=1;
    }

6.运算符优先级是什么?(待处理1)
这里写图片描述

在实际使用中,如果不确定运算符的优先级,最好运用()运算符来控制运算顺序
1:下面程序的运行结果是什么?
    public static void main(String[] args) {
        byte a = 5;
        int b = 10;
        int c = a>>2+b>>2;
        System.out.println(c);
    }

结果:0
说明:+的优先级比>>高,上式相当于a>>(2+b)>>2,因此运算结果为0

7.Math类中的round, ceil ,和floor方法的功能各是什么?

1)round方法标识四舍五入
实现原理:在原来数字的基础上增加0.5,然后向下取整
(int)Math.floor(x+0.5)
2)ceil的功能是向上取整,就是取大于a的最小的整数值
注意:
返回值类型并不是int型,而是double型
若a为正数,则把小数入,若a为负数,则 把小数舍。
3)floor方法的功能是向下取整,对操作数取底,取小于a的最大的整数值
返回值类型也是double型,
若a为正数,把小数舍,若a为负数,把小数入。
1:
    public static void main(String[] args) {
        float m = 6.4f;
        float n = -6.4f;
        System.out.println("Math.round("+m+")="+Math.round(m));
        System.out.println("Math.round("+n+")="+Math.round(n));
        System.out.println("Math.ceil("+m+")="+Math.ceil(m));
        System.out.println("Math.ceil("+n+")="+Math.ceil(n));
        System.out.println("Math.floor("+m+")="+Math.floor(m));
        System.out.println("Math.floor("+n+")="+Math.floor(n));
    }

结果:
Math.round(6.4)=6
Math.round(-6.4)=-6
Math.ceil(6.4)=7.0
Math.ceil(-6.4)=-6.0
Math.floor(6.4)=6.0
Math.floor(-6.4)=-7.0

8.++i和i++有什么区别?

i++:在程序执行完毕后进行自增
++i:在程序开始执行前进行自增
1public static void main(String[] args) {
        int i = 1;
        System.out.println(i++ + i++);//1+2//3
        System.out.println("i="+i);//3
        System.out.println(i++ + ++i);//3+5//8
        System.out.println("i="+i);//5
        System.out.println(i++ + i++ + i++);//5+6+7
        System.out.println("i="+i);//8
    }

结果:
3
i=3
8
i=5
18
i=8

说明:如i++ + i++
首先执行第一个i++,自增操作会稍后执行,因此运算时i为1,但是自增操作后i的值变为2,执行第二个i++,即为1+2=3.

9.如何实现无 符号数的右移操作

>>有符号右移运算符
>>>无符号右移运算符
将参与运算的对象对应的二进制数右移指定位数
不同点:
>>:若参与运算的数为正数,则在高位补0,若为负,则在高位补1
>>>:无论正负,都会在高位补0
注意:
1)在对char,byte,short等类型进行移位操作时,编译器会自动将数值转化为int类型,然后进行操作。
2)int占4bytee(32bit),因此右移的位数超过32时,移位运算没有意义。
所以在java语言中,为了保证移位运算的有效性,使得向右移动的位数不超过32bit,采用了取余操作。
即a>>n  等价于  a>>(n%32)
1:

        public static void main(String[] args) {
        int i = -4;
        System.out.println("------------int>>:"+i);//4取反+1
        System.out.println("移位前二进制:"+Integer.toBinaryString(i));
        i>>=1;
        System.out.println("移位后二进制:"+Integer.toBinaryString(i));
        System.out.println("------------int>>:"+i);

        i=-4;
        System.out.println("------------int>>:"+i);
        System.out.println("移位前二进制:"+Integer.toBinaryString(i));
        i>>>=1;
        System.out.println("移位后二进制:"+Integer.toBinaryString(i));
        System.out.println("------------int>>:"+i);

        short j=-4;
        System.out.println("------------short>>:"+j);
        System.out.println("移位前二进制:"+Integer.toBinaryString(j));
        j>>>=1;
        System.out.println("移位后二进制:"+Integer.toBinaryString(j));
        System.out.println("------------short>>:"+j);


        i = 5;
        System.out.println("------------int>>:"+i);
        System.out.println("移位前二进制:"+Integer.toBinaryString(i));
        i>>=32;
        System.out.println("移位后二进制:"+Integer.toBinaryString(i));
        System.out.println("------------int>>:"+i);
    }

结果:
------------int>>:-4
移位前二进制:11111111111111111111111111111100
移位后二进制:11111111111111111111111111111110
------------int>>:-2
------------int>>:-4
移位前二进制:11111111111111111111111111111100
移位后二进制:1111111111111111111111111111110
------------int>>:2147483646
------------short>>:-4
移位前二进制:11111111111111111111111111111100
移位后二进制:11111111111111111111111111111110
------------short>>:-2
------------int>>:5
移位前二进制:101
移位后二进制:101
------------int>>:5



说明:
对于short来说,由于short占2byte,移位操作时先转换为int,
-4在无符号移位操作在高位补1,当把结果赋值为short类型时,只取其中低位的两个字节。
因此高位补0还是1对运算没有影响。

-4的二进制表示:1111111111111100,
转换为二进制时会以4Byte输出:高位补111111111111111111111111111111100
执行无符号右移1位:0111111111111111111111111111110
把运算结果赋值给j时只会取低的两个字节1111111111111110,对应的十进制为-2
引申:<<运算符和>>运算符有什么异同?
<<运算符表示左移,左移n位表示*2的n次方
例如一个数*16可以表示为(m<<4)
由于CPU直接支持位运算,因此位运算的效率比乘法高
区别:
与右移运算不同的是,左移运算没有有符号和无符号区别,在左移时,移除高位的同时在低位补0。
以4<<3为例
1)把4转换为二进制:00000000 00000000 00000000 00000100
2)把最高三位移走,同时其他向左移动三位:00000 00000000 00000000 00000100
3)在最低位补3个0:00000 00000000 00000000 00000100 000=32
相同点:
在进行左移时,如果移动的位数超过该类型的最大位数,编译器会对移动的位数取模,
例如对int型移动33位,实际只移动了33%32=1位。

10.char型变量中是否可以存储一个中文汉字?(待处理2)

java语言中,默认使用的是Unicode编码,即每个字符占用两个字节,可以存储中文
虽然String由char组成,但是采用了一种更灵活的方式来存储,即英文占一个字符,中文两个字符。
这种存储方式可以减少所需的存储空间,提高存储效率。
1public class Test{

    public static void getLen(String str){
        System.out.println(str+"的长度:"+str.length()+"所占用的字节数:"+str.getBytes().length);
    }

    public static void main(String[] args) {
        String s1 = "Hello";
        String s2 = "你好";
        getLen(s1);
        getLen(s2);
    }
}

结果:
Hello的长度:5所占用的字节数:5
你好的长度:2所占用的字节数:62:判断一个字符串中是否包含中文字符:
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class Test{

    public static void judgeChineseCharactor(String str){
        String regEx = "[\u4e00-\u9fa5]";
        //判断是否存在中文字符
        if(str.getBytes().length==str.length()){
            System.out.println("无汉字");
        }else {//如果存在汉字,找出字符串中的中文字符
            Pattern compile = Pattern.compile(regEx);
            Matcher matcher = compile.matcher(str);
            while(matcher.find()){
                System.out.print(matcher.group(0)+"");
            }
        }
    }

    public static void main(String[] args) {
        judgeChineseCharactor("Hello World");
        judgeChineseCharactor("Hello 你好");
    }
}

结果:
无汉字
你好

说明:首先通过字节长度和字符串长度判断字符串中是否包含中文字符,
若包含,则用正则表达式匹配的方式找出字符串中所有的中文字符

引申:
ResourceBundle:是一个资源处理类,可以经常在国际化应用中使用。

猜你喜欢

转载自blog.csdn.net/m0_37301141/article/details/80417876