文章目录
前言
在假期期间把自己以前学过的内容简单总结一下,肯定有不足或者不正确的地方,如果正好有被大佬看见的话欢迎指正,多谢。本文我有参考一位大佬的总结,有想学习Java的同学觉得我写的不好的话可以留下建议,然后去看那位大佬的总结,链接我放在下面。
以下是本篇文章正文内容。
1、Java简介
1.1、Java的三个版本
- JavaSE(标准版) Java的核心和基础
- JavaME(移动版) 移动端
- JavaEE(企业版) 企业开发
1.2、Java的特点
- 简单性
- 面向对象
- 分布性
- 编译和解释性
- 稳健性
- 安全性
- 可移植性
- 高性能
- 多线程性
- 动态性
2、Java的安装和配置
2.1、安装jdk
我们一般使用jdk8,下载Windows x 64 版本安装即可。
jdk:开发者工具包。
jre:运行环境。
jdk目录结构
目录 | 目录说明 |
---|---|
bin | 存放开发Java程序的工具,例如:编译工具(javac.exe)、运行工具 (java.exe) 、打包工具 (jar.exe)等。 |
db | 纯Java编写的数据库Derby。 |
include | C语言头文件,用于支持Java程序设计。 |
jre | Java运行环境根目录。 |
jre\bin | 包含Java平台所使用工具和类库的可执行文件和DLL文件。 |
jre\lib | Java运行时使用的核心类库。 |
lib | 开发Java程序使用的类库文件。 |
src.zip | Java的源代码包。 |
2.2、配置环境变量
安装完jdk后想要让操作系统可以找到Java并调用bin目录下的开发工具就需要配置环境变量。
- 选择系统变量,新建变量名为JAVA_HOME,值为JDK的安装目录。
- 选择系统变量里的Path,给Path加入jdk下的bin目录。
- 测试Java是否安装成功:在命令行界面输入 java -version 查看版本号。
2.3、Java程序编译过程
- 源文件(xxx.java)通过 javac命令 编译为字节码(xxx.class 文件)
- 字节码再通过 java命令 交给JVM即Java虚拟机运行
- JVM把字节码翻译为机器码,最后由操作系统执行
ps:Java程序编译过程很复杂,有想要深入了解的同学可以自行查找资料。
2.4、Java开发工具
- Eclipse
- IDEA
3、基础语法
3.1、注释
-
单行注释
//单行注释 -
多行注释
/*
多行注释
*/ -
文档注释
/**
文档注释
*/
3.2、标识符
标识符概念
在计算机编程语言中,标识符是用户编程时使用的名字,用于给变量、常量、函数、语句块等命名,以建立起名称与使用之间的关系。标识符通常由字母和数字以及其它字符构成。
标识符的命名规则
- 标识符由字母、数字、下划线“_”、汉字、美元符号“$”组成,第一个字符不能是数字。
- 不能把java关键字和保留字作为标识符。
- 标识符没有长度限制。
- 标识符对大小写敏感。
以下是Java的关键字和保留字
Java关键字含义
标识符的命名规范
- 见名知义,需要由有意义的英文单词。
- 变量名和方法名,驼峰命名,由一个或多个单词组成,第一个单词的开头小写后面的单词开头大写,如:goodsPrice、person。
- 类名,类似驼峰命名,每个单词的开头都是大写,如:HelloWorld、MyFirstHomework。
- 包名:全部单词小写中间由.连接,域名.公司名.项目名.模块名,如:com.sun.oa.login。
- 常量名,全部单词大写,中间由下划线分隔,如:MAX_VALUE。
3.3、数据类型
基本数据类型
一共分4类,共8种
- 整数:byte、short、int(默认值)、long
- 浮点数:float、double(默认值)
- 字符:char
- 布尔值:boolean
数据类型 | 字节长度 | 取值范围 |
---|---|---|
byte | 1字节 | -128 ~ 127 |
short | 2字节 | -2^15 ~ 2^15-1 |
int | 4字节 | -2^31 ~ 2^31-1 |
long | 8字节 | -2^63 ~ 2^63-1 |
float | 4字节 | -2^128 ~ 2^128 |
double | 8字节 | -2^1024 ~ 2^1024 |
char | 2字节 | 0 ~ 2^16-1,ASCII代码,’A’对于65,‘a’对应97 |
boolean | 1位(1字节(byte)=8位(bit)) | true/false |
注意:
- long类型变量赋值时,数字后面要加L或l,推荐使用L,用于区分1和l
long num = 100L; - float类型变量赋值时,浮点数后要添加f或F,否则默认是double类型,赋值出错
float num = 10.5f; - char的值用单引号括起来
char c = ‘A’;
引用数据类型
类、接口、数组
简单讲,所有非基本类型都是引用数据类型。
3.4、类型转换
自动类型转换
低转高,编译器自动完成
强制类型转换
高转低,需要手动完成
数据类型 变量名 = (数据类型)值;
long a = 10;
int b = (int) a;
注意:
- 强制转换可能会丢失精度
- 类型转换都会影响程序的性能
3.5、变量和常量
变量
变量的三要素:
- 数据类型
- 变量名
- 初始值
定义变量的方法
- 定义变量,同时初始化
数据类型 变量名 = 初始值;
int hp = 100; - 先定义变量,然后再初始化
数据类型 变量名;
变量名 = 值;
int hp;
hp = 100; - 同时定义多个类型相同的变量
数据类型 变量名1 = 值,变量名2 = 值;
int hp = 100,mp = 300;
变量的作用域
- 类变量:也叫静态变量,即变量前加了static的变量。
- 实例变量:也叫对象变量,即没加static的变量。
- 局部变量:定义在方法体内的变量。
常量
final关键字;
全部单词大写,中间由下划线分隔,如:MAX_VALUE。
3.6、运算符
算术运算符 | +,-,*,/,%,++,- - |
赋值运算符 | =,+=,-=,*=,/=,%= |
关系运算符 | >,<,>=,<=,==,!= |
逻辑运算符 | &&,//,! |
位运算符 | &按位与,/按位或,^按位异或,~取反,<<左移,>>右移 |
条件运算符 | ?: |
注意:
1、a++与++a不同之处,a–与--a同理
- a++先进行其他运算再自增
- ++a先自增再进行其他运算
2、==和=的区别
- ==是比较两个值是否相等,=是进行赋值
3、与或非优先级
- ! > && > ||
4、断路功能
- 表达式1 && 表达式2
表达式1为false,表达式2不会执行 - 表达式1 || 表达式2
表达式1为true,表达式2不会执行
5、条件运算符
- 数据类型 变量名 = 条件 ? 值1 : 值2;
可以用于条件判断,条件为true,返回值1,条件为false,返回值2。
运算符优先级
优先级由高到低:
- () 小括号
- ++、–、! 一元运算符
- *、/、% 乘除取余算术运算符
- +、- 加减
- <、>、>=、<=、==、!= 关系运算符
- && 逻辑运算符 与
- || 或
- ?: 条件运算符
- =,+=,-=,*=,/=,%= 赋值运算符
3.7、包机制
- 域名倒写:com.baidu.xxx
- 防止命名冲突
- package:如果有必须写在代码的第一行
- import
3.8、javadoc
用于生成帮助文档
- @author
- @version
- @pram
- @since
- @return
- @thorws
4、流程控制
4.1、scanner
从键盘输入值给变量赋值,需要用到Scanner类(扫描器)
import java.util.Scanner;
//scanner类
public class demo03 {
public static void main(String[] args) {
//创建scanner类
Scanner input = new Scanner(System.in);
//输入
String a = input.next();
int b = input.nextInt();
//输出
System.out.println("输入的字符串是:"+a);
System.out.println("输入的整数是:"+b);
}
}
4.2、顺序结构
顺序结构是最简单的程序结构,也是最常用的程序结构,只要按照解决问题的顺序写出相应的语句就行,它的执行顺序是自上而下,依次执行。
4.3、选择结构
if语句
if(条件){
语句;
}
if-else语句
if(条件){
语句1;
}else{
语句2;
}
多重if语句
if(条件1){
语句1;
}else if(条件2){
语句2;
}else{
缺省语句;
}
嵌套if语句
if(外部条件){
if(内部条件){
语句;
}else{
语句;
}
}else{
语句;
}
switch结构
switch(表达式或变量){
case 值1:
语句1;
break;
case 值2:
语句2;
break;
....
default:
缺省语句;
}
执行流程:
- 首先用switch后面的表达式的值和case后面的值进行比较,若和值1相等则执行语句1,不相等就与case2值进行比较,若相等则执行语句2。
- 以此类推,如果到default之前没有相等的情况则执行default中的语句结束。
注意事项:
- 本身可以跟的类型:int,byte,short,char
JDK5以后可以跟枚举类型
JDK7以后可以跟String类型 - case后面只能跟常量,不能跟变量且不能重复。
- default可以在switch语句的任何位置,也可以省略不写。
- 切记在case语句中缺少break会出现case穿透现象。
- switch语句遇见break结束,或者程序默认执行到末尾结束。
switch与多重if的比较:
- switch的运行流程和多重if一样,switch语法更加简洁。
- switch只能用于判断单个值的,多重if可以判断一个范围。
case穿透现象:
//case穿透
public class demo04 {
public static void main(String[] args) {
int i = 1;
switch (i){
case 1:
System.out.println(1);
case 2:
System.out.println(2);
break;
case 3:
System.out.println(3);
break;
case 4:
System.out.println(4);
break;
default:
System.out.println("default");
break;
case 5:
System.out.println(5);
break;
}
}
}
输出结果:执行了case1后由于没有break语句导致继续执行下一条case2语句,然后遇到case2中的break语句后程序结束输出1和2,正常情况应该是只输出1。
4.4、循环结构
循环三要素
- 起始量,循环变量的初始化
- 停止循环的条件
- 循环变量的更新
for循环
一般用于固定次数的循环
for(循环变量的初始化;循环的条件;循环变量的更新){
循环的执行语句;
}
执行流程:
- 初始化
- 判断循环条件,如果成立
- 执行循环语句
- 循环变量的更新
- 判断循环条件,如果成立
…
增强for循环
for each 的用法:
for(元素类型type 元素变量x : 遍历对象obj){
引用了x的java语句;
}
总结:
- for each是for循环在特殊情况下的增强版本。
- 简化了编程,提高了代码的可读性和安全性(不用怕数组越界)。
- 遍历数组和集合的时候建议使用for each。
- 需要引用数组和集合的索引时for each无法做到。
while循环
循环变量的初始化
while(循环条件){
循环的执行语句
循环变量的更新
}
while循环执行的流程和for一样
do-while循环
变量的初始化;
do{
循环执行的语句;
变量的更新;
}while(循环条件);
do-while和for、while的区别:
- for、while是先进行条件判断,再执行循环语句,如果条件不成立,一次都不执行。
- do-while是先执行循环语句,再进行条件判断,如果条件不成立,至少会执行一次。
break关键字
break作用:在循环中间停止整个循环的执行。
//break关键字
public class demo05 {
public static void main(String[] args) {
//使用循环模拟上365天班
for(int i = 1;i <= 365;i++){
System.out.println("上了"+i+"天班");
//在第100天中奖,辞职不上班了
if(i == 100){
//停止整个循环的执行
break;
}
}
}
}
continue关键字
continue作用:在循环中间跳过某一次循环的执行,继续下一次循环。
//continue关键字
public class demo06 {
public static void main(String[] args) {
for(int i = 1;i <= 356;i++){
//在第100天,跳过当天的上班
if(i == 100){
System.out.println("今天发烧了,要请假!");
//跳过本次循环,继续
continue;
}
System.out.println("上第"+i+"天班");
}
}
}
嵌套循环
循环内部可以执行任何语句,包括另一个循环,这样就形成了嵌套循环。
如:
for(...){
for(...){
...
}
}
while(..){
while(..){
...
}
}
等,for、while、do-while也可以相互嵌套。
嵌套循环的执行流程:
外部循环执行一次,内部循环执行一轮。
九九乘法表:
//九九乘法表
public class demo07 {
public static void main(String[] args) {
for (int i = 1; i < 10; i++) {
for (int j = 1; j <= i; j++) {
System.out.print(j+"*"+i+"="+(j*i)+"\t");
}
System.out.println();
}
}
}
嵌套循环的break和continue
for(...){
for(...){
if(...){
break/continue;
}
}
}
内层循环中的break和continue只对内层循环起作用,对外层没有影响。
如果希望break和continue影响外层循环,可以使用label标签(label标签可以自定义)
label:
for(...){
for(...){
if(...){
break label;
}
}
}
以下是两个示例:
//break label
public class demo09 {
public static void main(String[] args) {
System.out.println("label test");
outer:
for (int i = 0; i < 3; i++) {
System.out.println("outer" + i);
inner:
for (int j = 0; j < 3; j++) {
System.out.println("inner" + j);
break outer;
}
}
}
}
//运行结果
//label test
//outer0
//inner0
//continue label
public class demo10 {
public static void main(String[] args) {
System.out.println("label test");
outer:
for (int i = 0; i < 3; i++) {
System.out.println("outer" + i);
inner:
for (int j = 0; j < 3; j++) {
System.out.println("inner" + j);
continue outer;
}
}
}
}
//运行结果
//label test
//outer0
//inner0
//outer1
//inner0
//outer2
//inner0
5、方法
5.1、方法的概述
什么是方法?
方法Method是一段代码块,当需要时可以被调用执行,在一些其他语言(C、JavaScript等)中又称为函数Function。
方法的好处
- 方法将代码进行封装,方便进行调用
- 代码可以被重用
- 代码方便进行维护
5.2、方法的定义
无参方法
public static 返回值类型 方法名(){
方法体;
}
注意:如果没有返回值,返回值类型就为void(空)
有参方法
public static 返回值类型 方法名(数据类型 参数名,数据类型 参数名..){
方法体;
}
注意:
- 参数可以没有,也可以有任意个,每个参数用逗号隔开。
- 形式参数由数据类型和参数名组成。
- 参数名不能重复。
- 定义方法时声明的参数叫形参(形式参数)。
- 调用方法时传入的参数叫实参(实际参数)。
- 一旦定义了形参,调用时必须传入对应类型和数量的实参。
5.3、方法的调用
类名.方法();
对象.方法();//对象就是平时我们new出来的东西
//调用有参方法需要传参
5.4、方法的重载
- 方法名相同
- 参数不同(类型,个数)
5.5、命令行传参
为了给main方法传递参数,进行JVM性能调优。
5.6、可变长参数
… 必须放在参数的末尾,否则会报错。
5.7、递归
自己调用自己,给自己一个出口
6、数组
6.1、数组的定义
创建数组时,同时定义数组的长度
类型[] 数组名 = new 类型[长度];
int[] array = new int[5];
[ ]也可以在数组名的后面
类型 数组名[] = new 类型[长度];
int array[] = new int[5];
也可以先声明数组,然后定义长度
类型[] 数组名;
数组名 = new 类型[长度];
注意:创建数组后,数据会有默认值:
int默认是0,float默认是0.0f,double默认是0.0,String默认是null。
6.2、数组的初始化
静态初始化
在定义数组的同时,给数组的数据赋值
数据类型[] 数组名 = {值1,值2,值3...};
如:int[] array = {20,44,33,100,55};
数据类型[] 数组名 = new int[]{值1,值2,值3...};
如:int[] array = new int[]{20,44,33,100,55};
注意:静态初始化就不能定义长度,数组的长度是由值的个数决定。
动态初始化
定义数组后,使用循环给数组赋值
int[] array = new int[5];
for(int i = 0;i < array.length;i++){
array[i] = i;
}
数组的访问
数组通过 数组名[下标] 访问数据,注意:
- 下标从0开始
- 如果下标不在0到数组长度-1范围内,就会出现ArrayIndexOutOfBoundsException 异常(数组下标越界)
增强型的for循环
foreach循环是在jdk1.5后支持的语法,用于遍历数组或集合。
for(元素类型type 元素变量x : 遍历对象obj){
引用了x的java语句;
}
6.3、二维数组
二维数组的定义
数据类型[][] 数组名 = new 数据类型[行数][列数];
int[][] array = new int[3][5];
二维数组的访问
数组名[行下标][列下标];
访问上面数组第二行第三个元素,array[1][2];
二维数组的静态初始化
数据类型[][] 数组名 = {{值,值,值...},{值,值,值...},{值,值,值...}..};
数据类型[][] 数组名 = new 数据类型[][]{{值,值,值...},{值,值,值...},{值,值,值...}..};
二维数组的动态初始化
int[][] array = new int[3][5];
6.4、Arrays工具类
- 排序
Arrays.sort(数组名) - 查找
int Arrays.binarySearch(数组名,要找的数)
获得的结果是要找数字的下标 - 复制
Arrays.copyOf(被复制的数组名,新的数组的长度)
获得的结果是新数组 - 填充
Arrays.fill(数组名,要填充的数);
给数组填充相同的值 - 比较
Arrays.equals(数组名1, 数组名2)
返回两个数组值是否相同 - 输出数组内容
Arrays.toString(数组名)
6.5、排序算法
冒泡排序
算法步骤:
- 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
- 针对所有的元素重复以上的步骤,除了最后一个。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
import java.util.Arrays;
//冒泡排序
public class BubbleSort {
public static void main(String[] args) {
int[] array = {25,15,30,50,45};
//i控制比较次数
for (int i = 0; i < array.length-1; i++) {
//j控制前后两个比较的数字
for (int j=0; j < array.length-i-1; j++){
if (array[j] > array[j+1]){
int tmp = array[j];
array[j] = array[j+1];
array[j+1]=tmp;
}
}
}
System.out.println(Arrays.toString(array));
}
}
选择排序
算法步骤:
- 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
- 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
- 重复第二步,直到所有元素均排序完毕。
import java.util.Arrays;
//选择排序
public class SelectionSort {
public static void main(String[] args) {
int[] array = {25,15,30,50,45};
//i控制比较次数
for (int i = 0; i < array.length-1; i++) {
int min = i;
//j控制前后两个比较的数字来寻找最小的数字
for (int j = i+1; j < array.length; j++) {
if (array[j] < array[min]) {
// 记录目前能找到的最小值元素的下标
min = j;
}
}
if(i != min){
int tmp = array[i];
array[i] = array[min];
array[min] = tmp;
}
}
System.out.println(Arrays.toString(array));
}
}
插入排序
算法步骤:
- 将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
- 从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)
//插入排序
public class InsertSort {
public static void main(String[] args) {
int[] array = {25,15,30,50,45};
// 从下标为1的元素开始选择合适的位置插入,因为下标为0的只有一个元素,默认是有序的
for (int i = 1; i < array.length; i++) {
// 记录要插入的数据
int tmp = array[i];
// 从已经排序的序列最右边的开始比较,找到比其小的数
int j = i;
while (j > 0 && tmp < array[j - 1]) {
array[j] = array[j - 1];
j--;
}
// 存在比其小的数,插入
if (j != i) {
array[j] = tmp;
}
}
System.out.println(Arrays.toString(array));
}
}
7、面向对象
7.1、类和对象
面向对象的概念
面向对象(Object Oriented)是一种编程模式,把业务相关的数据和方法组织为一个整体来看待,从更高的层次来进行系统建模,更贴近事物的自然运行模式。
面向过程和面向对象的区别是:
- 面向过程主要关注实现功能的步骤,关注实现细节。
- 面向对象主要关注对象以及对象之间的关系,不关注实现细节。
类和对象
- 类是对象的抽象,是模板
- 对象是类的具体
面向对象编程
面向对象编程(Object Oriented Programming),就需要借助类和对象来实现。
OOP 编程的过程是:
- 定义类
- 使用类创建对象
- 调用对象的属性或方法
定义类的语法是:
public class 类名{
属性
方法
}
构造方法
构造方法有什么作用?
- 创建对象并且分配内存
- 方便对对象的属性进行初始化
如何定义构造方法?
- 名称和类名相同
- 没有返回值类型
- 可以有参数也可以没有
public 类名(){
}
public 类名(类型 参数..){
}
默认的构造方法:
- 如果没有定义构造方法,编译器会自动为类添加一个无参的空的构造方法,类似: public 类名(){}
- 如果我们手动添加了构造方法,编译器会删除掉这个默认的构造方法。
- 一般情况下我们如果给类添加有参构造方法的同时,会手动添加一个无参的构造方法,方便无参的情况下创建对象。
this关键字
this代表当前的对象。
this关键字的作用是:
- 调用当前对象的属性,this.属性
- 调用当前对象的方法,this.方法名(…);
- 调用当前对象的构造方法,this(…)
构造方法里使用this的原因是参数名称和属性名称相同,为了区分它们,用this调用属性,没有this调用的就是参数。
this.name = name;
对象的内存分配
JVM是虚拟的计算机,把内存分为5大区域管理:
- 程序计数器
保存线程上下文的执行代码行数 - 本地方法区
保存native方法的相关信息 - 方法区
保存类信息、静态变量、常量等 - 虚拟机栈
保存局部变量、对象的引用(内存地址) - 堆
保存真实的对象
方法区中保存着类,
一旦使用new,就会在堆中分配内存,保存对象,
栈区保存的是堆区对象的内存地址。
7.2、封装
封装的特性
什么是封装?
封装这个词语,包含两层含义:
- 包装
将代码包装起来成为整体,方便调用和维护 - 封闭
信息的隐藏,将实现的细节隐藏起来,提高安全性
封装的好处
- 安全性高
- 使用方便
- 维护性高
封装的实现
- 包装代码
将代码包装到方法中,将属性和方法包装到类中,将类保存到包中… - 信息隐藏
隐藏某些不想被其他类调用的属性、方法和类
访问修饰符
访问修饰符是Java中的关键字,用于限制属性、方法、类的访问范围
访问修饰符有:
- public
- protected
- default
- private
访问权限 | 类 | 包 | 子类 | 其他类 |
---|---|---|---|---|
public | Y | Y | Y | Y |
protected | Y | Y | Y | N |
default | Y | Y | N | N |
private | Y | N | N | N |
属性的封装
Java类中一般将属性定义为私有的,然后提供公开的getter/setter方法对属性进行访问和修改
private double price;
//返回属性的值
public double getPrice(){
return this.price;
}
//修改属性的值
public void setPrice(double price){
this.price = price;
}
这么写的好处:
- 在set方法中可以控制值的范围
if(price < 0){
System.out.println("价格不能小于0");
this.price = 0;
}else{
this.price = price;
}
- 可以控制属性: 读写、只读、只写
包的封装
什么是包?
类似于文件夹,把有相关功能的类保存到一起。
作用:
- 方便进行查找
- 避免命名冲突
创建包(package代码必须是类中的第一句):
package 包名;
导入包中的类(在一个包中的类,不需要导入):
import java.util.Scanner; 导入一个类
import java.util.*; 导入包中的所有类
7.3、继承
继承概述
可以由子类继承父类的成员(属性和方法),起到代码重用的作用
特点:
- 程序中的类分为父类和子类
- 子类可以继承父类,也可以说父类派生子类
- 子类可以继承父类的属性和方法,是一种代码重用的机制
继承的语法
public class 子类 extends 父类{
}
Java中的继承的特性
- 传递性,父类的成员可以传递给子类,以及子类的子类
- 单根性,子类只能有一个父类
super关键字
super代表父类对象
作用有:
- 调用父类的属性
super.属性
- 调用父类的方法
super.方法(..)
- 调用父类的构造方法
super(参数);
super和this的区别
- this代表当前类的对象,super代表父类的对象
- this可以调用当前类的属性和方法(包括自身特有和父类的)
super只能调用父类的属性和方法 - this可以调用自身的构造方法,super可以调用父类的构造方法
super调用父类的构造方法:
给父类添加构造方法:
public Person() {
System.out.println("这是Person无参的构造方法");
}
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("这是Person带参的构造方法");
}
给子类添加构造方法:
public Student(){
System.out.println("这是Student无参的构造方法");
}
public Student(String no,String name,int age) {
//调用了父类的构造方法
super(name,age);
this.no = no;
System.out.println("这是Student带参的构造方法");
}
调用子类:
Student stu = new Student();
Student stu1 = new Student("001","张三",20);
//运行结果
//这是Person无参的构造方法
//这是Student无参的构造方法
//这是Person带参的构造方法
//这是Student带参的构造方法
注意:
- 子类如果不写super,系统会默认调用父类无参的构造方法
- 调用父类有参构造方法时,子类必须使用super显式的调用父类的构造方法
- super()必须出现在子类构造方法的第一行
- 构造方法的调用,总是先调用父类的,再调用子类的
方法重写
什么是方法重写?
子类中的方法可以覆盖父类中的方法。
方法重写的特点:
- 方法在子类和父类中
- 方法名相同
- 参数、返回值都相同
- 子类方法的访问修饰符不能比父类更严格
Object类
Object类是Java中一切类的父类
Object类的常用方法:
- equals 比较是否相等
- hashCode 返回哈希代码
- getClass 返回对象的类型信息
- toString 返回对象的字符串信息
= =和equals的区别:
= =对于基本类型,比较的是值;对于引用类型,比较的是内存地址。
Object类的equals比较的也是对象的地址,因为源码中是用= =实现的。
public boolean equals(Object obj) {
return (this == obj);
}
对于String类型来说,equals比较的是字符内容,因为String类重写了Object类的equals方法。
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
int x = 10;
int y = 10;
String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(x == y); // true
System.out.println(str1 == str2); // false
System.out.println(str1.equals(str2)); // true
final关键字
用于修饰类、变量、方法:
- 被final修饰的类,不能被继承
- 被final修饰的变量变成常量,不能修改值
- 被final修饰的方法,不能重写
抽象方法和抽象类
在实际开发过程中,有时候我们需要把某些方法的具体实现,推迟到子类中实现。
抽象方法的意义在于:
- 可以规范方法的定义(返回值类型、方法名、参数)
- 具体实现由子类完成
抽象方法:
public abstract 返回值 方法名(类型 参数);
抽象类:
abstract class 类名{
}
注意:
- 一旦类中定义了一个抽象方法,这个类必须定义成抽象类
- 有抽象方法的类,必须是抽象类;抽象类不一定有抽象方法
抽象类的特点:
- 抽象类不能实例化
- 抽象类必须由子类继承并实现其中的抽象方法,除非子类也是抽象的
7.4、多态
多态的概念
多态(Polymorphism)意思就是事物具有多种状态,如水随着温度变化会在液态、固态、气态三种状态之间转换,人到了不同的国家会转换为中国人、美国人、印度人等。
作用:多态提高了程序的灵活性和扩展性。
多态的实现
多态性基于封装和继承机制,必须满足以下条件:
- 子类继承了父类
- 子类重写了父类的方法
/**
* 人类
*/
abstract class Human {
public abstract void eat();
}
/**
* 中国人
*/
class Chinese extends Human{
public void eat(){
System.out.println("中国人拿筷子吃");
}
}
/**
* 美国人
*/
class American extends Human{
public void eat(){
System.out.println("美国人拿刀叉吃");
}
}
/**
* 印度人
*/
class Indian extends Human{
public void eat(){
System.out.println("印度人拿手吃");
}
}
多态有几种实现的方式:
- 将子类的对象赋值给父类的引用
对象调用父类的方法
public class HumanTest{
public static void main(String[] args) {
Human man1 = new Chinese();
man1.eat();
Human man2 = new American();
man2.eat();
Human man3 = new Indian();
man3.eat();
}
}
- 将方法的参数定义为父类类型,传入子类对象
public static void service(Human man){
man.eat();
}
public static void main(String[] args) {
service(new Chinese());
service(new American());
service(new Indian());
}
- 将方法的返回类型定义为父类类型,返回子类对象
public static Human getHuman(int type){
Human man = null;
switch(type){
case 1:
man = new Chinese();
break;
case 2:
man = new American();
break;
case 3:
man = new Indian();
break;
}
return man;
}
public static void main(String[] args) {
Human man1 = getHuman(1);
Human man2 = getHuman(2);
Human man3 = getHuman(3);
man1.eat();
man2.eat();
man3.eat();
}
向上转型和向下转型
向上转型
子类对象转为父类类型
Human person = new Chinese();
向下转型
父类对象转为子类类型
如:子类类型 对象= (子类类型)父类对象;
向下转型的前提是:先向上转型
//向上转型
Human person = new Chinese();
//向下转型
Chinese ch = (Chinese)(person);
ch.eat();
注意:向下转型存在类型转换错误的风险
Human person = new Chinese();
//出现 ClassCastException 类型转换异常
Indian ch = (Indian)(person);
instanceof 运算符
instanceof 作用:判断一个对象是否是某个类型的
对象 instanceof 类型
返回boolean类型结果
7.5、static关键字和单例模式
static关键字
static的意思是静态,可以用来修饰类的属性和方法,一旦被static修饰的属性和方法,就会由类的所有对象共享,而不是属于某一个对象。
当某些属性和方法不想被对象调用时,就可以定义成静态的。
静态属性
语法:
static 类型 属性名;
静态属性的调用:
类名.属性名
也可以使用:对象名.属性
一般推荐使用类名调用静态成员
静态常量
开发过程中我们需要一些固定的数据,如:圆周率 3.1415926
public static final double PI = 3.1415926;
优点:
- 可读性高
- 提高数据的安全性
- 调用和维护方便
静态方法
定义:
public static 返回值类型 方法名(参数){
}
调用:
类名.方法名(参数)
注意:
- 静态方法中可以直接调用当前类的其它静态属性和方法
- 静态方法中不能直接调用当前类的非静态方法属性和方法
- 非静态方法中可以直接调用当前类的静态和非静态的属性和方法
- 静态方法中不能出现this和super关键字
Java中的各种工具类都大量使用静态方法,如:Arrays、Math等。
静态代码块
语法:
static{
代码块
}
作用:
对静态的成员进行初始化
特点:
- 静态代码块只执行一次
- 在类加载到内存后执行,是类中所有代码最先执行
- 在第一次使用类的使用调用
执行顺序:
- 静态代码块(只执行一次)
- 非静态代码块(每个对象执行一次)
- 构造方法(每个对象执行一次)
静态导入
jdk1.5的特性,导入某个类的静态方法后,可以不通过类名直接调用。
//静态导入
import static java.util.Arrays.sort;
public class Test3 {
public static void main(String[] args) {
int[] array = {3,5,7,8,2};
//直接调用
sort(array);
for(int n : array){
System.out.println(n);
}
}
}
单例模式
设计模式是前人针对不同的应用需求总结一套解决方案,常见的设计模式有23种,也称为GOF23。
单例模式属于GOF23中的创建性模式,作用是:保证一个类只能创建一个实例
单例模式的应用场景:
- 某些业务场景,如:公司只有一个老板
- 减少大型对象对系统资源的消耗,如:连接池、线程池
如何实现单例模式?
步骤:
- 将构造方法定义成private的
- 在单例类的内部定义一个该类的静态对象
- 定义一个静态方法用于返回静态对象
单例模式分为:饿汉式和懒汉式
区别是:
- 饿汉式,一开始就创建对象
优点:代码简洁
缺点:如果方法没有调用,就浪费了内存 - 懒汉式,开始不创建对象,当方法被调用后,再创建对象
优点:内存的分配更高效
缺点:有线程安全问题
7.6、接口和内部类
接口
定义接口的语法:
public interface 接口名{
静态常量的定义;
方法的定义;
}
定义接口要注意:
- 方法是abstract的,不能实现
- 定义的属性会自动转变为静态常量
- 方法只能是public的,默认是public
实现接口:
public class 类名 implements 接口名{
具体方法的实现
}
实现接口要注意:
- 必须实现所有接口中的方法
- 方法必须和接口中定义的完全一致
- 方法必须是public的
- 一个类可以实现多个接口
- 一个类可以继承类同时实现接口
class 类名 extends 父类 implements 接口{
}
- 接口可以继承接口,实现类必须实现所有的方法
interface A{
void testA();
}
//接口之间的继承
interface B extends A{
void testB();
}
class C implements B{
public void testA() {
}
public void testB() {
}
}
接口在开发中的意义:
- 为类的实现制定一套规范
- 把设计和实现分开
default关键字
Java1.8的新特性,被default定义的方法可以有默认的实现。
public interface Person {
//给接口方法默认的实现
default void eat(){
System.out.println("在吃饭!!");
}
void walk();
}
实现类不强制要求实现带default的方法。
接口与抽象类的异同
相同点:
- 都可能存在没有实现的方法
- 都不能实例化
不同点: - 抽象类是单一继承,类可以实现多个接口
- 接口不能定义构造方法
- 接口中的方法只能是public,抽象类可以有各种访问类型的方法。
- 接口中只能定义静态常量,抽象类可以定义普通的成员变量。
- 接口中的抽象方法不用加abstract,抽象类必须加。
内部类
内部类就是在类里面定义的类。
Java的内部类包含:
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类
匿名内部类
一个没有名字的内部类,创建类的同时创建了对象。
应用场合:接口或抽象类的实现类只需要使用一次,代码是一次性的。
语法:
new 接口/抽象类()
{
实现方法
};
实现USB案例:
//Usb接口
interface Usb{
void connect(); //连接
void charge(); //充电
}
//匿名内部类实现接口
Usb usb = new Usb(){
public void connect(){
System.out.println("Test2测试连接");
}
public void charge(){
System.out.println("Test2测试充电");
}
};
usb .connect();
usb .charge();
和一般类的区别:
- 没有名字
- 只能使用一次
- 没有构造方法
- 不能定义静态成员
在?处填写什么可以在控制台输出30,20,10。
class Outer {
public int num = 10;
class Inner {
public int num = 20;
public void show() {
int num = 30;
System.out.println(?); num
System.out.println(?); this.num
System.out.println(?); Outer.this.num
}
}
}
class InnerClassTest {
public static void main(String[] args) {
Outer.Inner oi = new Outer().new Inner();
oi.show();
}
}
8、Java常用类
- 字符串相关:String、 StringBuffer、 StringBuilder
- 包装类:Integer等
- 日期相关: Date、 Calendar、 SimpleDateFormat
- 其他:Math、Random、Runtime、System
8.1、字符串相关
String类
创建方式1:赋值常量
String str = "hello";
使用双引号括起来的内容叫字符串常量值,字符串常量值分配在方法区的常量池中。
常量池的好处:
- 节约内存,反复使用,不需要重新分配
- 性能高,直接调用,省去创建对象的时间
创建方式2:使用new创建对象
String name = new String("zhangsan");
StringBuilder类
如果字符串变量需要频繁修改,就会创建大量字符串对象,大量消耗内存空间。
String和StringBuilder的区别:
- String的字符串是不可修改,如果修改会创建新字符串,浪费内存。
- StringBuilder的字符串是可以修改的,不会创建新字符串。
为了解决这个问题,可以使用StringBuilder。
创建方式:
创建空值的对象
StringBuilder strb = new StringBuilder();
创建有默认值的对象
StringBuilder strb = new StringBuilder("默认值");
StringBuffer类
StringBuffer的特点和StringBuilder相似,都是在自身的数组上进行的修改,常用方法也一样。
不同点:
- StringBuffer的方法是线程安全的,StringBuilder是非线程安全的
- StringBuilder的执行效率高于StringBuffer
8.2、包装类
Java中的8种基本数据类型不是引用类型,也不能创建对象,这就违背了Java是完全面向对象语言这一前提。Java推出8个包装类,把基本类型包装成对象。
包装类包装了8种基本数据类型,把基本数据类型看做对象进行处理。
基本类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
创建方式:
Integer num1 = 1000;
或
Integer num2 = new Integer(1000);
装箱和拆箱
装箱与拆箱是基本数据类型和引用类型的相互转换。
- 装箱:基本数据类型转换为引用类型
如: Integer n = 200; - 拆箱:引用类型转换为基本数据类型
如:Integer n = 200;
int m = n; //拆箱
字符串与基本类型的转换
字符串转换为int类型
String s = "1234";
int num = Integer.parseInt(s);
其他类型
Float.parseFloat(字符串) 转换为Float
Double.parseDouble(字符串) 转换为Double
...
int类型转换为字符串
String s = String.valueOf(num);
或
int num = 20;
String s = "" + num;
8.3、日期和时间处理
Date类
处理日期时间
创建方法1:
Date date = new Date(); 获得当前的时间
创建方法2:
Date date = new Date(long); 指定时间的1900-1-1到现在的毫秒数
常用方法
int getYear() 获得年
int getMonth() 获得月
int getDate() 获得天
int getHours() 获得小时
int getMinutes() 获得分钟
int getSeconds() 获得分钟
void setYear(int year) 设置年
...
Calendar 类
日历类可以获得和修改日期的某个部分
创建Calendar对象
Calendar calendar = Calendar.getInstance();
常用方法
get(日期类型) 获得日期的某个部分
Calendar.YEAR
Calendar.MONTH 月份从0开始
....
set(日期类型,数值) 设置日期的某个部分
add(日期类型,数值) 实现日期某个部分的追溯
SimpleDateFormat类
对日期进行格式化
创建对象
SimpleDateFormat sdf = new SimpleDateFormat("日期格式字符串");
日期格式字符串,如:"yyyy-MM-dd hh:mm:ss"
yyyy 代表4位的年份
MM 代表2位的月份
dd 代表2位的日期
hh/HH 12小时制/24小时制
mm 代表分钟
ss 代表秒钟
a 代表AM/PM
将日期进行格式化
String format(Date对象);
将字符串转换为日期
Date parse(String对象);
8.4、其他类
Math类
Random类
获得随机数
Random random = new Random();
random.nextInt(100); //0 ~ 100间随机整数
Runtime类
System类
9、集合
9.1、集合框架体系
接口特点:
- Collection接口
定义了集合的通用方法,如:添加、删除、集合个数 - List接口
可以排序、可以添加重复的数据 - Set接口
不能单独访问,数据不能重复 - Map接口
键值对,通过键访问
9.2、如何选择集合
在开发过程中,需要根据实际业务场景,结合集合的特点选择集合
- 可以排序,可以添加重复数据,可以随机访问 ----- List
- 对数据访问要求高 ----- ArrayList
- 对插入和删除要求高 ----- LinkedList - 不能添加重复的数据,不需要随机访问 ------ Set
- 没有顺序 ----- HashSet
- 可以进行排序 ----- TreeSet
- 保留添加顺序 ----- LinkedHashSet - 可以进行快速查找 ,以键值对保存------ Map
- 键没有顺序 ----- HashMap
- 键可以排序 ----- TreeMap
- 键保留添加顺序 ----- LinkedHashMap
10、异常
10.1、异常体系结构
10.2、常见异常
10.3、try-catch异常处理
异常被处理后,程序可以正常运行
语法:
try{
可能出现异常的代码
}catch(异常类型 ex){
处理异常的代码
}
异常处理过程:
- 如果try中的代码出现了异常,JVM会自动抛出异常,程序跳转到catch中执行异常处理,处理结束后程序正常执行。
- 如果try代码中没有异常,try代码执行完,跳过catch执行后面的代码。
10.4、多重catch异常处理
语法:
try{
可能出现异常的代码
}catch(异常类型1 ex){
处理代码1;
}catch(异常类型2 ex){
处理代码2;
}...
异常处理过程:
- 一旦出现异常,把异常和每个catch块进行匹配,如果匹配成功就执行catch后结束
- 如果不匹配再判断下一个catch的异常类型。
注意:如果catch中出现其他异常的父类,父类异常必须放在最后
10.5、try-catch-finally异常处理
finally最终的,在finally中的代码无论如何都会执行
程序有部分代码,是必须执行,如:数据库连接关闭、文件流关闭、网络连接的关闭。
try{
可能出现异常的代码
}catch(异常类型 ex){
异常处理
}finally{
如论如何都执行的代码
}
异常处理过程:
- 如果try出现异常,跳转到catch执行处理,最后执行finally代码;
- 如果try没有异常,try执行完,最后执行finally。
10.6、throws 和 throw关键字
throws关键字
用于方法声明异常,一旦方法声明异常,方法中可以不处理异常,由方法的调用者处理。
一般用于非运行时异常。
语法:
public 返回值类型 方法名(参数列表) throws 异常类型1, 异常类型2...{
}
throw关键字
用于手动抛出异常,给调用者进行提示
语法:
if(条件){
throw new 异常类型("异常描述信息");
}
10.7、自定义异常
实现步骤:
- 定义类继承Exception或其子类
- 满足某些条件时,在方法中用throw抛出异常
- 在声明方法时,用throws声明异常
- 调用方法时,使用try-catch处理异常
11、IO流
11.1、IO流分类
11.2、文件读写
文件读取
文件读取的过程
- 创建文件输入流
- 创建byte数组
- 循环调用read方法读取数据,存入byte数组
- 操作byte数组
- 读取完毕关闭文件流
文件写入
文件写入步骤:
- 创建文件输出流
- 将字符串转换为byte数组
- 调用write写入文件
- 关闭文件流
文件复制
文件的复制是在读取文件的同时,将数据写入到另一个文件中
思路:
- 创建输入流和输出流
- 通过输入流读取数据到byte数组中
- 同时将byte数组中的数据写入输出流
- 循环1、2、3
11.3、缓冲
11.4、字符流
11.5、打印流
11.6、数据流
12、网络编程
12.1、IP
IP地址指的是互联网地址(Internet Protocol Address ,是联网设备与互联网之间的唯一标识,在同一个网段中,IP地址是唯一的
IP地址是数字型的,是一个32位的二进制,通常将其分成4个8位的二进制数,每8位之间用圆点隔开,每个8位整数可以转换为一个0~255的十进制整数,例如:202.9.128.88
12.2、端口
IP地址可以唯一的确定网络上的一个通信实体,但一个通信实体可以有多个通信程序同时提供网络服务,此时还需要使用端口
数据的发送和接收都需要通过端口出入计算机,端口号用于唯一标识通信实体上进行网络通讯的程序,同一台机器上不能两个程序占用同一个端口
端口号的取值范围:0 ~ 65535
12.3、Socket编程
12.4、TCP
三次握手和四次挥手
12.5、UDP
TCP和UDP同属于传输层协议,对比TCP和UDP: