一、基本数据类型
二、基本描述
bit --位:位是计算机中存储数据的最小单位,指二进制数中的一个位数,其值为“0”或“1”。
byte --字节:**字节是计算机存储容量的基本单位,一个字节由8位二进制数组成
一个byte的表现形式:
1000 0011
由8位0或1二进制码组成
其中第一位符号位表示正负数,所以1000 0010 = 0* 2^0 + 1* 2^1 = -3
所以byte的最大值:0111 1111 ;因为111 1111再进1位1000 0000=2^7,所以111 1111为2^7-1 = 127
为什么byte的最小值是-128
正整数表示:
0000 0001 - 0111 1111 ====》1-127
负整数:
1000 0001 - 1111 1111 ====》-1-(-127)
零:
0000 0000
那还有一个数:
1000 0000 ===》 -0表示啥呢?
-127 - 127的范围表示了255个数,但是八位二进制数由256种可能。
即2的8次方
我们先来看二进制数的计算:
2+1 = [0000_0010]原+[0000_0001]原=[0000_0011]原 = 3
1+-1=[0000_00001]原+[1000_0001]原=[1000_0010]原=-2
显然计算的时候出现了问题,为什么?
补码的来源
从上面的原码表中可以看见左边每增加一个二进制单位对应的真数是递减的,而右边每增加一个二进制单位对应的真数是递增的,所以对于原码来说,能满足正数的加法,但无法满足负数的加法
为了满足负数对加法的需求,就必须让负数与他对应的二进制码是同步递增或者同步递减
众所周知,减去一个数相当于加上这个数的相反数,这样减法就转换成了加法。
于是就通过符号位不变,其余位取反来满足这个同步递增或者递减的要求,由于正数本来就满足它本身的加法,所以不需要做任何改变。这就是反码的定义由来。
从上图的反码表中可以看到在运算不跨过0的时候,正负数的加法已经能满足要求
-2+1=[1111_1101]反+[0000_0001]反=[1111_1110]反=-1
127+1=[1000_0000]反=-127=128 加法算出来是128,由于128超过最大值,余1,所以取最小值开始的第一位,也就是
最小值-127,但是这里有个不合理的地方,就是[1111_1111]和[0000_0000]都表示0,这导致在实际计算中每当跨过0一次,就有一个单位的误差
-1+2=[1111_1110]反+[0000_0010]反=[0000_0000]反=0
要解决这个问题就必须让反码中的[1111_1111]和[0000_0000]合并,由于[1111_1111]+[0000_0001]=[0000_0000],所以在负数反码的基础上+1就可以解决反码中跨0的误差问题,同时不会对负数与它对应的二进制反码的同步递增产生影响,所以在反码的基础上+1就完美的解决了符号参与预算的问题,这就是补码为什么是在负数反码的基础上+1的由来。
回到原来的问题0000 0000 - 1111 1111表示了在-127 - 127的范围,那么1000 0000 就可以用来表示-128
[-128~127] 刚好256个数值,即2的8次方。
其他类型的表示范围也是以此类推。
在Java中整型、实型、字符型被视为简单数据类型,这些类型由低级到高级分别为:
(byte,short,char)--int--long--float--double
整数比浮点数低级。低级到高级可以自动转换。而高级到低级需要用代码强制转换,不强转会编译错误。
三、Char字符型
1.JAVA中,char占2字节,16位。可在存放汉字
2.char赋值
char a='a'; //任意单个字符,加单引号。
char a='中';//任意单个中文字,加单引号。
char a=111;//整数。0~65535。十进制、八进制、十六进制均可。输出字符编码表中对应的字符。
注:只能放单个字符。
3、char运算
在JAVA中,对char类型字符可以与ASCII表对应的数值来处理,可以做一些位运算
所以在遇到一些字符串查找字符等操作可以使用char对应一个数值的特性来提高计算速度。
算法示例:
判定字符是否唯一
实现一个算法,确定一个字符串 s 的所有字符是否全都不同。
示例 1:
输入: s = "leetcode"
输出: false
示例 2:
输入: s = "abc"
输出: true
限制:
0 <= len(s) <= 100
如果你不使用额外的数据结构,会很加分。
思路
由于ASCII码字符个数为128个,而且题目说了如果你不使用额外的数据结构,会很加分。因此可以使用两个64位的long变量来存储是否出现某个字符,二进制位1表示出现过, 0表示未出现过。具体代码如下:
public boolean isUnique(String astr) {
long low64 = 0;
long high64 = 0;
for (char c : astr.toCharArray()) {
if (c >= 64) {
long bitIndex = 1L << c - 64;
if ((high64 & bitIndex) != 0) {
return false;
}
high64 |= bitIndex;
} else {
long bitIndex = 1L << c;
if ((low64 & bitIndex) != 0) {
return false;
}
low64 |= bitIndex;
}
}
return true;
}
作者:yuruiyin
链接:https://leetcode-cn.com/problems/is-unique-lcci/solution/java-wei-yun-suan-by-npe_tle-2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
首先利用char字符的对应ASCII码数值特性切入,该题目涉及位运算,后面再学习补充。
四、浮点型
基础: IEEE754是现在公认的、最广泛使用的浮点数转换运算标准,所以要知道浮点型在计算机的表示方式需要知道IEEE754标准
知识点一:IEEE754单精度(32位)的二进制排列规则:符号位S(1位,0为正数,1为负数) + 阶码E(8位) + 尾数M(23位)
知识点二:单精度的偏置常数为127(固定值),阶码 = 127 + 阶
知识点三:计算公式
s为符号位,Exponent为指数位,Sig为尾数为部分
float单精度、32位(4个字节)
### double双精度、64 位(8个字节)
### float单精度计算转化过程
以float单精度计算转化为例:
-22.75转二进制码
float a = -22.75的二进制码式什么
1.转换整数部分:
22 = 10110
2.转换小数部分:
.75 = .5 + .25 = .11(二进制)
3.把两部分组装起来,变成标准化:
10110.11 = 1.100112^4*
4.转换指数部分:
阶码 = 127 + 阶
127 + 4 = 1000 0011(二进制)
5.结果
1 <u>1000 0011</u> <u>10011</u> 00 0000 0000 0000 0000(二进制)
二进制 1011 11101 110 0000 0000 0000 0000 0000转化为float是多少?
1. s = 1
2.Exponent = 011 11101
011 11101 = 125
125 - 127 = -2
3.Significand
float和double的一些总结:
float:32位,数据范围在3.4e-45~1.4e38,直接赋值时必须在数字后加上f或F
double:64位,数据范围在4.9e-324~1.8e308,赋值时可以加d或D也可以不加
float、double两种类型的最小值与Float.MIN_VALUE、 Double.MIN_VALUE的值并不相同,实际上Float.MIN_VALUE和Double.MIN_VALUE分别指的是 float和double类型所能表示的最小正数。也就是说存在这样一种情况,0到±Float.MIN_VALUE之间的值float类型无法表示,0 到±Double.MIN_VALUE之间的值double类型无法表示。这并没有什么好奇怪的,因为这些范围内的数值超出了它们的精度范围。
Float和Double的最小值和最大值都是以科学记数法的形式输出的,结尾的"E+数字"表示E之前的数字要乘以10的多少倍。比如3.14E3就是3.14×1000=3140,3.14E-3就是3.14/1000=0.00314。
五、引用类型
了解引用类型之前,可以先看一下Java运行时的内存分配模型
Java把内存分成两种,一种叫做栈内存,一种叫做堆内存
- 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配。当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量分配的内存空间,该内存空间可以立刻被另作他用。
- 堆内存用于存放由new创建的对象和数组。在堆中分配的内存,由java虚拟机自动垃圾回收器来管理。
- 在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于数组或者对象在堆内存中的首地址,在栈中的这个特殊的变量就变成了数组或者对象的引用变量
堆内存特点:
- 每一个 new 出来的东西都有地址值;
- 每个变量都有默认值 (byte, short, int, long 的默认值为 0;float, double 的默认值为 0.0;char 的默认值为 “\u0000”;boolean 的默认值为 false;引用类型为 null);
- 使用完毕就变成垃圾,但是并没有立即回收。会有垃圾回收器空闲的时候回收。
栈内存特点:
- 局部变量:在方法的定义中或者在方法声明上的变量称为局部变量。
- 特点:栈内存的数据用完就释放。
所以引用变量存在两个属性:
- 引用变量本身的值,存储在堆中
- 引用变量的在堆内存中的地址指针变量,存储在栈中
引用的过程:
用以下的代码来看一下引用类型存储模型与基本类型变量只存储在栈的问题
package com.java.test;
public class MainTest {
public static void main(String[] args) {
Student s = new Student("小明",1);
int a = 10;
System.out.println("学生的信息如下: "+s.toString());
System.out.println("变量a的值="+a);
change(s,a);
System.out.println("调用方法后");
System.out.println("学生的信息如下: "+s.toString());
System.out.println("变量a的值="+a);
}
static void change(Student s, int a) {
a=a-10;
s.setAge(1);
s.setName("大明");
}
}
class Student{
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
运行结果: