Java
C类语言:底层都由C语言实现,语法和C语言一样
程序语言的建立
- 原始团队:创建具备基本功能、具有内核核心的新语言
- 邀请:业界大牛进行功能拓展和完善
- 推广吸引更多业界大牛进行
- 语言的版本更新后,功能更为全面——有更完善的库实现,实现某些代码的代码会越来越少。功能丰富不代表性能提高,经典版本更加稳定,出错易修改或不易出错
Java语法同等于C语言语法——高中数学语法
高中数学语法:Java,GO,C,C++,C#。性能快
初中数学语法:PHP,Python,JavaScript。性能慢
语法类似的学会一个就都能融会贯通
JavaJDK:运行
JavaEE:web开发
JavaSE:用于桌面或简单服务器应用开发
JavaSDK:样例代码和使用说明
图形化界面开发已经被C#淘汰,C#由微软开发,与Windows适配度更高
Java命名规范(最好这么写):
- 大驼峰:多个单词组成,每个单词的首字母要大写。
类名
- 小驼峰:多个单词组成,除了第一个单词其余单词首字母大写。
变量名,方法名,文件名,文件夹名
- 全部大写。
常量名字写出来的程序要让别人看懂。
/*
作者:
最后一次维护:2024年10月,未离职
联系邮箱:
此类作用:
*/
public class Main{}
数据类型
八大基本数据类型:byte-8 short-16 int-32 long-64 float-32 double-64
char-8/16/24/32(根据编码类型决定) boolean-32
1字节 = 8 bit
K = 2 ^ 10,M = 2 ^ 20,G = 2 ^ 30
byte-8 bit; // 1字节
short-16 bit; // 2字节
int-32 bit; // 4字节
long-64 bit; // 8字节
float-32 bit; // 4字节
double-64 bit; // 8字节
char-8/16/24/32 bit("根据编码类型决定"); // 1/2/3/4字节
boolean-32 bit; // 4字节
byte x1 = 12; // 不满8比特在前补0,其他数据类型同理
short x2 = 9;
int x3 = 1;
long x4 = -10L;//不加L,数据上限和int一样
float x5 = 23.45F; "float 和 int 相比,int 的精度更高"
double x6 = 234.56;
char x7 = '9';
// 字符——符号——几何图形,即代表形状,表达形状
boolean x8 = true;
""
float 和 int 相比,int 的精度更高:float 在十进制下,能达到39位,但是有效位数只有6~7位,后面的数据无法准确表达,而 int 类型最高损失1精度。在少位数时,精度相同。double有效精度为15位。
这是因为:二进制较难表示浮点数,使用大数时会降低精度
计算机的存储基础是 1字节——8bit,为什么boolean要用32bit?这是为了缓存行填充提升性能,减少阻塞。高速缓存中的每一缓存行是64字节,boolean的底层是由C语言的六种数据类型来完成的,数据传输通过是电压在总线中传输实现,由于电压不能连续传输,语言对boolean依赖较高的情况下,如果boolean类型较小,传输等待时间会加长,导致传输阻塞卡顿。高质量代码离不开计算机底层。
// 整数类型
int x1 = 0b100110; // 2进制
int x2 = 0xaf82; // 16进制
int x3 = 07564; // 8进制
// 在不超范围的情况下,四个进制的用法是一样的
// 2进制转10进制,10进制转2进制 M进制转N进制
/*
1后面有M个0,N进制情况下:N^M ------> N进制转10进制
*/
/*
10进制转7进制
234:
234/7 = 33......3
33/7 = 4......5
4/7 = 0......4
倒写余数:453 √
M进制m转N进制n:
m除以n,商0为止,倒写余数
*/
int 类型数据计算超范围后,只保留右侧对应32位数。第一位确定符号,1代表负数,0代表正数。
负整数的表示,原码的符号位不变,换为反码,反码+1变成补码,使用补码表示。
float;
1位符号位,8位阶位,23位数值位;
"float的构成"
x(0-255) y( 0 - (2^23-1) );
float = ( 2 ^ (x-127) *y )
int;
1位符号位,31位数值位;
double--64: 1 + 11 + 52;
"double的构成"
x(0-2047) y( 0 - (2^52-1) );
double = 2 ^ (x-1023) *y;
float 和 double 在离 0 越近的时候表示的越精确
char x1 = 'w';
char x2 = '3';// char 类型数据可以进行加减乘除,需要int类型来接收
int x3 = x1 + x2;
int x4 = x1 * x2;//
显示器显示原理
视觉信息:形状、颜色和亮度,像素点形状固定:圆形和方形
每个像素点的信息:坐标,颜色,亮度
每个像素点的不同亮度可以实现不同颜色的显示,每个像素点的颜色等级可以用8 * 3 = 24bit 来记录,24bit既表示颜色由表示亮度。
两个short类型数据表示坐标。
一个像素点用 56bit 表示信息完全足够。
显示文字的内部原理和图形一样,都有固定的比特点,有固定的代号即编码,并将编码存储在库中,使用编码降低了存储压力。因为文字的不同形状,有不同编码来保存。不同编码之间不能互相解析,会出现乱码。
GBK编码——ANSI,Windows在编译JDK中默认为ANSI编码
记事本数据存储
比特流存储。
流的本质就是数组
char类型本身就是数字,使用编码存储。
如何查看char类型变量的编码:使用int类型变量进行接收编码
输入法输入原理
在记事本中存入的都是编码。
输入一个f,找的是f的编码,然后以f开头的例如有那四个,找到那些对应文字,这些文字也是编码。
Unicode编码是16位的,也是16进制的,每四位代表一个数值,\u表示用Unicode编码。
码点:与一个编码表中的某个字符对应的代码值。整套的叫做编码。即字母和字母表的关系
变量
使用变量需要事先说明,包括类型和名字。变量名规定:以特殊符号、数字、字母为开头。
变量需要初始化即初次赋值才能使用。
常量:值不会被更改的变量。使用 final 关键字进行修饰
final关键字:
- fianl修饰的基本类型:不可以第二次赋值。
- final修饰的引用类型:不可以第二次改变指向。
- final修饰的类:不可以被继承。
- final修饰的方法:不可以被重写。
- final修饰的防止指令重排序,遏制流水线性能优化,保障多线程并发场景下的可见性。
在其他语言中同样适用
final 通常会和 static 一起使用
int a = 100;
int b =6;
int c = a/b; // 16,向下取整,即结果浮点数的整数部分
int d = a+b; //
int e = a % b; // 4
数学函数与常量
有现成的工具可以直接 import
开根号
首先得到一个最大的数使得该数的平方略小于目标数,把此时的商(整数部分)与2相乘,做竖式除法。小数部分把商。
放下两个0,(去掉小数点 * 20 + x )* x(x挨个去试)< 放下两个0后的数,将 x 放到结果位置。
类型转换
容量多的容纳容量少的没有语法错误。会产生丢失精度的情况。虚线表示A到B会丢失精度。一般是被转换的类型的数值位与转化到的类型的数值位不匹配产生的末尾数据丢失。
转换成容量少的需要强制类型转换。只留最右侧的对应位数,其他舍弃。
自增与自减运算符
int a = 9;
int b = a++; // a = a+1,先算其他的,最后算a++。先将a=9赋值给b,再a自增
int c = ++a; // a = a+1,先算算a++,最后其他的。先a自增,再将a=9赋值给b
System.out.println(b); // 9
System.out.println(c); // 11
// 面试点
int d = 1;
d = d++; // 一个变量 n : n++ 或 n-- ,是无效的。
System.out.println(d); // 1
int e = 1;
e = ++e;// 一个变量 n : n = ++n 或 n = --n ,是有效的。
System.out.println(e); // 2
逻辑运算符
&& 并且
|| 或者
三目运算符
变量 = 表达式 ?值1 :值2
表达式为真时,值1赋给变量,否则值2赋给变量
import java.lang.Math.*;
public class Main{
public static void main(String[] aaa){
int a = 9;
int b = 12;
int c = a>b?23:11;
char d = a == 9?'w':'k';
float e = a+b >18 ? 23.24F : 11.9F;
System.out.println(c);
System.out.println(d);
System.out.println(e);
}
}
public class Main{
public static void main(String[] aaa){
int a = 9;
int b = 12;
int c = a>b?23:11;
char d = a == 9?'w':'k';
float e = a+b >18 ? 23.24F : 11.9F;
System.out.println(c);
c = a & b; // a 与 b ,上下
System.out.println(c);
c=a|b; // a 或 b
System.out.println(c);
c = a^b; // a 异或 b
System.out.println(c);
c = ~a; // 非a
System.out.println(c);
// a = 101011
// b = 10011
// 与运算将二进制数据进行按位对比,同逻辑运算符运算。
}
}
public class Main{
public static void main(String[] aaa){
int a = 43;
int b = 19;
int c = a & b;
System.out.println(c);// 3
c=a | b;
System.out.println(c);// 59
c = a^b;
System.out.println(c);// 56
c = ~a;
System.out.println(c);// -44
}
}
// 位运算涉及一些算法,&和^
// x - - - > 判断x的二进制从右往左第m位是0还是1
// 假设x 二进制为 sssssss
// 取第m位为1的和x位数一样的二进制,则使用&运算判断
// x & 2^(m -1 ) = 0; 该位为0
// x & 2^(m -1 ) = 2^(m -1 ); 该位为1
// ^ 两数交换不涉及第三个变量。
// x ^ s =k;
// 使用二进制,根据s和k一定能反推x出来
// ^ :
// 0 ^ 1 = 0
// 0 ^ 0 = 1
// 1 ^ 0 = 0
// 1 ^ 1 = 1
public class Main{
public static void main(String[] aaa){
int a = 7;
a = a << 3;// a=56
a= a >> 2;// a = 14
// 左移多少位就是乘 2 的多少次方
// 右移多少位就是除 2 的多少次方
// N进制左移M位都是相当于乘以 N 的 M 次方
// 动一次位移解决相加或者相乘的问题
System.out.println(a);
}
}
// a* 64 - - -> a<<6
// a * 137 - - - > a * (128+8+1) = a << 7 + a << 3 + a
// 位移运算符非2的整数倍时,可以拆成2的整数倍
// 任何十进制都可以变成包含若干个1 的二进制。
// 110101 = 1 + 100 + 10000 + 100000
// a * m , m是万亿级别 的数字
// 万亿 = 10 ^ 12 = (10 ^ 3) ^4 < 1024 ^ 4 = 2 * 40
// 万亿级别的数字转换成二进制最多是 40 位,最多40个1组成
// a * m,即使是万亿级别的,计算机底层实际上计算几十次即可完成
// 位移计算会将数值位的1位移到符号位,出现正数乘积为负数
// 位移计算超范围计算会产生错误数据
// 左移运算时,后面百分百补充0
// 右移运算时,前面补充符号位,符号位是1就补1,符号位是0就补0
// a >>> 9 ,此时补0
字符串
底层是char类型数组
import java.util.*;
public class Main{
public static void main(String[] aaa){
String x = "差一个蒸熊掌蒸鹿尾";
x = x + "鱼香肉丝";
x = x + 32 +54;
String str1 = "aabbccddkkkkk----###13423";
String str2 = str1.replace("k","mm");
System.out.println(x);
System.out.println(str2);
String[] arr = str1.split("cc");
String str3 = "ddkkkk";
int index = str1.indexOf(str3);
System.out.println(Arrays.toString(arr));
System.out.println(index);
System.out.println(str1.substring(2,5));
// 包括2但不包括5,左闭右开
}
}
字符串不可变
——原内存地址的值不可被修改
字符串类型是引用数据类型,底层是c语言的指针。
import java.util.*;
public class Main{
public static void main(String[] aaa){
String x = "aaaa"; // char [],内存中仅开辟对应大小的区域,不可越界访问,属于违法访问。
// 字符串值发生改变时,创建新的空间
x = "sdf";// 底层使用了malloc,创建新内存空间
String str1 = "aabbccddkkkkk----###13423";
}
}
对非基本类型,== 比较变量是否指向相同
常量池:如果有现成的String类型数据,会指向现成的变量。即两个变量指向相同。面试点
字符串使用量很大,没必要重复创建,使用常量池中的String即可。
常量池作用:
在Java中,对于没有指向或不使用的内存地址,会自动回收,进行清除。
但是在常量池中,字符串空间不会被立刻回收,如果有变量要使用该空间,会令变量指向现有的字符串。
import java.util.*;
public class Main{
public static void main(String[] aaa){
String s1 = "aaa":
String s2 = "bbb";
boolean x = s1 == s2;
// 对非基本类型,== 比较变量是否指向相同。
System.out.println(x);// true
}
}
import java.util.*;
public class Main{
public static void main(String[] aaa){
int[] arr1 = {12,23};
arr1 = new int[] {1,2,3,4};
String s1 = "aaa":
s1 ="zzz";
String s2 = "bbb";
String s3 = new String("aaa");// 创建一个新String数据,但不在常量池存储该内容
String s4 = new String("zzz");
// 见到new一定是开辟了个新的空间,并将值存储到该空间中
System.out.println(s1 == s2);
// 使用equals比较值是否相等
System.out.println(s1.equals( s2 ) );
System.out.println(s1 == s3);
System.out.println(s2 == s4);
}
}
// equals() 作用
// 每个类都用这个方法,都是继承Object,默认和 == 一样,比较指向是否相同
// 字符串中对这个方法进行了重写,仅比较值
// 重写equals还需要重写hashcode(),因为hashmap的使用需要这两个方法配合
// 面试题会问如何配合,hashmap存放数据原理
见到new一定是开辟了个新的空间,并将值存储到该空间中。
见到new一定涉及底层的 malloc ,创建新的内存空间
字符串比大小,使用equals是最稳妥的。
空串和null串
String s1 = null;// 没有任何指向 String s2 = "";// 指向空字符串 ,本质上是{'\0'},是占用内存的
构建字符串
String和StringBuilder和StringBuffer的区别
拼接速度:StringBuilder 和 StringBuffer 的拼接速度快于 String
StringBuilder 和 StringBuffer 相比:StringBuilder 多线程并发操作不安全,StringBuffer 多线程并发操作下安全
StringBuffer 里面加了锁,速度稍微慢了一点点。
String 和 StringBuilder 和 StringBuffer 的构造原理
- 内存构造
内存按页进行存储数据,如果按块进行存储,那么对内存的读写就会按块,每次读写的时间开销就会很大。对页进行存储,每次存储4kB,存储三个页,只需要下达三次地址指令即可,每个内存页对外只显示一个地址,内存页中其他地址对外是透明的,所以一个内存页只能使用一个变量,但是多余的空间就浪费了。内存页越大,读取越快,浪费的比例也越低,内存页越小,读取越慢,浪费的比例也越小。将内存页设置为4KB属于折中处理,操作系统以4kB空间起步发放,即一个内存页最小为4kB,若申请7kB,则批给两个内存页
数组里面的数据可以放在一个内存页,因为长度相同,可以确定具体的位置。弱类型语言例外,默认不区分类型,如Python,PHP,JS,SHELL等,这些语言的数组中可以放不同类型的数据,每个数据大小不一样,底层还是C语言,会将不同类型放到不同的内存页。弱类型语言对内存占用比较高,使用时需要进行旧内存空间的回收和新内存的申请。
字符串拼接时,对于不可变对象,每次的拼接都会创建新的空间进行存储,每变化一次消耗一个内存页对于下面代码,循环了10w次,前4096次长度在4kB之内,每次消耗一个内存页,后面后4096次, 长度超过4kB,每次申请2个内存页,依次类推
前四万次,平均消耗5.5kB。后面每次20多个内存页,整个过程平均下来,每次申请10多个内存页。十万次消耗了约1.2GB。消耗这么多内容,本质在于该类型数据本地不可变,即原地址值不可变。
如果本地可变,则不需要每次申请新地址,直接在原地址上进行修改。本地可变,可以极大降低内存损耗。StringBuilder就是这个原理
StringBuilder在创建时可以指定申请多大空间,可以在构造方法中,传入长度参数,默认长度为16个。
前16次在本地修改,不够了将原来的拷贝,并将长度增长为原来的两倍,以此类推。
StringBuilder使用缓冲区机制。这种机制,不用每次申请空间,先缓冲到缓冲区,内存缓冲满了再申请内存空间,大大减少了内存开销。StringBuilder的前身是StringBuffer。
缓冲区在类名中一般带着buffer,底层都是用缓冲区数组,速度很快,对内存的损耗都很少。文件流操作,网络流操作都会用到缓冲区。
流本质上也是数组,stream等。
好的代码 = 性能棒 + 可维护程度(懂计算机基础根基)
public class Main{
public static void main(String[] aaa){
long starttime = System.currentTimeMillis();
// 从1970年第一天开始计算毫秒数
String str = "";
for(int i = 0;i<100000;i++){
str+="a";
}
StringBuilder str2 = new StringBuilder();
// 拼接字符串速度比String类型快
for(int i = 0;i<100000;i++){
str2.append("a");
}
//System.out.println("str = "+ str);
//System.out.println("str2 = " + str2);
long endtime = System.currentTimeMillis();
long timeall = endtime - starttime;
System.out.println("总计花费时间:"+ timeall );
}
}
字符串的输入和输出
import java.util.*; // 算法比赛第一条先写这一句
public class Main{
public static void main(String[] aaa){
System.out.println(Arrays.toString(aaa));
// 其他高级语言包括C语言,也是用这种方式对主方法进行传参
Scanner x =new Scanner(System.in);
int k1 = x.nextInt();
int k2 = x.nextInt();
float k3 = x.nextFloat();
String k4 = x.nextLine();
k4 = x.nextLine(); // 吃掉一个回车
String k5 = x.nextLine();
String k6 = x.nextLine();
String k7 = x.nextLine();// 空格和回车属于字符串,需要隔离
System.out.println(k1+ "," + k2 + "," + k3);
System.out.println("k4="+k4);
System.out.println("k5="+k5);
System.out.println("k6="+k6);
System.out.println("k7="+k7);
}
}
标签:Java,String,int,基础,System,内存,println,out
From: https://blog.csdn.net/m0_75260099/article/details/143358133