面试准备系列——Java基础技术篇(4)/基本类型与运算

1.(重点)Java提供了哪些基本数据类型

Java语言一共提供了8种原始的数据类型(byte, char,short, int, long, float, double,boolean),这些数据类型不是对象,而是Java语言中不同于类的特殊类型,这些基本类型的数据变量在声明之后就会立刻在栈上被分配内存空间除了这8种基本的数据类型外,其他类型都是引用类型(例如类、接口、数组等),引用类型类似于C ++中的引用或指针的概念,它以特殊的方式指向对象实体,这类变量在声明时不会被分配内存空间,只是存储了一个内存地址而已。

下表为Java中的基本数据类型及其描述:

在这里插入图片描述
以上这些基本类型可以分为如下4种类型:

    1. int 长度数据类型: byte (8 bit)、short (16 bit)、int (32 bit)、long (64 bit)。
    1. float 长度数据类型:单精度(32 bit float)、双精度( 64 bit double)。
    1. boolean 类型变量的取值: ture、 false。
    1. char 数据类型: unicode 字符(16 bit)。

此外,Java语言还提供了对这些原始数据类型的封装类(字符类型Character,布尔类型Boolean,数值类型Byte、Short、 Integer、 Long、 Float、 Double)。 需要注意的是,Java 中的数值类型都是有符号的,不存在无符号的数,它们的取值范围也是固定的,不会随着硬件环境或者操作系统的改变而改变。除了以上提到的8种基本数据类型以外,在Java语言中,还存在另外一-种基本类型void,它也有对应的封装类java. lang. void,只是无法直接对它进行操作而已。封装类型和原始类型有许多不同点:首先,原始数据类型在传递参数时都是按值传递,而封装类型是按引用传递的。其次,当封装类型和原始类型用作某个类的实例数据时,它们所指定的默认值不同。对象引用实例变量的默认值为null,而原始类型实例变量的默认值与它们的类型有关(例如int默认初始化为0),示例如下:
在这里插入图片描述
在这里插入图片描述
除了以上需要注意的内容外,在Java语言中,默认声明的小数是double类型的,因此在对float类型的变量进行初始化时需要进行类型转换。float 类型的变量有两种初始化方法: floatf=1. Of或float f=( float)1.0。与此类似的是,在Java语言中,直接写的整型数字是int 类型的,如果在给数据类型为long的变量直接赋值时,int类型的值无法表示一个非常大的数字,因此,在赋值时可以通过如下的方法来赋值: long l =26012402244L。

引申:

  • 1.在Java语言中null值是什么?在内存中null是什么?
    null不是一个合法的Object 实例,所以编译器并没有为其分配内存,它仅仅用于表明该引用目前没有指向任何对象。其实,与C语言类似,null是将引用变量的值全部置0。

  • 2.如何理解赋值语句String x = null?
    在Java语言中,变量被分为两大类型:原始值( primitive)-与引用值( reference)。声明为原始类型的变量,其存储的是实际的值。声明为引用类型的变量,存储的是实际对象的地址(指针,引用)。对于赋值语句Stringx=null, ,它定义了一个变量“x”, x中存放的是String引用,此处为null。

常见题型:
在这里插入图片描述

2.什么是不可变类

不可变类( immutable class)是指当创建了这个类的实例后,就不允许修改它的值了,也就是说,一个对象一旦被创建出来,在其整个生命周期中,它的成员变量就不能被修改了。它有点类似于常量(const), 即只允许别的程序读,不允许别的程序进行修改。

在Java类库中,所有基本类型的包装类都是不可变类,例如Integer,Float 等。此外,String也是不可变类。可能有人会有疑问,既然String是不可变类,为什么还可以写出如下代码来修改String类型的值呢?
在这里插入图片描述
表面上看,好像是修改String类型对象s的值。其实不是,Strings= “Hello” 语句声明了一个可以指向String类型对象的引用,这个引用的名字为s,它指向了一个字符串常量“Hello”。s+=" world"并没有改变s所指向的对象(由于“Hello”是String类型的对象,而String又是不可变量),这句代码运行后,s指向了另外一个String 类型的对象,该对象的内容为“Hello world"。原来的那个字符串常量“Hello” 还存在于内存中,并没有被改变。

这里一定要注意不可变类和引用不可变的区别:
在这里插入图片描述
在介绍完不可变类的基本概念后,下面主要介绍如何创建一个不可变类。 通常来讲,要创建一个不可变类需要遵循下面4条基本原则:

1)类中所有成员变量被private所修饰。

2)类中没有写或者修改成员变量的方法,例如setxxx,只提供构造函数,一次生成,永不改变。

3)确保类中所有方法不会被子类覆盖,可以通过把类定义为final或者把类中的方法定义为final来达到这个目的。

4)如果一个类成员不是不可变量,那么在成员初始化或者使用get方法获取该成员变量时,需要通过clone方法来确保类的不可变性。

5)如果有必要,可使用覆盖Object类的equals( )方法和hashCode( )方法。在equals( )方法中,根据对象的属性值来比较两个对象是否相等,并且保证用equals( )方法判断为相等的两个对象的hashCode()方法的返回值也相等,这可以保证这些对象能被正确地放到HashMap或HashSet集合中。

除此之外,还有一些小的注意事项:由于类的不可变性,在创建对象时就需要初始化所有成员变量,因此最好提供-个带参数的构造函数来初始化这些成员变量。

下面通过给出一个错误的实现方法与一个正确的实现方法来说明在实现这种类时需要特别注意的问题。首先给出一个错误的实现方法:
在这里插入图片描述
需要说明的是,由于Date对象的状态是可以被改变的,而ImmutableClass保存了:Date类型对象的引用,当被引用的对象的状态改变时会导致ImmutableClass对象状态的改变。

其实,正确的实现方法如下所示:
在这里插入图片描述
Java语言中之所以设计有很多不可变类,主要是不可变类具有使用简单、线程安全、节省内存等优点,但凡事有利就有弊,不可变类自然也有其缺点,例如,不可变类的对象会因为值的不同而产生新的对象,从而导致无法预料的问题,所以,切不可滥用这种模式。

3.(重点)值传递和引用传递的区别

方法调用是编程语言中非常重要的一个特性,在方法调用时,通常需要传递一些参数来完成特定的功能。Java 语言提供了两种参数传递的方式:值传递和引用传递。

  • (1)值传递
    在方法调用中,实参会把它的值传递给形参,形参只是用实参的值初始化一-个临时的存储单元,因此形参与实参虽然有着相同的值,但是却有着不同的存储单元,因此对形参的改变不会影响实参的值。

  • (2)引用传递
    在方法调用中,传递的是对象(也可以看作是对象的地址),这时形参与实参的对象指向同一块存储单元,因此对形参的修改就会影响实参的值。

在Java语言中,原始数据类型在传递参数时都是按值传递,而包装类型(非原始数据类型)在传递参数时是按引用传递的。

下面通过一个例子来介绍按值传递和按引用传递的区别:

在这里插入图片描述
按引用传递其实与传递指针类似,是把对象的地址作为参数的,如图所示:
在这里插入图片描述
为了便于理解,假设1和“Hello”存储的地址分别为0X12345678和0XFFFFF12。在调用方法testPassParameter时,由于i为基本类型,因此参数是按值传递的,此时会创建一个i的副本,该副本与i有相同的值,把这个副本作为参数赋值给n,作为传递的参数。而StringBuffer由于是一个类,因此按引用传递,传递的是它的引用( 传递的是存储“Hello的地址”),如上图所示,testPassParameter内部修改的是n的值,这个值与i是没关系的。但是在修改ss1时,修改的是ss1这个地址指向的字符串,由于形参ss1与实参s1指向的是同一块存储空间,因此修改ss1后,s1指向的字符串也被修改了。

Java中处理8种基本的数据类型用的是值传递,其他所有类型都用的是引用传递,由于这8种基本数据类型的包装类型都是不可变量,因此增加了对“按引用传递”的理解难度。下面给出一个示例来说明:

在这里插入图片描述
对于上述程序的前两个输出“1”和“2”,不少读者可能会认为,Integer 是按值传递的而不是按引用传递的,其实这是一个理解上的误区,上述代码还是按引用传递的,只是由于Integer是不可变类,因此没有提供改变它值的方法,在上例中,在执行完语句b++后,由于Integer是不可变类,因此此时会创建-一个新值为2的Integer赋值给b,此时b与a其实已经没有任何关系了。

下面通过程序的后两个输出来加深对“按引用传递”的理解。为了理解后两个输出结果,首先必须理解“引用也是按值传递的”这一要点。为了便于理解,假设s1和s2指向字符串的地址分别为0X12345678和0XFFFF12,那么在调用函数changeStringBuffer时,传
递s1与s2的引用就可以理解为传递了两个地址0X12345678和0XFFFFF12,而且这两个地址是按值传递的( 即传递了两个值,ss1 为0X12345678,ss2 为0XFFFFFF12),在调用方法ss1. append(" World" )时,会修改ss1所指向的字符串的值,因此会修改调用者的sl的值,得到的输出结果为“Hello World"。但是在执行ss2=ss1时,只会修改ss2的值而对s2毫无影响,因此s2的值在调用前后保持不变。为了便于理解,下图给出了函数调用的处理过程。

在这里插入图片描述
从上图可以看出:
在传递参数时相当于传递了两个地址,在调用ss1. append(" World") 时,修改了这个地址所指向字符串的值,而在调用ss2=ss1时,相当于修改了函数changeStringBuffer内部的局部变量ss2,这个修改与ss1没关系。
常见题型:
在这里插入图片描述

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

在Java语言中,当参与运算的两个变量的数据类型不同时,就需要进行隐式的数据类型转换,转换的规则为:从低精度向高精度转换,即优先级满足byte < short < char< int <long<float<double,例如,不同数据类型的值在进行运算时,short类型数据能够自动转为int类型,int类型数据能够自动转换为float类型等。反之,则需要通过强制类型转换来实现。

在Java语言中,类型转换可以分为以下几种类型:
(1)类型自动转换
低级数据类型可以自动转换为高级数据类型,下表给出了常见的自动类型转换的规则:
在这里插入图片描述
当类型自动转换时,需要注意以下几点:

    1. char 类型的数据转换为高级类型(如int, long 等),会转换为其对应的ASCII码。
    1. byte、 char、 short 类型的数据在参与运算时会自动转换为int型,但当使用“十=”运算时,就不会产生类型的转换
    1. 另外,在Java语言中,基本数据类型与boolean类型是不能相互转换的。

总之,当有多种类型的数据混合运算时,系统会先自动地将所有数据转换成容量最大的那一种数据类型,然后再进行计算。

(2)强制类型转换
当需要从高级数据类型转换为低级数据类型时,就需要进行强制类型转换,下表给出了强制类型转换的规则。
在这里插入图片描述
需要注意的是,在进行强制类型转换时可能会损失精度。
常见题型:
在这里插入图片描述
在这里插入图片描述

5.强制类型转换需要注意什么

Java语言在涉及byte、short和char类型的运算时,首先会把这些类型的变量值强制转换为int类型,然后对int类型的值进行计算,最后得到的值也是int类型。因此,如果把两个short类型的值相加,最后得到的结果是int类型;如果把两个byte类型的值相加,最后也会得到一个int类型的值。如果需要得到short类型的结果,就必须显式地把运算结果转换为short类型,例如对于语句shortsl =1; s1=s1 +1,由于在运行时会首先将s1转换成int类型,因此s1 +1的结果为int类型,这样编译器会报错,所以,正确的写法应该short sl =1;s1 = ( short)(s1 +1)。有一种例外情况。“+=”为Java语言规定的运算法,Java编译器会对其进行特殊处理,因此,语句shortsl=1;sl+=1能够编译通过。

6.运算符的优先级是什么

Java语言中有很多运算符,由于运算符优先级的问题经常会导致程序出现意想不到的结果,下表详细介绍了运算符的优先级。
在这里插入图片描述
在实际使用时,如果不确定运算符的优先级,最好运用括号运算符来控制运算顺序。

常见题型:
在这里插入图片描述

7.Math类中的round、ceil、floor方法的功能介绍

round、ceil和floor方法位于Math类中,Math是-一个包含了很多数学常量与计算方法的类,位于java. lang包下,能自动导人,而且Math类里边的方法全是静态方法。下面重点介绍这3个方法代表的含义。

    1. round方法表示四舍五入。round, 意为“环绕”,其实现原理是在原来数字的基础上先增加0.5然后再向下取整,等同于( int ) Math. floor(x +0.5f)。它的返回值类型为int型,例如,Math. round(11. 5)的结果为12,Math. round( -11. 5)的结果为-11。
    1. ceil 方法的功能是向上取整。ceil,意为“天花板”,顾名思义是对操作数取顶,Math. ceil(a),就是取大于a的最小的整数值。需要注意的是,它的返回值类型并不是int型,而是double型。若a是正数,则把小数“人”,若a是负数,则把小数“舍”。
    1. floor 方法的功能是向下取整。floor, 意为“地板”,顾名思义是对操作数取底。Math. floor(a),就是取小于a的最大的整数值。它的返回值类型与ceil 方法一样,也是double型。若a是正数,则把小数“舍”;若a是负数,则把小数“入”。

