八大基本类型
Java 是强类型语言,每个变量必须声明一种数据类型。
JavaScript是弱类型语言,变量定义可以不声明数据类型。
Java共有八种基本类型(primitive type):包含四种整型、两种浮点型、一种字符型、以及一种布尔型
关于 bit byte kb mb gb tb
bit 代表一位,比如二进制数据 1101 就是 4 位
byte 代表一个字节, 1 byte = 8 bit , 二进制数 1101 0011 就是一个字节
kb 1kb = 1024 byte
mb 1mb = 1024 kb
…
整型
类型字节数位数最小值最大值默认值byte1byte8bit-2^72^7-10short2byte16bit-2^152^15-10int4byte32bit-2^312^31-10long8byte64bit-2^632^63-10
long 需要在数字后加上 L/l
8bit 的最大值和最小值为什么是 2^7 – 1 和 -2^7 ?
一个八位的二进制数,可以表示2 ^ 8 个数字,其中正负各占一半。
整型各个进制表示法
八进制
:0 **;(*表示0到7任意数字)
十六进制
:0x** _;(表示0到f任意数字)
Java 7 新特性:
二进制
:0b_** _;
(_ 表示0或1)
Java7 新特性 :
int a = 100_345_001;
可以使用下划线分割数字,编译时自动忽略下划线,等同于100345001 。 作用,便于阅读
浮点型
类型字节数位数默认值float4byte32bit0.0double8byte64bit0.0
float需要在数字后加上 F/f, 如果不加代表double双精度浮点类型
浮点类型不适用于禁止出现舍入误差的金融计算中 (2.0-1.1)= 0.8999999999
浮点类型的运算遵循 IEEE754 标准,即IEEE二进制浮点数算术标准。
在Java1.2时,提出了关键字 strictfp ,用来修饰方法和类型, 被该关键字修饰后,会完全遵循 IEEE754标准进行浮点运算,早期java的浮点运算是一种近乎我行我素的方式。
char类型
char类型用于表示单个字符,通常用于表示字符常量,两个字节,16bit, 他的默认值为 0 ,即字符 空格。
可以表示:
Unicode编码单元:十六进制值,范围为\u0000
到 \uffff
转义字符:\b
, , , , \"
, \'
, \\
代码点和代码单元
背景 :
Unicode是为了统一世界上所有符号所设置的编码方式,当时认为 unicode使用2个字节表示足以应对所有的文字和符号,但是,随着时间的发展,16位的unicode编码无法表示所有的字符了。
概念 :
代码点 code point ,是指一个编码表中某个字符对应的代码值。在Unicode编码表中,一个代码点使用十六进制数表示,并且加上前缀 U+
, 比如 U+0041
是字母 A
的代码点, U+2A000
是增补字符 𪀀
的代码点。
Unicode 代码点,可以分为17个代码级别(code plane)。第一个代码级别称为 基本的多语言级别(basic multilingual plane),代码点从 U+0000
到 U+FFFF
,主要包含了经典的Unicode代码点。其余的16个附加级别, 代码点从 U+10000
到 U+10FFFF
,包含了一些增补字符
代码单元 code unit ,utf-16编码使用两个字节表示一个基本语言级别的字符,及一个代码单元,比如 \u20041
。 当遇到增补字符时,16位无法进行表示,则使用两个代码单元表示,比如汉字 𪀀
的字符编码为 \ud868\udc00
。
在 Java 中char类型用UTF-16编码描述一个代码单元。
注意: 增补字符无法用一个char来表示,比如: char a = '\ud868\udc00';
就会报错
boolean类型
默认值 false
,占用字节大小要根据虚拟机确定。
整型和布尔类型之间不能相互转换。
有关数据类型的面试题
short和int的转换问题
复制 byte b = 128; // 打印b?:A编译报错 B运行报错 C:可以输出
// 选A
short s = 1;
s = s + 1; // 打印s?:A编译报错 B运行报错 Cs=2
// 选A,类型不匹配
// s + 1 , 因为1是int类型,会自动提升表达式类型为int,而接收的是short,所以编译报错
s += 1; // 正确?
// 正确,正常执行,+=是语法规定的特殊运算符,有对其特殊处理,所以正常执行
char类型是否可以存储一个汉字?
char 存储一个Unicode代码单元,汉字则是由一个Unicode代码点组成,一个代码点可能由多个代码单元组成。
所以char可以存储一个汉字,但是生僻字就不行了。
double 和 float
复制 double a = 100f; // 正确,小范围类型数据可以赋值给大范围类型,即向上转型,直接自动转换
float b = 100; // 编译报错,100 是一个double类型,大不可给小,会有精度损失,即向下转型,需要强制类型转换帮助
double 和 long
复制 double a = 100L; // 正确
long b = 100.0; // 编译报错,double比long更精确
long b = 100.0f; // 同样不可以
浮点运损失
复制 3 * 0.1 == 0.3 // false 浮点运算都有损失
包装类型与基本类型比较
复制 Double d1 = 100.0;
Double d2 = 100.0;
d1 == d2; // false d1 d2 都是包装类型,都是被自动装箱创建出来的
Integer i1 = 128;
Integer i2 = 128;
i1 == i2 // false,同上
Integer i3 = 127;
Integer i4 = 127;
i3 == i4 // true,Integer内部有IntegerCache,是一个享元池,范围在-128~127之间的值,自动装箱时直接从池中获取,提高了java性能
// Short/Byte/Long同样具有范围也是(-128~127)
// 浮点数没有
new Long(100L) == 100L; // true , 基本类型和包装类型比较时,先将包装类型拆箱后再比较
new Long(100).equals(100L); // true
new Long(100).equals(100); // false
// 先比较类型,再比较值
Java中的i++和++i
参考:
i = i++
复制 int i = 0;
i = i++; // 0
解释 :
Java 使用缓存变量的机制, i=i++
相当于:
复制 int i = 0;
temp = i;
i++; // i = i + 1;
i = temp;
i++ 和 ++i 多个混合运算
复制
int i=5;
int s=(i++)+(++i)+(i--)+(--i)=24;
解析:
1. i++ => i=6; s'=5;
2. ++i => i=7; s'=7;
3. i-- => i=6; s'=7;
4. --i => i=5; s'=5;
==>s=5+7+7+5=24;
金山面试
答案 :
复制 i = 2;
j = 7;
k = 7;
h =3;
p1 = 1;
p2 = 1;
q1 = 1;
q2 = 0;
for、foreach、iterator遍历时修改、删除、增加的问题
Java中,一般存在三种遍历方式:
for循环
删除 :遍历中删除某个元素,集合/数组的长度就会变短,这样在遍历时就有可能造成数组下标越界异常(注意,for循环的list.size每次遍历都会获取,所以如果删除一个中间元素,遍历并不会出现下标越界;下表越界只会发生在同一循环内,比如已经删除了最后一个元素,却又获取最后一个元素的情况)
复制 // 代码示例如下
// 不会抛出异常
for (int i = 0; i < list.size(); i++) {
if (i == 0) {
list.remove(0);
}
System.out.println(list.get(i));
}
// 会抛出数组下标越界
for (int i = 0; i < list.size(); i++) {
if (i == 2) {
list.remove(0);
}
System.out.println(list.get(i));
}
添加 :遍历中一直添加元素,有可能造成无穷循环的情况
复制 // 代码示例如下
for (int i = 0; i < list.size(); i++) {
if (i == list.size() - 1) {
list.add("a");
}
}
foreach 与 iterator
foreach 实际上只是 iterator的一个语法糖,如果由如下foreach代码:
复制 for (String item : items) {
System.out.print(item);
}
实际上编译后的代码为:
复制 Iterator<String> iterator = items.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next());
}
修改 :使用iter在遍历时修改,与for循环一样,不会发生任何问题
删除 :使用iter在遍历时删除元素,如果使用的是 iterator.remove()
方法是可以正常删除的:
复制 // 情况一: 使用集合提供的remove方法:
for (String s : list) { // 抛出ConcurrentModificationException 抛出并发修改异常,无法删除
if (s.equals("0")) {
list.remove(0);
}
System.out.println(s);
}
// 情况二:删除倒数第二个元素,不会报错,因为下次hasNext返回false直接退出循环
Iterator<string> iterator = list.iterator();
while (iterator.hasNext()) {
String value = iterator.next();
if ("1".equals(value)) {
list.remove(list.size() - 2); // 删除元素1
}
System.out.println(value); // 0, 1
}
System.out.println(list); // [0, 2]
// 情况三:使用iterator提供的remove方法:
Iterator<string> iterator = list.iterator();
while (iterator.hasNext()) {
String value = iterator.next();
if ("0".equals(value)) {
iterator.remove(); // 不会抛出异常,会正常删除
}
System.out.println(value); // 还是会打印出被删除的元素 0,1,2
}
System.out.println(list); // 输出的元素确实是删除过后的 [1,2]</string></string>
增加 :增加无论任何情况下,都会抛出 ConcurrentModificationException
异常
Iterator的源码
参考类: ArrayList$Itr
复制 // 源码如下:
private class Itr implements Iterator<E> {
int cursor; // 下次调用next()获取的元素的指针
int lastRet = -1; // 最后一次操作的索引,如果上次操作为remove,则会置为-1 是一个记录变量
int expectedModCount = modCount; // modCount代表当前列表被修改的次数,使用expectedModCount记录当前状态的修改次数
public boolean hasNext() { // 如果当前指针大小小于list的size大小,说明还有元素没有遍历完
return cursor != size;
}
// 获取下一个元素
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
// 判断是否已经遍历到最后一个元素
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1; // 获取cursor位置元素成功,将指针位置后移一位(cursor++)
return (E) elementData[lastRet = i]; // 使用lastRet记录上一次操作的位置
}
// 移除当前元素
public void remove() {
// 如果上次操作为remove,则抛出非法参数异常,所以循环直接使用iterator.remove() 就会抛出 IllegalStateException
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
// 调用当前集合的remove()方法,移除当前的元素
ArrayList.this.remove(lastRet);
cursor = lastRet; // 指针变为上次操作的索引(删除,数组后面的元素会向前移动,下次遍历的元素位置是coursor+1)
lastRet = -1; // 标记上次操作为remove
expectedModCount = modCount; // 更新当前修改次数
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
// 检查修改次数是否发生变化: 即检测list是否被修改
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
按位运算符
按位&
非短路与,所有的表达式都会执行,最主要的功能不是用来连接判断条件表达式的.
复制 表达式1 & 表达式2 & 表达式3 ....
当前仅当所有表达式为true的时候,整个判断结果才是true. 最主要的是具备运算能力.
复制 当且仅当全部是1的情况,结果才是1.
10&8=8
1 0 1 0
1 0 0 0
-------
1 0 0 0->十进制8
按位 |
非短路或,所有的表达式都会执行,最主要的功能不是用来连接判断条件表达式的.
复制 表达式1 | 表达式2 | 表达式3 ....
只要有一个表达式是true,那么结果就是true:, 最主要的是具备运算能力
复制 只要出现一个1,就是1
10|8=8
1 0 1 0
1 0 0 0
-------
1 0 1 0->十进制10
取反
1变0,0变1
复制 ~10
00000000 00000000 00000000 00001010
11111111 11111111 11111111 11110101
异或. ^,相同为0,不同为1
复制 10^8^8 = 10
1 0 1 0
1 0 0 0
--------
0 0 1 0 -> 2
1 0 0 0
-------
1 0 1 0 -> 10
结论 :一个数连续异或同一个数两次,结果是它本身. 应用 :以后可以利用^进行加密和解密[IO流...]
位移运算符
面试 :位移运算是计算机中性能最高的运算方式,请您以最高性能的方式计算出2的3次方
复制 System.out.println(2+2+2+2);
System.out.println(2*2*2);
System.out.println(Math.pow(2,3));
System.out.println(2<<2);
<<:左位移...
2<<2
000000 00000000 00000000 0000001000 -> 8
>>:带符号的右位移,移动了多少位,就在最高位补符号位
>>>:不带符号的右位移,移动了多少位,就在最高位补几个0
0 正 1 负
面向对象基本概念
软件开发主流开发方法:
结构化程序设计
结构化程序设计 :自顶向下、逐步求精、模块化
结构化程序设计步骤 :
结构化分析(Structured Analysis,SA):需求分析
结构化设计(Structured Design,SD):系统概要设计、详细设计
结构化编码(Structured Program,SP)
主张按照功能将软件系统划分,也称为 面向功能程序设计 ,每个功能都有输入输出, 函数 就是结构化程序设计的最小单位。
自顶向下:每个模块包含子模块,子模块又包含子模块
逐步求精:先完成大体结构,再具体到小功能(主函数作为入口, 将其他众多函数串起来)
缺点 :
扩展性差,修改实现方式,需要自顶向下的修改模块结构
面向对象程序设计
面向对象:使用类、对象、封装、继承、消息等基本概念进行程序设计,根据客观事物进行抽象来构造软件系统。
类 是面向对象程序设计的最小单位,是具有共同属性方法的一类事物,是对客观事物进行抽象模拟,通过公共方法暴露该类提供的功能,从而提高内聚性,降低耦合,类是由方法和属性组成:
复制 成员变量(状态数据) + 方法 (行为) = 类定义
对象 :是类的具体实例,反应客观事物,具有标识唯一性、分类性、多态性、封装性、模块独立等。
消息 :对象直接相互通信合作,需要“消息”机制完成
面向对象的三大特征 :
封装 是指将对象的具体细节隐藏起来,使用公共方法暴露对象的功能
多态 则是指子类对象赋值给父类引用,编译时类型为父类,运行时类型为子类,意味着统一类型对象,可能表现多种特征。
抽象 也是面向对象的重要组成部分,但是所有编程语言都需要抽象,抽象只考虑部分可归类的问题,不考虑所有细节。
面向对象软件开发三个阶段
面向对象软件开发,需要经过三个阶段: