JAVA语言入门
1. 语言简介
计算机语言的分类:
- 机器语言
- 汇编语言
- 高级语言
- 第一代:C语言,面向过程为编程思想,它是唯一一门可以直接操作计算机硬件的语言。
- 第二代:C++,面向对象为编程思想,没有默认的垃圾回收机制(GC)。
- 第三代:Java,面向对象为编程思想,有默认的GC。
- 第四代:自然语言,面向问题为编程思想。
2. Java语言简介
为什么要学习Java语言?
-
Java是使用最广泛,且用法简单的语言。
-
Java是一门强类型的语言(对数据类型的划分很精细)。
-
Java有非常严谨的异常处理机制。
-
Java提供了对于大数据的基础性支持。hadoop的底层使用java编写。
1995年由SUN公司推出,Java之父:詹姆斯·高斯林,SUN公司在2009年被Oracle收购。
2.1 平台版本
- J2SE:标准版,也是其他两个版本的基础。在JDK1.5时正式更名为JavaSE。
- J2ME:小型版,一般用来开发嵌入式程序,已经被andorid替代。在JDK1.5时正式更名为JavaME。
- J2EE:企业版,一般开发企业级互联网程序。在JDK1.5时正式更名为JavaEE。
2.2 特点
开跨两多面:开源跨平台,多线程多态面向对象。
- 开源
- 跨平台
- 用Java编写的程序可以在不同的操作系统上运行。
- 原理:有JVM保证Java程序的跨平台性,但是JVM本身不跨平台。
3. JDK和JRE
3.1 概述
- JDK:Java开发工具包(Java Development Kit),包含开发工具和JRE。
- JRE:Java运行时环境(Java Runtime Environment),包含运行Java程序所需的核心类库和JVM。
- 核心类库:java.lang, java util, java.io
- JVM:Java虚拟机(Java Virtual Machine)
- 作用:用来保证Java程序跨平台,但是JVM本身不能跨平台
3.2 Java环境搭建
Library->Java->JavaVirtualMachines->jdk1.8.0_301.jdk
- 目录解释
- bin:存放编译器和工具
- db:存放数据
- include:编译本地方法
- jre:java运行时文件
- lib:类库文件
- src.zip:源代码
4. Path环境的配置
目的:在任何路径下都可以使用JDK提供的工具。例如java,javac。
vim ~/.bash_profile
添加变量名,变量值。
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Homeexport CLASSPAHT=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jarexport PATH=$JAVA_HOME/bin:$PATH
验证:
在terminal中输入:java -version
5. Hello World案例
程序的开发:编写->编译->执行
- 编写源代码:在后缀为.java的文件中编写。
- 编译:把我们能看懂的文件编译成计算机能看懂的文件。
- .java -> .class(字节码文件,给计算机看的)
- 运行:计算机运行字节码文件,用过java指令实现。
javac HelloWorld.java
会产生一个HelloWorld.class文件
java HelloWorld #由于设计规范,此处无需后缀,实际执行.class文件
显示运行结果
6. IDEA
6.1 IDEA中模块和项目之间的关系
方式:新建一个JavaSE项目,每天新增一个模块(推荐)
6.2 HelloWorld案例
创建一个空的项目
在project选项中设置SDK和Project Language Level
在该项目下创建Day01模块 Modules->Java->Moduel SDK设置
在Day01模块的src(源代码包)下创建:自定义包 在自定义包下创建Java类
在Java类中写代码
运行
6.3 快捷键
7. 关键字
关键字:被java赋予了特殊含义的单词。
- 特点
- 关键字有纯英文小写字母组成
- 常用开发工具会highlight关键字
7.1 常用关键字
- public:公共的权限
- class:表示创建一个类
- static:静态
- void:表示方法的返回值类型
7.2 定义java源代码文件的格式
public class 类名{public static void main(String[] args){System.out.println(这里的内容随便写);}}
8. 常量
概述
在程序的执行中,其值不能发生改变的量。
分类
- 自定义常量
- 字面值常量
- 整数常量 直接写
- 小数常量 直接写
- 字符常量 值要用’ '包裹
- 字符串常量 值要用“ ”包裹
- 布尔常量 值只有true和false
- 空常量 值只有null
9. 变量
9.1 概述
在程序的执行中,其值可以在某个范围内发生改变的量。从本质上讲,变量其实是内存中的一小块区域,例如:
- 商品的价格(1元,5元,100元)
- 钟表的时间(5点,6点,10点半)
- 人的年龄(10岁,20岁,50岁)
- 程序员生涯的职位(开发工程师,开发经理,项目经理,CTO)
java中要求每个变量每次只能保存一个数据,而且必须要明确保存数据的数据类型。
9.2 变量的限制
数据类型 变量名 = 初始化值;
9.3 数据类型
java是一门强类型语言。其对数据的划分很精细。
整型
| byte | 1个字节 | -128~127 |
| short | 2个字节 | |
| int | 4个字节 | |
| long | 8个字节 | |
| 浮点型 | | |
| float | 4个字节 | |
| double | 8个字节 | |
| 字符型 | | |
| char | 2个字节 | |
| 布尔型 | | |
| boolean | 1个字节 | |
byte类型的取值范围是:-128~127,char类型的取值范围是0~65535默认的整型是int,默认的浮点是double定义long型时,数据后面要加字母L定义float型时,数据后面要加字幕F 方式一:直接初始化变量
int a = 10;
方式二:先声明,后赋值
int a;a = 10;
9.4 注意事项
变量未赋值不能使用。变量只在它所属的范围内有效(代码块内)。一行可以定义多个变量,但是不建议。 10. 标识符
10.1 概述
标识符就是用来给类,接口,方法,变量,包等起名字的规则。
10.2 命名规则
标识符只能包含52个英文字母(区分大小写)、数字、$和_。标识符不能以数字开头。标识符不能和关键字重名。最好做到见名知其意。 10.3 命名规范
类,接口:每个单词的首字母大写,其他小写(大驼峰命名法)。变量,方法:从第二个单词开始,每个单词的首字母都大写,其他小写(小驼峰命名法)。常量:所有字母都大写,单词之间用下划线连接。包:所有字母全小写,多级包之间用.隔开,一般是把公司的域名反写。 hust.edu.cn -> cn.edu.hust
11. 数据类型转换
11.1 自动类型转换
小类型转大类型,会自动提升为大类型,运算结果时大类型。
double a = 10;
int和double相加,为doublechar和int相加,为intboolean和int相加,报错 - 转换规则
- 范围小的类型向范围大的类型提升,byte, short, char运算时直接提升为int
- boolean类型不能参与数值转换
11.2 强制类型转换
手动将大类型转换成小类型,运算结果是小类型
short s = 1;s = (short)(s+1);System.out.println(s);//结果为2,short类型
12. 常量和变量相加
Java中对于常量,有常量优化机制。
13. 运算符
运算符:连接常量和变量的符号。
表达式:用运算符把常量或者变量连接起来符合java语法的式子。
不同运算符连接的表达式体现的是不同类型的表达式。
13.1 算数运算符
整数相除,结果还是整数。除非有浮点型参与。
加号运算符在字符串之间表示连接。
System.out.println("Hello" + 5 + 5)//Hello55System.out.println("Hello" + (5 + 5))//Hello10System.out.println(5 + 5 + "Hello" + 5 + 5)//10Hello55
13.2 自增和自减运算符
i++,先运算后自增
++i,先自增后运算
++和–都隐含了强制类型转换。
byte a = 1;a = a++;//包含了强制类型转换,等价于下行代码a = (byte)(a + 1);
13.3 赋值运算符
基本的赋值运算符:=
拓展的赋值运算符:+=, -=, *=, /=, %=
a += b; 等价于 a = a + b;
赋值运算符的左边不能为常量。
拓展的赋值运算符默认包含了强制转换。
byte a = 1;a += 1;//包含了强制类型转换,等价于下行代码a = byte(a + 1);
13.4 关系运算符
==, !=, >, <, <=, >=,返回true或者false
13.5 逻辑运算符
&, |, !,^异或
短路逻辑运算符
符号作用说明
| && | 短路与 | 作用和&相同,但是有短路效果,前面如果出现false,后面不执行 |
| || | 短路与 | 作用和|相同,但是有短路效果,前面如果出现true,后面不执行 |
13.6 三元运算符
(关系表达式)? 表达式1 : 表达式2;
首先执行关系表达式,如果true执行表达式1,如果false执行表达式2
14 键盘录入 Scanner类
14.1 使用步骤
导包 写在类上面,package下面
import java.util.Scanner;
创建Scanner类的对象 Scanner sc = new Scanner(System.in);
通过Scanner类的nextInt()方法接收用户录入的数据 int a = sc.nextlnt();
15 流程控制
15.1 概述
某些代码在满足特定条件下才会执行;有些代码在满足特定条件下重复执行。这些都需要用到流程控制语句。
15.2 分类
- 顺序结构
- 选择结构(if语句,switch.case语句)
- 循环结构(for循环,while循环,do.while循环)
15.3 顺序结构
代码按照从上往下从左往右的顺序依次逐行执行。是java程序的默认结构。
System.out.println(10+10+"Hello"+10+10);
20Hello1010
15.4 选择结构
15.4.1 if语句
if语句一般用为范围的判断
if(条件语句){//语句体;}
- if.else语句(双分支)
一般用来判断两种情况
if(条件语句){//语句体1;}else{//语句体2;}
if(条件语句1){//语句体1}else if(条件语句2){//语句体2}else if(条件语句3){//语句体3}···else{//语句体}
15.4.2 switch语句
一般用于固定值判断
switch(表达式){//表达式的取值类型:byte, short, int, char, JDK5以后可以是枚举,JDK7以后可以是Stringcase 值1: //case后面跟的是要和表达式进行比较的值语句体1; //语句体可以使一条或者多条语句break; //break表示中断,结束switch语句case 值2:语句体2;break;case 值3:语句体3;break;···default: //default表示所有情况都不匹配时执行,写在任何位置都是最后才执行语句体n;break;}
case穿透:
在switch语句中,如果case后面不写break,将出现case穿透现象,也就是不会在判断下一个case的值,直接向后运行,知道遇到break,或者整体switch结束。
switch(month){case 1:case 2:case 12:String season = "Winter";break;···}
思考题:break关键字可以省略吗?
答:最后一个分支的break可以省略,其他分支如果省略break可能对结果有影响。
15.5 循环结构
- for循环
一般适用于循环次数固定的情况。 - while循环
一般适用于循环次数不固定的情况。 - do.while循环
实际开发中基本不用,适用于先执行一次然后判断的情况。
15.5.1 for循环
for(初始化条件;判断条件;控制条件){//循环体}
先执行初始化条件,然后执行判断条件,查看结果true还是false,如果true执行循环体,执行控制条件。
15.5.2 while循环
初始化条件;while(判断条件){//循环体;//控制条件;}
15.5.3 do.while循环
初始化条件;do{循环体;控制条件;}while(判断条件);
15.6 三种循环区别
for和其他两个循环的区别
for循环结束后,初始化条件就不能继续使用了。而while和dowhile可以。do.while和其他两个循环的区别
先执行一次再判断,而其他两个循环是先判断再执行。 15.7 循环跳转
- break:用来终止循环,循环不再继续执行。
- continue:用来结束本次循环,进行下一次循环,循环还会继续。
15.8 循环嵌套
用得最多的是for循环的嵌套
for(初始条件;判断条件;控制条件){//外循环for(初始条件;判断条件;控制条件){//内循环//循环体}}
16 Math生成随机数
Math类似Scanner,是Java提供好的API(Application Programming Interface),内部提供了产生随机数的工程。
16.1 格式
int num = (int)(Math.random()*100 + 1) //获取1到100之间的随机数
可以直接使用,不需要调包。取值范围为[0.0, 1.0) 17 数组
如果需要同时存储多个同类型的数据,我们可以通过数组来实现。
数组就是用来存储多个同类型元素的容器。
17.1 格式
//格式一数据类型[] 数组名 = new 数据类型[长度]; //数组名小驼峰命名法,推荐使用//格式二数据类型 数组名[] = new 数据类型[长度];//上述两种定义方式只是写法不同,并无其他区别。格式一应用的多一些。
//格式一数据类型[] 数组名 = new 数据类型[]{元素1, 元素2, 元素3, ...};//格式二 语法糖(简化语法的格式)数据类型[] 数组名 = {元素1, 元素2, 元素3, ...};
初始化数组不可同时给定长度和初值。
int[] arr = new int[3];
解释:
- 数据类型:数组中存储元素的数据类型。如这里的int说明数组中只能存储int类型的数据。
- []:表示是数组
- 数组名:类似于变量名,要符合小驼峰
- new:指创建数组对象
17.2 数组特点及基本用法
17.2.1 特点
数组中的元素编号(索引)从0开始。数组中每个元素都有默认值。 类型元素默认值
| int | 0 |
| double | 0.0 |
| boolean | false |
| String | null |
17.2.2 基本用法
通过数组名[索引]的形式,可以快速获取数组中的指定元素。通过数组名[索引]=值;的形式,可以修改数组中的指定元素值。通过数组名.length的方式可以获取数组的长度。 17.3 数组的内存图
17.3.1 内存解释
内存是计算机中的重要元件,也是临时存储区域,作用是运行程序。我们编写的程序是存放在硬盘当中的,在硬盘中的程序是不会运行的,必须放入内存中才能运行,运行完毕后会清空内存。
即:Java虚拟机要运行程序,必须要对内存进行空间的分配和管理。为例提高运算效率,就对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。
JVM的内存划分
栈:存储局部变量以及所有代码执行的。
局部变量:定义在方法中,或者方法声明上的变量。
特点:先进后出堆:存储所有new出来的内容,即对象
特点:堆中的内容会在不确定的时间被GC回收。方法区:存储字节码文件。
字节码文件:后缀为.class的文件。本地方法区:和系统相关,了解即可。寄存器:和CPU相关,了解即可。
内存图解,示意图(并不严格) public class ArrayDemo{public static void main (String[] args){int[] arr = new int[3];System.out.println(arr[0]);System.out.println(arr);//打印的是存储地址}}
17.3.2 两个数组的内存图
public class StoragePic {public static void main(String[] args) {int[] arr = new int[3];arr[0] = 11;System.out.println(arr[0]);System.out.println(arr[1]);System.out.println(arr);int[] arr2 = {1, 2};arr2[1] = 22;System.out.println(arr2[1]);System.out.println(arr2);}}
17.4 数组的两个小问题
17.4.1 数组索引越界异常
产生原因:访问了数组中不存在的索引
解决方案:访问数组中存在的索引即可
17.4.2 空指针异常
产生原因:访问了空对象
解决方案:对对象赋具体的值
17.5 遍历数组
int[] arr = {11, 22, 33, 44, 55};for(int i : arr) System.out.println(i);
17.6 获取数组的最值
public class ArrayDemo02 {public static void main(String[] args) {int[] arr = {55, 22, 66,77, 33, 44, 55};int max = arr[0];for(int i : arr){if (i >= max) max = i;}System.out.println(max);}}
17.7 反转数组
public class ReverseArray {public static void main(String[] args) {int[] arr = {11, 22, 33, 44, 55};int temp;for (int i = 0; i <= arr.length/2; i++){temp = arr[i];arr[i] = arr[arr.length-1-i];arr[arr.length-1-i] = temp;}for(int i : arr) System.out.println(i);}}
18 方法
方法也叫函数,是将具有独立功能的代码块组织成为一个整体,使其具有特殊功能的代码集。
18.1 定义格式
修饰符 返回值的数据类型 方法名(参数类型 参数名1, 参数类型 参数名2){//这里可以写多个参数//方法体;return 具体的返回值;}
- 修饰符:目前记住这里是固定格式public static
- 返回值的数据类型:用于限定返回值数据类型,如果没有具体的返回值,数据类型用void修饰
- 方法名:方便调用方法
- 参数类型:限定调用方法时传入数据的数据类型
- 参数名:用于接收调用方法时传入的数据的变量
- 方法体:完成特定功能的代码
- return 返回值:用来结束方法并把返回值返回给调用者。如果没有明确的返回着,return关键字可以不写
18.2 注意事项
- 方法与方法之间时平级关系,不能嵌套定义
- 方法必须先创建才可以使用
- 方法自身不运行,需要调用
- 方法的功能越单一越好
- 定义方法的时候写在参数列表中的参数都是形参
- 调用方法的时候,传入的具体的值,是实参
18.3 无参无返回值的方法
public static void 方法名(){//方法体;}
图解
18.4 有参无返回值的方法
public static void printNum(int number){System.out.println("The number is "+ number);}
18.5 无参有返回值的方法
public static String getSchool(){return "Huazhong University of Science and Technology";}
18.6 有参有返回值的方法
public static int getAdd(int a, int b){return a+b;}
18.7 方法重载
同一个类中,出现方法名相同,但是参数列表不同的两个或多个的方法时,称为方法重载,方法重载与方法的修饰符、返回值的数据类型无关。
参数列表不同分为两种类型:
18.8 基本类型、引用类型作为形参的情况
形参如果是基本类型,形参的改变不影响实参。
public class demo01 {public static void main(String[] args) {int num = 100;change(num);System.out.println(num);//打印结果仍然是100}public static void change(int num){num = 200;}}
List集合特有的成员方法
void add(int i, E element)//使index为i的位置插入elementE set(int i, E element) //修改指定索引处的元素指定的值,并返回修改前的元素E remove(int i)//根据索引移除指定元素,并返回此元素E get(int i)//根据索引,获得相应的元素
遍历List集合:
- 增强for
- 通过for循环+size()+get()的形式,遍历List集合
快捷键:itli
列表迭代器 ListIterator
列表迭代器指的是ListIterator接口,它是List集合特有的迭代器
该迭代器继承了Iterator迭代器,所以我们可以直接使用
ListIterator<E> listIterator()ListIterator<E> listIterator(int index)
成员方法:
boolean hasPrevious();E previous();int nextIndex();int previousIndex(); List<String> list = new ArrayList<>();list.add("Hello");list.add("World");list.add("Java");//通过ListIterator正向遍历ListIterator<String> it = list.listIterator();while(it.hasNext()){String s = it.next();System.out.println(s);}//逆向遍历while(it.hasPrevious()){String s = it.previous();System.out.println(s);}
并发修改异常
当使用普通迭代器(Iterator)遍历集合的同时,又往集合中添加了元素,就会报并发修改异常。
List<String> list = new ArrayList<>();list.add("Hello");list.add("World");list.add("Java");Iterator<String> it = list.iterator();while(it.hasNext()){String s = it.next();if("World".equals(s)){it.add("JavaSE");//会报错并发修改异常}}
产生原因:
迭代器是依赖于集合而存在,当判断成功后,集合中添加了新的元素,而迭代器不知道,所以报错了。(计数器会计算剩下的元素个数,所以add报错而remove不报错)本质是,迭代器遍历集合中的元素是,不能使用集合对象去修改集合中的元素。
解决方案:
通过列表迭代器(ListIterator)解决 List<String> list = new ArrayList<>();list.add("Hello");list.add("World");list.add("Java");ListIterator<String> it = list.listIterator();while(it.hasNext()){String s = it.next();if("World".equals(s)){it.add("JavaSE");}}
通过for循环+size()方法解决 List<String> list = new ArrayList<>();list.add("Hello");list.add("World");list.add("Java");for(int i=0; i<list.size(); i++){if("World".equals(list.get(i))){list.add(i+1, "JavaSE");}}for (int i = 0; i < list.size(); i++) {String s = list.get(i);System.out.println(s);}
通过CopyOnWriteArrayList集合解决(它的底层已经解决了这个问题) CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();list.add("Hello");list.add("World");list.add("Java");Iterator<String> it = list.iterator();//普通迭代器while(it.hasNext()){if("World".equals(it.next())){list.add("JavaSE");//并发}}
面试题
什么是并发修改异常,怎么产生的,怎么解决
ConcurrentModificationException
增强for
增强for是JDK1.5的新特性,它是用来简化数组和Collection集合的遍历。
for(元素的数据类型 变量名:数组或Collection集合中的每一个元素)for(int a: array)for(Student l:list)
注意事项:数组或Collection集合不能为null;增强for的底层是迭代器
for(String elem : list){ }
常见的数据结构
数组:查询快,增删慢
链表:查询慢,增删快
List集合的子类(ArrayList和LinkedList)
List集合是一个接口,它的常用子类有两个ArrayList和LinkedList。
ArrayList的底层数据结构是数组,查询快,增删慢
LinkedList的底层数据结构是链表,查询慢,增删快
相同点:都是有序可重复的
LinkedList特有方法
void addFirst(E element)void addLast(E element)往链表的开头或末尾插入制定元素E removeFirst()E removeLast()删除链表的开头或末尾元素,并返回这个元素E getFirst()E getLast()返回链表的开头或末尾元素
Set集合
Set集合是Collection集合的子体系,它的元素特点是无序、唯一
Set集合是一个接口,所以不能通过new的方式直接创建对象Set集合中没有带索引的方法,不能通过普通for进行遍历Set集合的常用子类有两个HashSet集合和TreeSet集合。 Set<String> set = new HashSet<>();//实际使用不用Set<String>,直接用HashSet<String>set.add("Hello");set.add("Java");set.add("Hello");for(String s : set){System.out.println(s);}
Java
Hello
HashSet集合
底层数据结构是哈希表对集合的迭代顺序不做保证没有带索引的方法不包含重复的元素
总结:无序,唯一,元素无索引。底层数据结构是哈希表 HashSet<String> set = new HashSet<>();set.add("Hello");set.add("Java");set.add("World");for(String s : set){//遍历法1:增强for循环System.out.println(s);}Iterator<String> it = set.iterator();while(it.hasNext()){//遍历法2:普通迭代器System.out.println(it.next());}
LinkedHashSet集合
底层数据结构是哈希表+链表保证元素有序保证元素唯一
总结:有序,唯一。 可变参数
可变参数又称参数个数可变,它用作方法的形参出现,那么方法参数个数就是可变的了。
格式
修饰符 返回值类型 方法名(数据类型... 变量名){}
可变参数的底层是一个数组如果一个方法有多个参数,将可变参数放在形参列表的最后。 public static void main(String[] args) {System.out.println(getSum(1,2,3));}public static int getSum(int... nums){return Arrays.stream(nums).sum();//可变参数的底层是数组}
Map集合
public interface Map<K,V>{}
特点:
键具有唯一性,值可以重复双列集合的数据结构只针对于键有效Set集合底层依赖的是Map集合
成员方法: public V put (K key, V value)