下边来个实例对比:
在这里插入图片描述
测试代码:
在这里插入图片描述

8.++i与i++区别

在编程时,经常会用到变量的自增或自减操作,尤其在循环中用得最多。以自增为例,有两种自增方式:前置与后置,即++i和i++,它们的不同点在于i ++是在程序执行完毕后进行自增,而++i是在程序开始执行前进行自增,示例如下:
在这里插入图片描述
上例中的程序运行结果让很多读者感到困惑不解,其实稍作分析,问题便迎刃而解了。表达式i+++i++首先执行第- -个i++ 操作,由于自增操作会稍后执行,因此,运算时i的值还是1,但自增操作后,i的值变为了2,接着执行第二个i++,运算时,i 的值已经为2了,而执行了一个自增操作后,i 的值变为了3,所以i+++i++=1 +2=3,而运算完成后,i 的值变为3。

表达式i+++++i首先执行第一个i++,但是自增操作会稍后执行。因此,此时i的值还是3,接着执行++i,此时i的值变为4,同时还要补执行i ++的自增操作,因此此时i的值变为5,所以i +++++i=3 +5=8。

同理,i +++i+++i++=5 +6+7=18。

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

Java提供了两种右移运算符:“>>”和“>>>”。其中,“>>”被称为有符号右移运算符,“>>>”被称为无符号右移运算符,它们的功能是将参与运算的对象对应的二进制数右移指定的位数。二者的不同点在于“>>”在执行右移操作时,若参与运算的数字为正数,则在高位补0;若为负数,则在高位补1。而“>>>”则不同,无论参与运算的数字为正数或为负数,在执行运算时,都会在高位补0

此外,需要特别注意的是,在对char、byte、 short 等类型的数进行移位操作前,编译器都会自动地将数值转化为int类型,然后才进行移位操作。由于int型变量只占4Byte (32 bit),因此当右移的位数超过32 bit时,移位运算没有任何意义。所以,在Java语言中,为了保证移动位数的有效性,以使右移的位数不超过32 bit,采用了取余的操作,即使a>>n等价于a>>(n%32),示例如下:

在这里插入图片描述
引申:“<<”运算符与“>>”运算符有什么异同?

“<<”运算符表示左移,左移n位表示原来的值乘2的n次方,经常用来代替乘法操作,例如,一个数m乘以16可以表示为将这个数左移4位(m<<4), 由于CPU直接支持位运算,因此位运算比乘法运算的效率高。

与右移运算不同的是,左移运算没有有符号与无符号左移,在左移时,移除高位的同时在低位补0。以4<<3 (4为int型)为例,其运算步骤如下所示。
1)把4转换为二进制数字0000 0000 0000 0000 0000 0000 0000 0100。
2)把该数字的高三位移走,同时其他位向左移动3为。
3)在最低位补3个零。最终结果为0000 0000 0000 0000 0000 0000 0010 0000,对应的十进制数为32。

与右移运算符相同的是,当进行左移运算时,如果移动的位数超过了该类型的最大位数,那么编译器会对移动的位数取模,例如对int型移动33位,实际上只移动了33%32 =1位。

10.char型变量中是否可以存储一个中文汉字

在Java语言中,默认使用的Unicode编码方式,即每个字符占用两个字节,因此可以用来存储中文。虽然String是由char所组成的,但是它采用了一种更加灵活的方式来存储,即英文占用一个字符,中文占用两个字符,采用这种存储方式的一一个重要作用就是可以减少所需的存储空间,提高存储效率。
在这里插入图片描述
在上例中:“Hello" 是英文字符,因此所占字节数和字符串长度相同。“你好”字符串长度为2,由于每个字符都占用两个字节,因此总的字节数为4。此方法可以用来判断一个字符串中是否包含中文字符,示例如下:
在这里插入图片描述
在上例中,首先通过字节长度和字符串长度判断字符串是否包含中文字符,若包含,则用正则表达式匹配的方式找出字符串中的所有中文字符。

常见题型:
在这里插入图片描述

11.为什么c中的char的字节数和java中的不一样的

直接回答个编码问题就差不多了。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值