Java基础学习系列
1.“Java程序设计概述”篇
2.“ Java基本程序设计结构” (一)篇(本文)
3.持续更新~~
目录
前言
一、一个简单的Java应用程序
1.part1 (大小写)
2.part2 (修饰符)
3.part3 (class)
4.part4 ( {} )
5.part5 ( static )
二、注释
1.第一种注释 ——> //
2.第二种注释 ———>/* */
3.第三种注释
三、数据类型(重点)
1.整型
2.浮点字符类型
3.字符类型(char):
4.boolean布尔类型 (真值)
5.变量和常量
1)声明变量:
2)变量初始化:
3) 常量
4)枚举类型
四、运算符
1.算数运算符
2.强制类型转化
3.自增与自减运算符
4.关系和boolean运算符
5.位运算符(难点)
总结
上一篇文章(有兴趣的读者本文顶部有跳转链接)
本次内容会比较多,请耐心看完哦~相信看完,你会发现收获挺大的
===============================================================
我们简单了解了一下Java的一些基本知识和对编程初学者的一些建议,这一次我们来简单学习一下Java程序基本结构的内容~~
public class HelloWord {
public static void main(String[] args) {
System.out.println("Hello word");
}
}
“HelloWord” 这个程序属于是经典程序了,别看他比较简单,但是它可以流传这么久并用来作为初学者的第一段代码,肯定是有原因的,这里面能讲的东西挺多的,那么接下来就让我们来研究一下这段程序代码(030)。
==================分割线 ==================
首先 是大小写和拼写问题,Java是区分大小写的,假如你把main()写成了Main()或者mian(),这个时候你会发现程序执行的不是当前想运行的,所以这是我们应该特别注意的一个地方,这个是许多编程初学者都会犯的错误,main()函数是每个主程序都带必须带有,因为它可以说是第一个被调用到的东西,然后才能通过它来带动其他方法(这里留一个思考:方法是什么,这个在后边文章会解答,现在先了解一下),这样程序才能跑起来~~所以说当main()都写错了,你写的程序自然也就罢工啦~
==================分割线 ==================
然后是public这个 东西,在Java里面被称为访问修饰符(access modifer),访问修饰符其实不止这一个还有其他三个我们现在先不做过多赘述,你现在把它理解为,它们的作用就是限制它们自身能被访问的权限程度
==================分割线 ==================
public 后面是一个class 的玩意,它是一个关键字,表示的就是一个类,它的后面紧跟的就是他的类名
就拿下面这张图作为例子,HellWord就是它的类名,有的同学可能已经注意到了,类名的每个单词的首字母都大写的, 这其实是Java语言的命名风格,使用的是驼峰命名法,类名可以是多个单词组成,但是每个单词的首字母必须是大写!!!我们得养成一个好的编程习惯。
当然这只是类名的命名法,后面我们学到方法(Java叫做方法,C/C++叫做函数)时,它的命名法再另说~~
这里还有第二个我们要注意的东西,就是每个Java程序只能有一个主类,以我们下面这个图片介绍就是被public 访问修饰符修饰的类就是主类,假如一个Java程序里面有两个主类,程序会直接报错!!!所以我们要注意一下,避免编写类时出现错误(owo),类的编写后面文章会进行介绍,不着急~~大伙现在先有个映像~~
再然后就是Java文件和程序编译问题,源代码的文件名必须要与公共类的名字相等,而扩展名是".java"。以下面例子说明:
可以看到当主类名和文件名不一样时,就报错了, 这个初学者犯的错会比较多。
再来,以我们的HelloWord程序来说,它的文件名就是HelloWord.java,在创建之初,也只有它这么一个文件,但是在你运行之后,Java编译器会将这个文件解析成一个字节码文件,会自动的命名为HelloWord.class。下面就是文件保存在目录下的样子~~
Java文件在src文件下:
class文件在bin下:
这些大家就可以大致了解一下,重点还是命名要遵循驼峰命名法 和 主类名==文件(owo)
==================分割线 ==================
接下来是程序中的的这个大括号{}
被这对括号包围的代码被称为块,它通常用来划分程序的各个部分,让代码更清晰明了,增加代码的可读性,当然肯定是从”{“开始到”}“结束。
以下面的语句块为例子,这里只有一条语句,它的功能是把“HelloWord”打印输出到控制台,在这里是我们调用了System.out对象的println()方法“HelloWord”是println()的一个参数。
相信会有人回问System.out是一个对象???但是我没有创建啊?!(没有人问也得有doge)
其实这是Java类库里面已经写好的东西,我们可以直接用,所以这就是我们Java的一个优势。当然System.out的方法也不止这一个,也有一个我们C语言的输出语法printf(),写法是一样的,请大胆尝试~~
而对象和方法之间的 “.” ,相信大家也注意到了,这是Java的调用符号,接触过C的同学在结构体那边应该有接触过,功能可以理解为相同(owo)
{
System.out.println("HelloWord");
}
再又是代码风格的问题:我个人是倾向于第一种,应为第一种比第二种充分利用到了每一行,还是得看个人
第一种:
第二种:
==================分割线 ==================
static关键字:
这个关键字,表示静态,它这个静态的设立就使得这个方法在被编译之后,就已经存在了,这里的存在是指,已经被分配内存空间了,在即将运行的路上,如果没有static,这个main()方法就不会被调用到,因为它还没有被分配内存空间,意味着不存在。所以可以简单把static理解为存在与不存在的问题,后面的文章我们还会深究static这个关键字,这个可能属于比较难的部分,我们不着急~~
被static标记的常量被称为静态常量,方法的话,被称为静态方法
==================分割线 ==================
这次的HelloWord程序介绍先到这里,这里面还有一些东西牵扯到后续内容,后续我们在把它拿出来讲,以上这些我们先消化吸收~~
这么一看,简单的Helloword其实也不简单,就和初中高中简单的线性方程一样,也挺复杂的呢........颇有一股大道至简的韵味,哈哈哈,扯远了,我们进入下一模块学习~~
如下图,当被 // 注释的时候,语句就被程序屏蔽忽略掉了,你不打开
它就永远处于无法被察觉状态
这里教大火一个注释快捷键 ==> crtl + /
注意是反斜杠,不是除号,我当初呆呆的按了半天除号才发现不对劲(T0T)
如下图:
这种注释必须成对存在,不然会报错,只要代码在这两个东西里面,
都会被忽略掉。
如下图:
这种注释一般是用来描写代码信息、作者之类的
=================================分割线 ==================================
是不是发现注释挺简单的,简单那我们就进入下一节~~
类型 | 存储要求 | 取值范围 | 默认值 |
---|---|---|---|
int | 4字节 | -(2^31) ~ (2^31) - 1 | 0 |
short | 2字节 | -(2^15)~ (2^15) - 1 | 0 |
long | 8字节 | -(2^63)~ (2^63) - 1 | 0L(数值后面带一个L) |
byte | 1字节 | -(2^7)~ (2^7) - 1 | 0 |
值得一提的是,从Java7开始可以在数字字面量加下划线来增加数据可读性,Java编译器会自动去掉这些下划线。例如:1_000_000;
这里有同学会有疑惑为什么int是的31次方(没有也得有doge)
是这样的,以int为例子,int是4个字节(byte),一个字节是8个比特(bit),所以int最长的长度为4x8 == 32位,因为数据得保存正负号,所以第32位保存了,正负号,作为符号位,所以这样32位就少了1位,也就是31位,又因为二进制数只有 0 或者 1 ,所以就是排列组合问题,每一个位置两种结果,多个位置那就是2x2x2x2x2x..........所以就是2^31; (其他的整型以此类推)
那现在问题又来为什么正数这边要减一呢?????
来,我们看下面的解释
正数:
1的二进制32位表示是 0000 0000 0000 0000 0000 0000 0000 0001
2147483647的二进制32位表示是 0111 1111 1111 1111 1111 1111 1111 1111
2147483648的二进制32位表示是01000 0000 0000 0000 0000 0000 0000 0000
因为32位已经是极限,所以说214783648已经超出范围了,所以我们2^31需要 -1;
好了,问题又双叒叕来了,正数减一,而为什么负数到-2147483648呢???
这又是一个底层的东西,请看下图:
负数:
负数在内存保存的是以补码的形式来的(正数也是),用补码是因为补码把运算符由-号到变成+号从而提高计算机硬件方面的运算速度。
可能有同学还不了解补码,我多提一嘴:
正数以 5 为例子: (正数源码反码补码都相同)
5的源码:0000 0101
反法:0000 0101
补码:0000 0101
负数以 -5 为例子
-5的源码:1000 0101
反码:1111 1010 ===>符号位之外按位取反
补码:1111 1011 ===>符号位之外按位取反+1
=========================================================================================
简单介绍反码补码后,回到我们一开始的问题:为什么负数范围到 -2147483648 ???
原因是这样的,在数据存储中,0 是一个很特殊的东西
+0 的补码表示 : 0000 0000 0000 0000 0000 0000 0000 0000
-0 的补码表示 : 1000 0000 0000 0000 0000 0000 0000 0000
-2147483648 的补码表示 , 1000 0000 0000 0000 0000 0000 0000 0000
对于 0 来说 ,虽然补码不一样,但是都是一个东西,而 0 只需要一个,就选择了-0 作为最小的数,
那就是变成了 -2147483648,因给这两个的补码相同,为了不浪费这个内存空间,所以就把它添加进来啦~~所以就是 -2^31
值得注意的是:
-2147483648真正的补码:0001 1000 0000 0000 0000 0000 0000 0000 0000,
因为int只能保存32位,所以36位越界了,只保存了后面32位
==>1000 0000 0000 0000 0000 0000 0000 0000
※ -2147483648是在32位中不存在原码
看完上面的介绍,我们来看一段代码:
public static void main(String[] args)
{
System.out.println(Integer.MAX_VALUE); //Integer.MAX_VALUE = 2147483647
System.out.println(Integer.MAX_VALUE + 1);
System.out.println(Integer.MIN_VALUE); //Integer.MIN_VALUE = -2147483648
System.out.println(Integer.MIN_VALUE - 1);
}
大家伙觉得输出什么,可以自己参考上面的理论推算一下……(答案在下面)
是不是很意外??可能也没有很意外,但请配合我一下(owo)
其实就是但超过正数范围时,max正数会变成最小的,min负数会变成最大的,原因都在于他们都通过补码这个桥梁,跨越进了对方的世界中♥♥(解析如下)
以-2147483648来讲
补码:1000 0000 0000 0000 0000 0000 0000 0000
补码 - 1 ==> 1000 0000 0000 0000 0000 0000 0000 0000
- 0000 0000 0000 0000 0000 0000 0000 0001
--------------------------------------------
0111 1111 1111 1111 1111 1111 1111 1111
运算后的补码:0111 1111 1111 1111 1111 1111 1111 1111 ==>求原码
由于符号位是0 ,是一个正数,所以补码 == 原码,所以原码就是0111 1111 1111 1111 1111 1111 1111 1111,而这个值刚刚好就是 2147483647,所以控制台就输出这个啦~~
另一个 2147483647 留给大家根据理论计算一下,这样才能消化吸收~~(绝对不是我想偷懒.jpg)
截止到这里,我们介绍了Int类型的数据存储问题,其他类型的以此类推,由一知多
类型 | 存储需求 | 取值范围 | 默认值 |
float(单精度) | 4字节 | 大约±3.40282347E+38F(有效位6~7位) | 0f |
double(双精度) | 8字节 | 大约±1.79769313486234570E+308(有效位15位) | 0 |
这里有一点要注意使用float数据类型,定义的时候需要加一个F 或者 f的尾巴给它,double数据类型也是加一个d或者D,但是double一般都不用去加,所以如果你用float但是忘记加尾巴,那就会造成数据变成double类型,float接收不了,从而报错(下图为例子)
正确形式:
double d1 = 12.3;
double d2 = 12.3d;
double d3 = 12.3D;
float f1 = 12.3F;
float f2 = 12.3f;
错误形式:
float f3 = 12.3 //注意这会导致数据默认为double,float接收不了。
接下来是三个特殊值,因为不常见,我们简单提一下:
这三个常量表示都是Java类库里面定义好的了,我们调用就可以了
那下面就到了浮点数最最最最容易踩坑的地方,我也是经历过血与泪啊TOT
误差,这玩意在浮点数里面是避免不了的,这是因为二进制系统无法做到十分精确表示1/10.,就像十进制无法精确表示1/3一样,看下面例子:
public static void main(String[] args)
{
System.out.println(2.0 - 1.1);
}
大火猜猜输出什么(doge)答案在下面
是不是又疑惑,为什么不是0.9???这就是因为我们刚刚说的精度问题, 底层里面浮点数有二进制表示,难免有误差,这就容易导致数据差之毫厘,失之千里 ,所以我们尽量避免浮点数运算,可以把转化为其他整数保存,这种操作比较常见,看下面:
1/3 分成===>1 和 3 来进行保存,在一起和其他数进行比较,
这样就可以最大程度的保证数据准确,因为他们的运算结果是确定且唯一的!!!!
但是如果我就是要浮点数运算怎么办,办法还是有的,就是BigDecimal类,这个类会在后面文章“Java程序结构(二)”中详细介绍,等我肝出来owo;
char类型数据原本用来表示单个字符,毕竟之前默认是用英文,英文又是字母组成的,所以完全没问题,但是在现在的发展中,但是我们博大精深的中文就特别多了,而且是两个字符,所以Java就用了unicode字符集,这里包含的东西,差不多可以囊括整个世界的文字符号了。常用的字符编码可以去看看ASCII码表,这玩意不用特意去背,代码写多就记得了
接下来需要特别注意,单个字符的正确表示是像这样:‘A’ ,是单引号
用双引号(“A”)是字符串不是字符,两个东西是不一样的,就像字和句子的关系一样;
有一些特殊的转义字符,来,看表:
转义序列 | 名称 | unicode |
---|---|---|
\b | 退格 | \u0008 |
\t | 制表 | \u0009 |
\n | 换行 | \u000a |
\r | 回车 | \u000d |
\" | 双引号 | \u0022 |
\' | 单引号 | \u0027 |
\\ | 反斜杠 | \u005c |
unicode转义序列会在解析代码之前得到处理,例如 “\u0022+\u0022” ,并不是一个由引号包队加号构成的字符串。实际上\u0022会在解析之前转换为 “ ,这会得到"" + "" ,也就是空字符。
字符类型内容不多,大家简单了解一下,好的往下看~~
类型 | 真 | 假 | 默认值 |
boolean | true | false | false |
boolean 类型有两个值,false 和 true 用来判定真假,一般用在逻辑语句上面。
在C/C++里面 1 和 0 可以表示真值,但是Java不行,只有true和false,
boolean不能转化为整数,
终于到这里了,这里的内容非常的importance,在这个部分,你会学会如何去声明变量!!!
在Java语言中,每一个数据都有一个类型(type),它们在被声明的时候就被写清楚他们是上面类型的,int、double、boolean、long.......不像JavaScript能用var(虽然Java10之后,也可以在局部变量中使用,但是我个人推荐还是避免,因为代码可读性低),PHP可以用$,这些语言是弱语言类型,系统会自己去判断,但是我们的Java是强语言, 你最好还是在声明变量前指定它是什么!!!
double salary;
int number;
char letter;
boolean flag;
boolean flag ; //与上面一条语句效果相等
格式:
数据类型 + 变量名 + 分号
在Java中,不止在Java中,就算是其他语言,加 ; (英文状态下的分号) ,就说明你这句好结束了,分号前面的空格不影响。
另外一个重点就是命名规则!!!
规则如下:
1. 字母开头 并由字母或者数字构成的序列 =====>下划线也可以开头,但是不是优先选择
2. 长度基本不限 ====>太长也不好
3. 大小写敏感 ====>day和Day 是两个不同的变量
4. 不能使用Java的关键字作为名字
5. 目前下划线_可以用来做变量名 ,记住我说的是目前
5. 不可以存在运算符 + - * % / | & ~......
以下是错误示范
1. double 66dd;
2. int +dad;
3. boolean false;
声明一个变量以后,必须用赋值语句对变量进行显示初始化,千万不要使用没初始化的变量值。例如:Java编译器会把下面语句识别为错误的:
这个报错翻译过来大概就是:CSDN没有被初始化(错了找百度算账),意思大概了解就好了,反正它红了就不是什么好事~~所以得出未初始化的变量是无法使用的。
如果想要对一个已经声明过的变量进行赋值,就需要将变量放在等号(=)左侧,再把一个适当取值的Java表达式放在等号右侧,例如:
第一种:
int money;
money = 100;
第二种:
int money = 100;
特别注意的是,当把一个字符赋值给整型时,需要强制类型转化,然后系统就会把字符对应的ASCII码值交给整型变量 ,当整数转为为字符型是,需要赋值为对应的ASCII码值
int a = (int)'a';
char b = 98;
声明变量,像是标记一块内存空间,赋值相当于往内存里面放东西,只有放了东西,才能在未来调用的时候取到东西。
声明可以放在代码中的任何地方,但是变量的声明尽可能地靠近第一次使用的地方,这是一种良好的程序编写风格。
既然有常量就肯定有变量啦~~
在Java中使用final关键字来表示标识常量,只能进行一次赋值,赋值之后,后面的程序是无法修改到的。常量一般都是使用全大写来命名
当然常量也和变量一样,得初始化才能使用,下面是错误示范:
这里报的错误也是没有初始化。正确操作应该是:
假如你在声明语句后面对常量进行修改,也是不可以的;
前面我们有提到static这个关键字,它的使用,标志着被标识的东西是不是已经存在了。final可以和它一起使用,使得一个类里面的任何方法都能调用。
这条语句放到main()函数外面,就和C/C++的全局变量的效果一样,就是每个方法都能看得到它。
枚举类型就是把数据保存在特定的东西中。
enum Size {SMALL , MEDIUM ,LARGE}
public static void main(String[] args){
enum Size {SMALL , MEDIUM ,LARGE};
Size s = Size.SMALL;
System.out.println(s);
}
在java中,使用算数运算符 +,-,*,/表示加减乘除
①两个整数相除的时候,结果会默认为整数,实际数值的小数点后面的数会被丢掉
整型:
5/2 = 2
不是等于2.5
②两个浮点数相除,结果都是浮点数,有的时候需要考虑精度问题,一个整数一个浮点数相除时,整数型会自动转化为浮点型
浮点数
25.0/2.0 == 12.5
25.0/2 == 12.5
25/2.0 == 12.5
整数被0除将会产生一个异常,而浮点数被0除将会得到无穷大或NaN结果.
在赋值过程中,可以使用二元运算符,例如:
x+=1 等价于 x = x + 1
x-=1 x*=1 x/=1 x%=1 以此类推
③类型自动转化:
如果两个操作数中有一个是double类型,另外一个操作数就会转化为double类型。
否则,如果其中一个操作数是float类型,另外一个操作数将会转化为float类型,
否则,如果其中一个操作数是long类型,另外一个操作数将会转化为long类型,
否则,两个操作数都将会转化为int类型
虽然数据在运算时,会自动转化,但是有时得使用比它本身字节小的数据,这时候就得用到强制类型转化。
在 byte 、short、char、混合运算时,会自动转化为int类型进行计算,还是用同样类型接收的时候就要造成越界,所以就会造成以下代码编译报错:
public static void main(String[] args){
byte a = 1;
byte b = 2;
byte c = a + b;
//cannot convert from int to byte
short d = 1;
short e = 2;
short f = d + e;
//cannot convert from int to short
char g = 'g';
char h = 'h';
char i = g + h;
//cannot convert from int to char
System.out.println(c);
}
如果是依然要这么用就必须强制类型转化
public static void main(String[] args){
byte a = 1;
byte b = 2;
byte c = (byte)(a + b);
short d = 1;
short e = 2;
short f = (short)(d + e);
char g = 'g';
char h = 'h';
char i = (char)(g + h);
}
强制类型转化有可能会造成数据的丢失,但是丢失在可接受范围内就行了。
然后是不能用boolean类型与其他任何类型进行强制类型转化,会报错
如果试图将一个数值从一个类型强制转化为另一个类型,。而又超出目标类型的表示范围,结果就会截断成一个完全不同的值,例如(byte)300 的实际值为44
了解这两个东西,还是看例子最好:
int n = 0;
n++; 这个会在语句结束的时候才进行运算 +1
++n; 这个会在语句执行的时候就进行运算 +1
n--
--n 这两个也是同样道理
看看下面这段代码的执行
public static void main(String[] args){
int n = 0;
System.out.println(n++); // 打印0,结束后+1,此时n变成了1;
System.out.println(++n); // 打印2,在执行的时候就进行运算,在n的基础上+1;
}
众所周知,程序语言中比较两个东西是否相等是用的 == ,用一个 = 是赋值
下面是boolean运算符的介绍:
符号 | 意义 |
== | 相等 |
!= | 不等 |
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
&& | 与(and) |
|| | 或(or) |
3 == 7,这个表达式是错的,所以表达式的真值是false;
3 != 7,当然表达式是真的,表达式真值是true;
其他的类似 , 但是最后两个&& || 大家可能不认识,这两个就是中学时命题逻辑部分
的与和或
对于与来说,一假全假,两真为真,对于或,是一真为真,两假为假,
当expression1 ==> true
expression1 && expression2 ---->当expression2为假(false) ==>表达式为假,否则反之
expression1 || expression3 ==>表达式为真(true)
当expression2 ==> false
expression1 && expression2 ===>表达式为假(false)
expression1 || expression2 ---->当expression2为真(true) ===>表达式为真,否则反之
这里会有一个优先级的问题,优先级:&& > ||,所以在多层与或判断的时候,&&会优先结合数据再和 || 结合,expression1 && expression2 || expression3 这个语句隐藏的小括号是这样 的:(expression1 && expression2) || expression3 ,只是因为优先的问题,可以省略,但是如果你要expression2 和expression3一起的话,需要用小括号括起来,像这样:expression1 && (expression2 || expression3 )
看到这里,文章就接近尾声了,同时也到了文章最难的一个部分。位运算是一个效率特别高的东西,但是初学者来学习的话,大部分同学一时半会都看不懂,时间成本比较大,在你代码能力进一步提升后再回来学是更容易上手的,别担心,后面我会单独写一篇来介绍,这一次,我们先简单认识一下~~
什么是位运算呢??其实就是二进制的运算系统,如下是几个用的比较多的几个位运算符(当然不止这几个):
1.按位与 & :
当两个数进行与运算,对应位都同时为1 ,才为1
例如:
0000 1011
& 0000 1101
————————————————
0000 1001
2.按位或 |:
对应位有1,为1,同时为0,才为零,有点像加法
例如:
0000 1011
| 0000 1101
————————————————
0000 1111
3.按位异或 ^:
对应位一样为0,不一样为1;
例如:
0000 1011
^ 0000 1101
————————————————
0000 0110
4.按位取反 ~:
01互换
例如:
~0000 1011
_______________
1111 0100
5.右移>>、左移<<
二进制位整体向左向右移,不够位直接补零
如果你换算成十进制,你会发现向右移就是除2,左移就是乘2,这玩意可比乘2,除2的效率高
例如:
0000 1111 >> 1 == 0000 0111
0000 1111 << 1 == 0001 1110
再简单认识一下后, 我分享几个我目前经常用到的技巧,要是你把它再进作业里面,说不定能给老师眼前一亮(doge)直接记住就好,底层是为什么,感兴趣可以去查
1) 求二的次幂
假如一个数满足n&(n-1)== 0,那它就是二的次幂;
2)判断奇数偶数
假如一个数满足n&1 == 0,则为偶数,满足n&1==1,则为奇数
3)n>>= number 、n <<= number
n就变成了原来的1/2^n (2^n)倍
4) a^a == 0、a^0 == a、
5)二进制中,找1的个数(这里需要用到循环语法)
n=n&(n-1),统计运算次数直到当n=0时,停止
这篇万字博客,字字珠玑啊! 里面有一些基础知识,有一些底层原理,但无论是基础知识,还是底层原理,都是我们需要了解学习的。这些东西需要反复去了解学习才能真正化为自己的,即使它们有的时候真的挺难的。
Any way ,希望能给你们带来帮助,最近挺感慨的,希望大家但行代码路,每日一句“Hello world”让自己开心点,做人生的主类,执行正确的main()方法走在正确的道路上,要像变量不畏将来随机应变,又要像常量不念过往勿妄强求,相信梦想的表达式终将变成真值true~~
十分感谢能一路看下来~~(owo)
· 鞠躬!