(当年0基础时,学习 java 记的笔记,还挺有趣的。许多话十分不严谨!重要内容专题重写。)
1.1 环境搭建
1.1.1 java跨平台语言
众所周知,java是跨平台语言(在不同操作系统下运行)。*面试题:为什么 java 跨平台?为什么说 java 是半解释型半编译语言? *
需要先了解一下,java 程序的 windows下的开发过程:
1.编写可阅读代码,保存为 .java 文件。
2.jvm编译.java文件,生成 .class 文件。(字节码)
3.jvm解释.class文件,生成 .exe 文件。(机器码)
这里提到的 jvm 是 java 虚拟机,包含于 jre下(java运行环境),而 jre 又包含于 jdk(java开发套件)。所以,java 程序没有跟操作系统关联,不同平台对应不同的jdk,所以 java 程序具有跨平台性。类比window下的可执行文件.exe,不具备跨平台执行能力。
经常会有面试将 java 与 c++ 对比:都是面向对象语言,但c++是编译型语言,直接将代码编译成为二进制代码(可以讲,exe文件就是二进制文件,将exe文件还原成你能看懂的代码就是逆向工程),而且C++面向对象不够“纯正”,好像不写在类里也可以执行。我个人认为java更友好一点, 虽然速度慢,但没有指针啊,不用手动垃圾回收释放内存啊,没有奇怪的功能(goto、自动强制类型转换),还具有平台无关性。
其他:
java 写一个 helloworld 要比其他语言费劲多了, 好几行!好像有一本很出名的书写道:“java语言写起来的复杂程度,证明了这种语言的设计缺陷。世界上流行的语言都会向更快的方向发展,(一些脚本语言,python ruby )…”这我还是不敢苟同的,java语言这种纯面向对象,对于工程上来说是非常严谨的,往后学就知道了。
1.1.2 环境搭建与相关名词
安装jdk :Oracle官网
环境变量的配置:略, 参考百度(win10 , 只需要配置两个环境变量了。 环境变量只是添加一些 exe 文件的寻址路径)
javac.exe -> 编译器
java.exe -> 解释器
在完成第一个程序之前,需要了解一下dos的基本操作命令:
命令 | 功能 |
---|---|
cd | 切换操作目录 |
dir | 查看目录下文件 |
d | 切换到盘符d |
cls | 清屏 |
cd.. | 返回上级目录 |
copy | 复制 |
move | 移动 |
del | 删除 |
ipconfig | 查看ip |
ping | 链接某ip |
1.1.3 HelloWorld
1、打开记事本,键入以下代码:
public class HelloWorld{
public class void main (String[] args){
System.out.println(“HelloWorld!”);
}
}
2、保存为HelloWorld.java,
3、CMD中切换到该目录下,执行:
javac HelloWorld.java
java HelloWorld
1.1.4 集成开发环境IDE
主流:intellij
记事本:Notepad++ 、Sublime
操作系统:CentOS 7 、Ubuntu 18.4
注:收回我的推荐,idea是真好用,谁用谁知道
1.1.5 其他
在《java程序员面试笔试宝典》中,关于main方法面试题 :如何在main 方法前输出某语句 ?
其实, main 就是这个程序的入口 , 凡是程序总要有个入口。 public static void main
不能改动,String[] args
就是执行 java XXXX ...(参数)...
后面的参数 。至于加什么其他关键字,synchronized 、final 、加 static 块都是可以的。
1.2 变量与数据类型
1.2.1 命名规则
文件命名规则:
类名首字母大写;运行类名与文件名相同。如果public修饰,必须相同。所以,一个.java文件中只能有一个public修饰的类。
变量命名规则:
首字母小写,使用驼峰命名法,望文生义。避免java内部的关键字与标识符。
例如:int a;
int a=1,b,c,d;
同时声明多个变量,并初始化 a。
注:这里面有一个有趣的点,声明一个变量a ,是否分配内存空间?
答:对于基本类型变量声明却没有赋值,jvm是没有分配内存空间,而是在赋值的时候分配内存空间。而引用类型变量是一个地址指向了堆内存(即分配了一个地址空间),new肯定是开辟了内存空间的。(网上也有其他答案,我比较认同这个)
1.2.2 变量
1、 变量 : 简言之就是来标识一块内存,代表内存的一块空间。不同数据类型,需求的内存空间大小不同。
例如 a = a+10; //取出a内存空间的值+10 后再放入a内存空间。
2、变量可以重新赋值,不能重复定义。
3、变量的作用域 :{} 表示作用域,变量只在最近的作用域内有效。
4、使用变量前必须初始化并且赋值。
1.2.3 计算机基础 : 进制简介
1、
十进制(0-9):正常输入即可
八进制(0-7):数字开头0
十六进制(0-9 ABCDEF):数字开头0X
二进制(0,1):数字开头0B
2、x进制,即逢x进1
3、不同进制转换,通用思路:借助十进制桥梁 (逐位*10^n;除2取余数)
4、java进制:采用二进制补码方式表示数字。(32位)
补码:想表示负数,相当于在数轴上将值域折半左移。原理:1打头二进制数字,表示负数,即第一位位符号位(注意1000表示 -8)。转换规则:逐位取反加1 。
范围:-2^(n-1) ~ 2^(n-1)-1
推理得出:int的最大值+1 ,变成了int的最小值。
1.2.4 数据类型
1、 知识储备:
A | B |
---|---|
1byte字节 | 8bit 位 |
1kb | 1024 byte |
1m | 1024 kb |
1gb | 1024m |
1tb | 1024gb |
2、八种数据类型:
类型 | 空间 | 范围 | 其他 |
---|---|---|---|
byte | 1 byte | -128 ~ 127 | 8位 |
short | 2 byte | -32768 ~ 32767 | 不使用 |
int | 4 byte | -2147483648 ~ 2147483647 | 整型 |
long | 8 byte | 太长 64位 | 长整型 |
float | 4 byte | 3.402823e+38 ~ 1.401298e-45 | 浮点 |
double | 8 byte | 1.797693e+308~ 4.9000000e-324 | 双精度浮点 |
char | 2 byte | 0~65535 | 字符(无符号) |
boolean | 1 byte | --- | 逻辑变量TF |
详情见数据类型专题。
1.2.5 其他
1.java中整数**默认int **,小数默认double
2.long a = 100000000000L;//结尾加L
3.计算机中浮点数不能做精确运算,精确运算用其他方法。
1.2.6 类型转换
小范围到大范围自动完成转换,大范围到小范围强制类型转换,并丢失精度 。 例如(int)10.5
1.3 基本语法
1.3.1 运算符
1、常规运算符
\+ 加 两数 相加
\+ 连接符 字符串之间连接
- 减 相减
* 乘 乘积
/ 除 相除 整数相除 取整数部分
% 取模 整数相除取整数、浮点相除取浮点
++ 自增 区别 a++ 与++a
-- 自减 同上
< 小于 返回布尔
\> 大于 返回布尔
== 等于 返回布尔
= 赋值 右边赋值给左边
&& 与 返回布尔
|| 或 返回布尔
! 非 返回布尔
+= a=a+5 表示为 a+=5
注意: 如果a = 10 , 表达式 a++ 值 10;表达式 ++a 值 11。不论如何,此条语句过后a都自增,a的值是 11 。
2、未介绍运算符:<=小等于、 >=大等于、!= 不等于、(条件表达式)?(真,结果1):(假,结果2);
条件运算符,也叫三元运算符。
逻辑运算符注意事项:注意"短路"操作!
例如,&&的第一条件为F,则第二条件 (表达式)不执行!同理,|| 。
3 、移位运算:
>>
有符号右移 , >>>
无符号右移
int a = 10; a = a>>2 ;
表示 10的二进制1010右移两位,0010,a值为2 。 右移一位相当于除以2取整 。
符号问题:对于负数,>> 运算补位补1 。>>>
无符号移位 : 补0。<<左移,末位均补0。
中位数写法:a > b , mid = b + (a - b)>>1;
4、很少使用的位运算符:& ^ |
位与、 异或 、位非 ,用于一些算法题的特殊场合。注意异或这是一个可逆操作。
1.3.2 条件语句
条件语句属于程序的分支结构。程序运行有三种结构:顺序结构、分支结构、循环结构。
if(条件1){
code;
}else if(条件2){
code;
}else{
code;
}
注 :
1、如果代码只有一行,可以不写{}。
2、If 与三元运算符的转换问题:三元运算符,必须有结果返回。而 If 只为分支语句 , 可以没结果。也就是说,if可以控制流程,三目运算符要有返回值,所以其常在 return 使用。
分支结构中的 Switch 选择结构
switch(表达式)
{
case 1 :
....;
break;
case 2 :
....;
break;
default:
...;
break;
}
break关键字:跳出swtich 大括号;如果case中没有break,就会顺序执行下一个case。
default 最多只能出现一次,可以出现在和 case 并列的任意位置,无论如何都会执行default 代码。
面试题:
1.switch中允许的数据类型:byte,short,int,char,enum(枚举),String
2.switch的简化:case执行结果相同,语句可以省略,只写一次。
case 1: // 穿透性,没遇到break,往下执行。
case 2:
System.out.println(“ccc”);
break;
1.3.3 循环语句
1、for循环:for(表达式1;表达式2;表达式3){循环体;}
执行顺序:执行表达式1,判断表达式2,t就执行循环体,执行表达式3,判断表达式2...开始循环。
常用格式:for(初始化;条件;增量){循环体}
注:
continue:结束本次循环,执行下一次(跳出循环体,执行第三个表达式)
break:结束整个循环
注:
1)双重for和嵌套for,能不用就少用 ,复杂度过高
2)break 在多重循环中,只能先跳出最近的for结构。
3)如何跳出最外层for,利用了label 标签 outer:
outer:for(...){
for(...){
break outer;
}
} //使用不多
4)foreach 用的很多,与迭代器一起说。
2、while循环:while(条件){循环体}
执行顺序:条件真,执行循环体;假,退出循环。
常用结构:
while(n!=0){...}
while(true){...}
注:
1)如果用 do-while 循环体, 循环体最少都要执行一次。
2)无限循环(死循环):
while(true){}
for(;true;){}
1.4 数组
1.4.1 基本概念
数组:存数据用的“容器”,一堆相同类型数据的集合
定义:数据类型[] 变量名=new 数据类型[存储大小];
数组编号:index 索引 ,也称下标
访问数组元素:数组名[索引]
例如:
int[] scores = {99,67,81} ;
System.out.println (score[0]); // 得到元素99
数组在内存中原理:
初始化时,scores 指向了一个连续堆内存空间, scores 含有这个数组的地址,我们称为“scores 引用了这个数组”。所以数组是引用数据类型不是值数据类型。而且数据开辟的是堆内存,所以需要 jvm 垃圾回收机制,来收回空间。在强调一下,数组是开辟了一块连续内存,在 java 中是属于引用数据类型,这点可能有别于其他语言 , 如 golang。
数组属性:数组的长度 = 数组名.length
数组遍历:索引方式取出所有元素,for 循环。
例如:
for(int i = 0 ; i <= scores.length ; i++){
System.out.println(scores[i]);
}
数组很重要的几个点:
1、数组是堆内存中一块连续的内存空间。
2、因为其连续性,对数组元素的查找速度是O(1)。
3、声明变量是个引用,指向首地址。
1.4.2 数组常见异常
1.索引越界异常
2.空指针异常:不合适地把数组名存放的地址释放
虽然 java 中声称没有指针,其实在数组处理中是有指针思想的。通过函数的形参指向实参的同一个数组,所以对数组操作的方法不需要返回值,直接操作原数组。
1.4.3 数组的排序
排序三种基本方法:
1)选择排序:
从第一个元素开始,每个元素与后面的元素比较,选出最小的方法在前面。
public static void sort(int[] a) {
/*选择排序*/
int i,j,t;
for ( i = 0; i < a.length; i++) {
for ( j = i + 1; j < a.length; j++) {
if (a[j]
2)冒泡排序:
相邻两个元素比较,最大的放在末位,每次循环少比较一个元素。
public static void sort2(int[] a) {
/*冒泡排序*/
int i,j,t;
for ( i = 0; i < a.length-1; i++) {
for ( j = 0; j < a.length-1-i; j++) {
if (a[j] > a[j+1]) {
t = a[j];
a[j] = a[j+1];
a[j+1] = t;
}
}
}
}
3)插入排序:
原理是在一个排好序的数列中插入一个元素。被插入的元素从后往前,逐一与已排好序的最末位元素比较。
public static void sort3(int[] a ) {
/*插入排序*/
int i,j,t;
for (i = 1; i < a.length; i++) {
t = a[i];
for ( j = i-1; j >= 0 && t < a[j]; j--) {
a[j+1] = a[j];
}
a[j+1] = t ;
}
}
注:
数组中提供的排序API: Arrays.sort(a);
其他排序方法:希尔排序,快速排序等
效率问题:冒泡排序性能比较差,自带排序算法效率最高,插入排序略优于冒泡
1.4.4 多维数组
二维数组:是由多个一维数组组成,每一个一维数组都是二维数组的元素。二维数组的长度就是其中一维数组的个数。
定义方式:
- 同一维数组
int[][] arr = new int[2][3];
-
int[][] = {{1,2},{42,56,8},{10,5,11}};
这个二维数组长度是3 。
二维数组的遍历:嵌套 for 循环
二维数组变量名依然存放地址 :遵循引用数据类型原则
注:杨辉三角
int[][] arr = new int[8][8];
for (int i = 0; i < arr.length; i++) {
arr[i][0] = 1;
if (i >= 2 ) {
for (int j = 1; j < arr[i].length; j++) {
arr[i][j] = arr[i-1][j] + arr[i-1][j-1];
}
}
if (i != 0) {
arr[i][i] = 1 ;
}
}
for (int i = 0; i < arr.length; i++) {
for (int j = 0 ; j <= i ; j++) {
System.out.print(arr[i][j] + " ");
}
System.out.println();
}
三维数组:(略)
同理,由多个二维数组组成,长度就是二维数组的个数。
例如:a[1][3][1] 第二个二维数组中的第四个一维数组的第二个元素。
1.4.5 其他
可以调用系统函数进行操作。
- 数组的复制:
int[] a = {1,2,3,4};
int[] b = new int[a.length];
System.arraycopy(a,0,b,0,a.length);
// 参数:源, 起始点, 目的, 拷贝起始点, 复制个数
- 数组的扩容:
a = Arrays.copyOf(a,2*a.length);//new了一个新内存空间
a 数组扩容两倍长度,复制给a。本质是 这个方法新开辟了新的内存空间。
数组的位置插入一个元素:
a[index] = 123;
String 就是多个 char 组成的一维数组
Scanner s = new Scanner(System.in); s.nextInt();
键盘读入
1.5 函数
1.5.1 综述
在 java 中, 函数也叫方法!
(在其他语言如 golang 中 函数和方法是有区别的!)
函数存在的意义:
重复利用一段代码 。 每次使用的时候替换相应变量(参数)即可,达到减少代码量重复使用的目的。
1.5.2 定义
1、 格式
修饰符 返回值类型 方法名(参数类型 参数名){
内容;return;
}
例如:public static int XXX(int a,double b){}
2、 调用:
方法名(传递参数);
注:
- 方法定义参数是形参,方法调用是实参
- 通常方法名要小写,在 main下面并列位置
- 函数遇到 return 就结束了,不会执行后面的代码
1.5.3 内存空间与参数传递
1、 简述 :内存分为堆内存和栈内存,堆内存通过垃圾回收器 gc 回收空间。栈内存,先进后出自动释放空间。即方法结束,内存自动清理。
注:jvm 内存空间划分:
- 程序计数器 : 线程私有,cpu 切换时候找到上次执行位置
- 本地方法栈 : c++调用本地方法
- 虚拟机栈:所有方法运行时进入地方(main方法 压栈运行)
- 虚拟机堆:储存 容器 和 对象,实际存放对象的内存空间。
- 方法区:存放类信息和常量池
2 、参数传递问题:
函数定义时,接受的传递参数叫形式参数。main方法调用函数的时候,传递的参数叫做实际参数。形式参数只在函数内部起作用,即使与实参叫了同样的名字也不会改变main中的实参(只是指的是 基本数据类型, 也就是值类型 )。这个问题产生的原因是,方法进栈内存中运行,而参数变量只是在栈内存中开辟了相应的内存空间,方法调用完成后出栈,方法内部的形参和其他参数空间随机被清理。
1.5.4 函数的递归
简言之,在函数内部调用自己。举一个阶乘运算例子:
public class Test{
public static void main(String[] args){
System.out.println(func(10));
}
public static long func(int n){
if (n==1)
return 1;
return func(n-1)*n;
}
}
递归的内存空间原理:利用还是栈的先进后出原理,先压栈存入10,次函数未结束未出栈,再执行函数存入9…直到return 1,得到返回值1,最里层1空间出栈,12,12*3…直到返回10!。
1.5.5 方法的重载
java 允许在一个类中定义多个同名的方法,称为方法的重载 。
要求:变量个数不同 ,或变量类型不同(用以区分),与返回值类型无关。
重载只看方法名和参数列表