Java基础语法
Java特性和优势
- 简单性
- 面向对象
- 可移植性
- 高性能
- 分布式
- 动态性
- 多线程
- 安全性
- 健壮性
Java三大版本
JavaSE:标准版(桌面程序、控制台开发…)
JavaME:嵌入式开发(手机、小家电…)
JavaEE:E企业开发(web端、服务器开发)
JDK、JRE、JVM
JDK:Java Development Kit
JRE:Java Runtime Environment
JVM:Java Virtual Machine
卸载JDK
-
删除Java的安装目录
-
删除JAVA_HOME
-
删除path下关于java的目录
-
cmd查询
java -version
安装JDK
- 搜索JDK8,找到对应下载地址
- 同意协议
- 下载电脑的对应版本
- 双击安装JDK
- 记住安装路径
- 配置环境变量
- 我的电脑-右键-属性
- 环境变量-JAVA_HOME
- 配置path变量
- 测试JDK是否安装成功
- 打开cmd
java -version
HelloWorld
1.随便新建一个文件夹,存放代码
2.新建一个java文件
文件后缀名为.java。即Hello.java
public class Hello{
public static void main(String[] args){
System.out.print("Hello,World!");
}
}
3.编译javac java文件
,会生成一个class文件
4.运行class文件,java 类名
,输出:Hello,World!
Java程序运行机制
既是编译型,又是解释型
注释
Java中的注释有三种:
- 单行注释
- 多行注释
- 文档注释
标识符
关键字
Java所有的组成部分都需要名字。类名、变量名以及方法名都被称为标识符。
标识符注意点
- 所有的标识符号都应以字符,美元符$,或者下划线_开始
- 首字符之后可以是字符,美元符$,或者下划线_或数字的任何字符组合
- 不能使用关键字作为变量名或方法名
- 标识符是大小写敏感的
- 可以使用中文命名,但不建议
数据类型
强类型语言:要求变量的使用要严格符合规定,所有变量都需要先定义后使用
Java为强类型语言
弱类型语言
Java的数据类型分为两大类
1.基本类型(primitive type)
a.数值类型:
- 整数类型:byte、short、int、long(Long类型要在数字后面加个L)
- 浮点类型:float(float类型要在数字后面加个F)、double
- 字符类型
b.boolean类型
2.引用类型(reference type)
a.类
b.接口
c.数组
字节
位(bit):是计算机内部数据存储的最小单位
字节(byte):是计算机中数据处理的基本单位,习惯上用大写B来表示
1B(byte)=8bit(位)
字符:是指计算机中使用的字母、数字、字和符号
扩展练习:
//银行业务不能用float,一般使用BigDeciaml数学工具类
//float 有限,离散,摄入误差,接近但不等与
float f = 0.1f;
double d = 1.0/10;
System.out.println(f==d);// fasle
所有的字符本质还是数字,使用Unicode编码进行转换
\u表示转义
char c3 = '\u0061';
System.out.println(c3);//a
转义字符:
\t
制表符,\n
换行符…
类型转换
Java是强类型语言,所以有时进行运算时,需要用到类型转换
运算中,不同类型的数据线转化为同一类型,然后进行运算。
低 -----------------------------------> 高
byte,short,char-> int -> long-> float -> double
- 强制类型转换:
(类型)变量名
,由高到低 - 自动类型转换:由低到高
public class Demo05 {
public static void main(String[] args) {
int i = 128;
byte b = (byte)i; //内存溢出,byte范围:-128~127
System.out.println(b);// -128
}
}
注意点:
不能对布尔值进行类型转换
不能把对象类型转换为不相干的类型
在把高容量转换到低容量时,强制转换
转换的实时可能存在内存溢出,或者精度问题
JDK7新特性:数字之间可以用下划线_分割
变量
变量就是可以变化的量
Java是一种强类型语言,每个变量都必须声明其类型
Java变量是程序中最基本的存储单元,其要素包括变量名,变量类型和作用域。
可以使用逗号隔开来声明多个同类型变量,但不建议。
注意:
每个变量都有类型,类型可以是基本类型,也可以是引用类型
变量名必须是合法的标识符
变量声明是一条完整的语句,因此每一个声明都必须以分号结束。
变量作用域
类变量staitc
实例变量,方法外面,类里面,从属于对象
局部变量,方法里面
注:
布尔值的默认值是false
除了基本类型,其余的默认值都为null
常量
常量:初始化后不能再改变值
常量可以理解为一种特殊的变量
关键字为final,一般命名使用大写字母
运算符
++和–为一元运算符
短路运算
位运算,根据位来运算
&:相同位的两个数字都为1,则为1;若有一个不为1,则为0。
|:相同位只要一个为1即为1。
异或^:相同位不同则为1,相同则为0。
~:把内存中的0和1全部取反
<<:a << b就表示把a转为二进制后左移b位(在后面添b个0)。例如100的二进制为1100100,而110010000转成十进制是400,那么100 shl 2 = 400。可以看出,a << b的值实际上就是a乘以2的b次方,因为在二进制数后添一个0就相当于该数乘以2。
>>
:和>>
相似,a >>
b表示二进制右移b位(去掉末b位),相当于a除以2的b次方(取整)。
<<<:空位用0补齐
字符串连接符+
多个变量,如果先写字符串,则全部拼接,如""+a+b
;若后写字符串,则其余运算完之后再进行拼接,如a+b+""
。
包机制
为了更好组织类,Java提供包机制,用于区别类名的命名空间
一般利用公司域名倒置作为包名
JavaDoc
javadoc命令是用来生成自己的API文档的
输入:/**
+回车,来写类和方法的注释
在项目文件中使用cmd:
javadoc -encoding UTF-8 -charset UTF-8 Doc.java
会生成html文档,点开生成的index.html会生成类似官方的API文档
或者可以使用IDEA来进行生成,菜单栏-Tools-Generate JavaDoc
Java流程控制
用户交互Scanner
通过Scanner类来获取用户的输入
Scanner s = new Scanner(System.in);
通过Scanner的next()与nextLine()方法获取输入的字符串,在读取前我们一般使用hasNext()与hasNextLine()判断是否还有输入的数据
凡是属于IO流的类如果不关闭会一直占用资源,用完就关掉
scanner.close();
next():
- 一定要读取到有效字符后才可以结束输入
- 对于输入有效字符之前遇到的空白,next()方法会自动将其去掉
- 只有输入有效字符后才能将其后面输入的空白作为分隔符或者结束符
- next()不能得到带有空格的字符串
nextLine():
- 以Enter为结束符,也就是说nextLine()方法返回的是输入回车之前的所有字符。
- 可以获得空白
一般的输入字符串都用nextLine()
小练习:
package scanner;
import java.util.Scanner;
public class Demo05 {
public static void main(String[] args) {
// 输入多个数字,并求其总和与平均数,每输入一个数字用回车分割,通过输入非数字来结束输入并输出执行结果
Scanner scanner = new Scanner(System.in);
// 和
double sum = 0;
// 计算输入了多少个数字
int m = 0;
while(scanner.hasNextDouble()){
double x = scanner.nextDouble();
m = m + 1;
sum = sum + x;
System.out.println("你输入了第" + m + "个数据,当前结果sum为:" + sum);
}
System.out.println(m + "个数的和为" + sum);
System.out.println(m + "个数的平均值为" + (sum / m));
scanner.close();
}
}
/**
* 12
* 你输入了第1个数据,当前结果sum为:12.0
* 15
* 你输入了第2个数据,当前结果sum为:27.0
* 20
* 你输入了第3个数据,当前结果sum为:47.0
* a
* 3个数的和为47.0
* 3个数的平均值为15.666666666666666
*
*
*/
顺序结构
JAVA的基本结构就是顺序结构,除非特别指明,否则就按照顺序一句一句执行,是任何一个算法都离不开的基本算法结构。
选择结构
-
if单选择结构
-
if双选择结构
-
if多选择结构
if(布尔表达式 1){ //如果布尔表达式1的值为true执行代码 }else if(布尔表达式 2){ //如果布尔表达式2的值为true执行代码 }else if(布尔表达式 3){ //如果布尔表达式3的值为true执行代码 }else{ //如果布尔表达式都不为true执行代码 }
-
嵌套的if结构
-
switch多选择结构
switch多选择结构
switch语句中的变量类型可以是:
- byte、short、int或char
- 从Java SE 7开始,switch支持字符串String类型
- case标签必须为字符串常量或字面量
switch(expression){
case value:
//语句
break;//可选
case value:
//语句
break;//可选
//可以有任意数量的case语句
default://可选
//语句
}
循环结构
- while循环
- do…while循环,先执行后判断,循环体至少执行一次
- for循环
- 在java5中引入了一种主要用于数组的增强型for循环
九九乘法表
public class forDmemo04 {
public static void main(String[] args) {
// 九九乘法表
for (int j = 0; j <= 9; j++) {
for (int i = 1; i <= j; i++) {
System.out.print(j+"*"+i+"="+(j*i)+"\t");
}
System.out.println();
}
}
}
增强for循环:
Java5引入了一种主要用于数组或集合的增强型for循环
Java增强for循环语法格式如下:
//声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时数组元素的值相等。
//表达式:表达式是要访问的数组名,或者是返回值为数组的方法
for(声明语句 : 表达式)
{
//代码句子
}
如,遍历数组:
public class forDemo05 {
public static void main(String[] args) {
int[] numbers = {10, 20, 30, 40, 50};
for (int x: numbers){
System.out.println(x);
}
}
}
break和continue
break强行退出循环,不执行循环中剩余的语句
continue用户终止某次循环,跳过循环中尚未执行的语句,接着进行下一次是否执行循环的判定
练习,打印三角形
public class TestDemo {
public static void main(String[] args) {
// 打印三角形,5行
for (int i = 1; i <= 5; i++) {
for (int j = 5; j >= i; j--) {
System.out.print(" ");
}
for (int j = 1; j <= i; j++) {
System.out.print("*");
}
for (int j = 1; j < i; j++) {
System.out.print("*");
}
System.out.println();
}
}
}
Java方法
方法
Java方法是语句的集合,它们在一起执行一个功能。
- 方法是解决一类问题的步骤的有序组合
- 方法包含于类或对象中
- 方法在程序中被创建,在其他地方被引用
设计方法的原则:保持方法的原子性,就是一个方法只完成1个功能,这样利于我们后期的扩展。
加法举例:
public class Demo01 {
public static void main(String[] args) {
int sum = add(1, 2);// 实际参数
System.out.println(sum);// 3
}
// 加法,形式参数
public static int add(int a, int b){
return a+b;
}
}
Java的方法类似于其它语言的函数,是一段用来完成特定功能的代码片段。
一般情况下,定义一个方法包含以下语法:
方法包含一个方法头和一个方法体。下面使一个方法的所有部分:
- 修饰符:可选的,告诉编译器如何调用该方法,定义了该方法的访问类型。
- 返回值类型:方法可能会返回值。returnValueType是方法返回值的数据类型。有些方法执行所需的操作,但没有返回值,这种情况下,returnValueType是关键字void。
- 方法名:是方法的实际名称。
- 参数类型:参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包括任何参数。
- 形式参数:在方法被调用时用于接受外接输入的数据
- 实参:调用方法时实际传给方法的数据
- 方法体:方法包含具体的语句,定义该方法的功能。
修饰符 返回值类型 方法名(参数类型 参数名){
...
方法体
...
return 返回值;
}
方法调用
调用方法:对象名.方法名(实参列表)
Java支持两种调用方法的方式,根据方法是否返回值来选择:
- 当方法返回一个值的时候,方法调用通常被当做一个值,例如:
int larger = max(30,40);
- 如果方法返回值是void,方法调用一定是一条语句。
练习,比大小
public class Demo02 {
public static void main(String[] args) {
int max = max(10, 20);
System.out.println(max);// 20
}
// 比大小
public static int max(int num1,int num2){
// 一般return写在最外面,此时定义一个变量
int result = 0;
if(num1==num2){
System.out.println("num1==num2");
// 终止方法
return 0;
}
if (num1>num2){
result = num1;
}else{
result = num2;
}
return result;
}
}
方法的重载
重载就是在一个类中,有相同的函数名称,但形参不同的函数。
方法重载的规则:
- 方法名称必须相同
- 参数列表必须不同(个数不同、或类型不同、参数排列顺序不同等)
- 方法的返回类型可以相同也可以不相同。
- 仅仅返回类型不同不足以成为方法的重载。
实现原理:
方法名称相同时,编译器会根据调用方法参数的个数、参数类型等去逐个匹配,以选择对应的方法,如果匹配失败,则编译器报错。
命令行传参
在程序运行时传递给它消息,要靠命令行参数给main()函数实现。
例如:
package method;
public class Demo03 {
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
System.out.println("args["+ i+ "]:"+args[i]);
}
}
}
打开文件所在路径,在路径位置输入cmd
先进行编译:javac Demo03.java
回退到src目录下才可以执行:cd ../
运行并传参:java method.Demo03 this is kkk
可看到参数已传入
可变参数
(不定向参数)
- JDK1.5开始,Java支持传递同类型的可变参数给一个方法。
- 在方法声明中,在指定参数类型后加一个省略号(…)。
- 一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明。
其本质为数组,如:
public class Demo04 {
public static void main(String[] args) {
Demo04 demo04 = new Demo04();
demo04.test(1,2,3,4,45,5);
// 输出:
// 1
// 2
// 3
// 4
// 45
// 5
}
public void test(int... i){
System.out.println(i[0]);
System.out.println(i[1]);
System.out.println(i[2]);
System.out.println(i[3]);
System.out.println(i[4]);
System.out.println(i[5]);
}
}
递归
A方法调用A方法,自己调用自己
java使用的都是栈机制
一般不使用递归,小量计算时才使用
Java数组
数组
数组是相同类型数据的有序集合
数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成
数组声明创建
如:
public class ArrayDemo01 {
public static void main(String[] args) {
int[] nums;// 1.声明一个数组
nums = new int[10];// 2.创建一个数组
//或者声明和创建可以写在一起???
// 3.给数组元素中赋值
nums[0] = 1;
nums[1] = 2;
nums[2] = 3;
nums[3] = 4;
nums[4] = 5;
nums[5] = 6;
nums[6] = 7;
nums[7] = 8;
nums[8] = 9;
nums[9] = 10;
// 计算所有元素的和
int sum = 0;
for (int i = 0; i < nums.length; i++) {
sum = sum + nums[i];
}
System.out.println(sum);//55
}
}
内存分析
Java内存:
- 堆:
- 存放new的对象和数组
- 可以被所有的线程共享,不会存放别的对象引用
- 栈:
- 存放基本变量类型(会包含这个基本类型的具体数值)
- 引用对象的变量(会存放这个引用在堆里面的具体地址)
- 方法区:
- 可以被所有的线程共享
- 包含了所有的class和static变量
只要使用new,都是保存在堆中
数组的三种初始化
public class ArrayDemo02 {
public static void main(String[] args) {
//静态初始化
int[] a = {1,2,3,4,5,6,7,8};
System.out.println(a[0]);
//动态初始化,包含默认初始化
int[] b = new int[10];
b[0] = 10;
System.out.println(b[0]);
//默认初始化
System.out.println(b[3]);// 0
}
}
数组的四个基本特点
- 数组长度是确定的,数组一旦被创建,它的大小就是不可改变的。
- 数组元素必须是相同类型,不允许出现混合类型。
- 数组中的元素可以是任何数据类型,包括基本类型和引用类型。
- 数组变量属于引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量。数组本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中的。
数组边界
下标的合法区间[0,length-1]
数组的使用
练习
public class ArrayDemo04 {
public static void main(String[] args) {
int[] arrays = {1,2,3,4,5};
//
// //JDK1.5,遍历
// for(int array : arrays){
// System.out.println(array);
// }
// printArray(arrays);
int[] reverse = reverse(arrays);
printArray(reverse);
}
//反转数组
public static int[] reverse(int[] arrays) {
int[] result = new int[arrays.length];
//反转操作
for (int i = 0,j=result.length-1; i < arrays.length; i++,j--) {
result[j] = arrays[i];
}
return result;
}
//打印数组元素
public static void printArray(int[] arrays) {
for (int i = 0; i < arrays.length; i++) {
System.out.println(arrays[i]+" ");
}
}
}
多维数组
多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组。
int a[][] = new int[2][5];
Arrays类
数组的工具类java.util.Arrays
Arrays类中的方法都是static修饰的静态方法,在使用的时候可以直击使用类名进行调用
具有以下常用功能:
- 给数组赋值:通过fill方法
- 对数组的排序:通过sort方法,按照升序
- 比较数组:通过equals方法比较数组中元素值是否相等
- 查找数组元素:通过binarySearch方法能对排序好的数组进行二分查找法操作。
冒泡排序
时间复杂度为O(n²)
public class ArrayDemo07 {
public static void main(String[] args) {
int[] a = {1,4,5,6,72,2,2,2,25,6,7};
int[] sort = sort(a);
System.out.println(Arrays.toString(sort));
}
// 冒泡排序
public static int[] sort(int[] array){
int temp = 0;
for (int i = 0; i < array.length-1; i++) {
// 减少没有意义的比较
boolean flag = false;
for (int j = 0; j < array.length-1-i; j++) {
if(array[j+1]<array[j]){
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
flag = true;
}
}
if(flag == false){
break;
}
}
return array;
}
}
稀疏数组
当一个数组中大部分元素为0,或者为同一值的数组时,可以使用稀疏数组来保存该数组。
稀疏数组的处理方式是:
- 记录数组一共有几行几列,有多少个不同值
- 把具有不同值的元素的行、列及值记录在一个小规模的数组中,从而缩小程序的规模
如图,左边是原始数组,右边是稀疏数组
代码:
public class ArrayDemo08 {
public static void main(String[] args) {
//1.创建一个二维数组11*11 0:没有棋子 1:黑棋 2:白棋
int[][] array1 = new int[11][11];
// 第2行第3列
array1[1][2] = 1;
// 第3行第4列
array1[2][3] = 2;
//输出原始的数组
System.out.println("输出原始的数组:");
for (int[] ints : array1) {
for(int anInt:ints){
System.out.print(anInt+"\t");
}
System.out.println();
}
System.out.println("================");
//转换为稀疏数组保存
//获取有效值的个数
int sum = 0;
for (int i = 0; i <11 ; i++) {
for (int j = 0; j <11 ; j++) {
if(array1[i][j]!=0){
sum++;
}
}
}
System.out.println("有效值的个数:"+sum);
//2.创建一个稀疏数组的数组
// 有效值的个数+1就是稀疏数组的行数,需要行、列、值3列
int[][] array2 = new int[sum+1][3];
// 第一行
array2[0][0] = 11;//存储行数
array2[0][1] = 11;//存储列数
array2[0][2] = sum;//存储有效值个数
//遍历二维数组,将非零的值存放在稀疏数组中
int count = 0;
for (int i = 0; i < array1.length; i++) {
for (int j = 0; j < array1[i].length; j++) {
if(array1[i][j]!=0){
count++;
// 每行的第一个位置存横坐标
array2[count][0] = i;
// 每行的第二个位置存纵坐标
array2[count][1] = j;
// 每行的第三个位置存当前值
array2[count][2] = array1[i][j];
}
}
}
//输出稀疏数组
System.out.println("稀疏数组:");
for (int i = 0; i < array2.length; i++) {
System.out.println(array2[i][0]+"\t"
+array2[i][1]+"\t"
+array2[i][2]+"\t");
}
System.out.println("================");
System.out.println("根据稀疏数组进行还原:");
// 1.读取稀疏数组
int[][] array3 = new int[array2[0][0]][array2[0][1]];// 数组的长、宽
// 2,给其中的元素还原它的值
for (int i = 1; i < array2.length; i++) {//从i=1开始,第一行信息不用读
// 根据上面稀疏数组array2来存到新的数组array3中
array3[array2[i][0]][array2[i][1]] = array2[i][2];
}
// 3.打印
System.out.println("输出还原的数组:");
for (int[] ints : array3) {
for(int anInt:ints){
System.out.print(anInt+"\t");
}
System.out.println();
}
}
}
/**
* 输出原始的数组:
* 0 0 0 0 0 0 0 0 0 0 0
* 0 0 1 0 0 0 0 0 0 0 0
* 0 0 0 2 0 0 0 0 0 0 0
* 0 0 0 0 0 0 0 0 0 0 0
* 0 0 0 0 0 0 0 0 0 0 0
* 0 0 0 0 0 0 0 0 0 0 0
* 0 0 0 0 0 0 0 0 0 0 0
* 0 0 0 0 0 0 0 0 0 0 0
* 0 0 0 0 0 0 0 0 0 0 0
* 0 0 0 0 0 0 0 0 0 0 0
* 0 0 0 0 0 0 0 0 0 0 0
* ================
* 有效值的个数:2
* 稀疏数组:
* 11 11 2
* 1 2 1
* 2 3 2
* ================
* 根据稀疏数组进行还原:
* 输出还原的数组:
* 0 0 0 0 0 0 0 0 0 0 0
* 0 0 1 0 0 0 0 0 0 0 0
* 0 0 0 2 0 0 0 0 0 0 0
* 0 0 0 0 0 0 0 0 0 0 0
* 0 0 0 0 0 0 0 0 0 0 0
* 0 0 0 0 0 0 0 0 0 0 0
* 0 0 0 0 0 0 0 0 0 0 0
* 0 0 0 0 0 0 0 0 0 0 0
* 0 0 0 0 0 0 0 0 0 0 0
* 0 0 0 0 0 0 0 0 0 0 0
* 0 0 0 0 0 0 0 0 0 0 0
*
*
*/
面向对象
面向过程&面向对象
面向过程思想:第一步做什么,第二步做什么…适合处理较为简单的问题。
面向对象思想:分类的思维模式,每个类进行单独思考。最后才对某个分类下的细节进行面向过程的思索。适合处理复杂的、需要多人协作的问题。
Java的核心思想就是面向对象编程OOP(Object-Oriented Programming)
面向对象编程的本质就是:以类的方式组织代码,以对象的方式组织(封装)数据。
面向对象的三大特性:
- 封装
- 继承
- 多态
从认识论角度考虑是先有对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽象。
从代码运行角度考虑是先有类后有对象。类是对象的模板。
非静态方法调用,需要将类实例化,使用new关键字.
如:
Student.java
package oop.demo01;
// 学生类
public class Student {
// // 静态方法
// public static void say(){
// System.out.println("学生说话");
// }
// 非静态方法
public void say(){
System.out.println("学生说话");
}
}
同级目录下:
Demo02.java
package oop.demo01;
public class Demo02 {
public static void main(String[] args) {
// 静态方法调用
// Student.say();
// 输出:学生说话
// 非静态方法调用,需要将类实例化,使用new关键字
// 对象类型 对象名 = 对象值
Student student = new Student();
// 调用
student.say();
// 输出:学生说话
}
}
static静态方法和类一起加载,类存在时就存在。但非静态方法和对象有关,对象创建后才存在(类实例化为对象)。
类与对象的关系
类是一种抽象的数据类型,它是对某一类事物整体描述/定义,但是不能代表某一个具体的事物。
对象是抽象概念的具体实例。
创建与初始化对象
- 使用new关键字来创建对象
- 使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用
一个类中只包含属性和方法。
构造器
类中的构造器也叫构造方法,是在进行创建对象的时候必须要调用的。
构造器有以下特点:
- 必须和类的名字相同
- 必须没有返回类型,也不能写void
一个类如果什么都不写,也会存在一个构造器(构造方法)。
Person.java
package oop.demo02;
public class Person {
}
Application.java
package oop;
public class Application {
public static void main(String[] args) {
// new实例化了一个对象
Person person = new Person();
}
}
运行Application.java文件
在项目目录下的out文件夹下打开生成的Person.class文件,可看到默认自动生成了 public Person()方法,即构造方法。
package oop.demo02;
public class Person {
public Person() {
}
}
- 在new一个对象时,本质是在调用构造器。
- 构造器可以用来初始化对象的值。
- 定义了有参构造之后,如果想使用无参构造,必须显示的定义一个无参构造
Person.java
package oop.demo02;
public class Person {
// 一个类即使什么都不写,它也会存在一个方法
// 显示的定义构造器
String name;
// 实例化初始值
public Person(){
}
// 无参构造
// public Person(){
// this.name = "kkk";
//
// }
// 有参构造:一旦定义了有参构造,无参构造就必须显示定义一个无参构造
public Person(String name){
this.name = name;
}
}
Application.java
package oop.demo02;
public class Application {
public static void main(String[] args) {
// new 实例化了一个对象
Person person = new Person("kkk");
System.out.println(person.name);
// 输出:kkk
}
}
运行Application.java文件,输出kkk。
ALT+INSERT-点Constructor-选中-OK,生成有参构造器。
ALT+INSERT-点Constructor-选中-Select None,生成无参构造器。
封装
程序设计追求“高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用。
封装就是数据的隐藏。通常,应禁止直接访问一个对象中的数据的实际表示,而应通过操作接口来访问,这称为信息隐藏。
private私有属性,使用一些public的get/set方法便可以操作这些属性。
ALT+INSERT-Getter and Setter-选中属性-OK,自动生成get和set方法。
Stundent.java
package oop.demo04;
public class Student {
// 属性私有
// 名字
private String name;
// 学号
private int id;
// 性别
private char sex;
//年龄
private int age;
// 提供一些可以操作这个属性的方法
// 提供一些public的get、set方法
// get 获得这个数据
public String getName(){
return this.name;
}
// set 给这个数据设置值
public void setName(String name){
this.name = name;
}
// ALT+INSERT-Getter and Setter-选中属性-OK,自动生成get和set方法。
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
// 封装过的可以做一下安全性的检查
public void setAge(int age) {
if (age>120 || age<0) {
this.age =3;
}else {
this.age = age;
}
}
}
Application.java
package oop;
import oop.demo04.Student;
public class Application {
public static void main(String[] args) {
Student s1 = new Student();
s1.setName("kkk");
System.out.println(s1.getName());
// 输出:kkk
// 年龄不合逻辑,可在封装中做检查
s1.setAge(999);
System.out.println(s1.getAge());
// 输出:3
}
}
封装的作用:
- 提高程序的安全性,保护数据
- 隐藏代码的实现细节
- 统一接口
- 提高系统的可维护性
继承
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模。
extends的意思是“扩展”,子类是父类的扩展。
Java中类只有单继承,没有多继承。(一个儿子只能有一个父亲,一个父亲可以有多个儿子)
继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等。
继承关系的两个类,一个为子类(派生类),一个父类(基类)。子类继承父类,使用关键字extends来表示。
子类和父类之间,从意义上讲具有"is a"的关系。
子类继承父类,就会拥有父类的全部方法。但父类私有的private无法继承,一般属性才设置为私有,但可以设置get和set方法来进行操作。
Person.java
package oop.demo05;
import com.sun.org.apache.xpath.internal.SourceTree;
// Person 人 : 父类
public class Person {
// 父类私有的private无法继承,一般属性才设置为私有,但可以设置get和set方法来进行操作。
private int money = 10_0000_0000;
public void say(){
System.out.println("说了一句话");
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
}
Person.java
package oop.demo05;
// 学生 is 人 : 派生类,子类
// 子类继承了父类,就会拥有父类的全部方法
public class Student extends Person {
}
Application.java
package oop;
import oop.demo05.Student;
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.say();
// 输出:说了一句话
System.out.println(student.getMoney());
// 输出:1000000000
}
}
CTRL+H显示当前类继承树
在Java中,所有的类,都默认直接或间接继承Object类。
继承中的Super
super代表父类的,this为当前的。
super注意点:
- super调用父类的构造方法,必须在构造方法的第一个。
- super必须只能出现在子类的方法或者构造方法中。
- super和this不能同时调用构造方法。(因为都要在第一行,所以同时出现会有一个报错)
Person.java
package oop.demo05;
import com.sun.org.apache.xpath.internal.SourceTree;
// Person 人 : 父类
public class Person {
// 类中有默认的构造器,调用一个对象会先走无参构造器
public Person() {
System.out.println("Person无参构造执行了");
}
protected String name = "kkk";
public void print(){
System.out.println("Person");
}
}
Student.java
package oop.demo05;
// 学生 is 人 : 派生类,子类
// 子类继承了父类,就会拥有父类的全部方法
public class Student extends Person {
// 默认调用了父类的无参构造,调用父类的构造器,必须要在子类构造器的第一行
// super();//可写可省略
public Student() {
System.out.println("Student无参构造执行了");
}
private String name = "kun";
public void print(){
System.out.println("Student");
}
public void test1(){
print();
// 输出:Student
this.print();
// 输出:Student
super.print();
// 输出:Person
}
public void test(String name){
System.out.println(name);
// 输出:kill
// Student类中的name
System.out.println(this.name);
// 输出:kun
// 父类Person类中的name
System.out.println(super.name);
// 输出:kkk
}
}
Application.java
package oop;
import oop.demo05.Student;
public class Application {
public static void main(String[] args) {
Student student = new Student();
// 输出:
// Person无参构造执行了
// Student无参构造执行了
student.test("kill");
student.test1();
}
}
super和this的区别:
- 代表的对象不同
- this:本身调用者这个对象
- super:父类对象的引用
- 使用前提
- this:没有继承的情况下也可以使用
- super:在继承条件下才可以使用
- 构造方法
this();
调用本类的构造super();
调用父类的构造
继承中的方法重写
B.java
package oop.demo05;
// 重写都是方法的重写,和属性无关
public class B {
public static void test(){
System.out.println("B=>test()");
}
}
A.java
package oop.demo05;
public class A extends B{
public static void test(){
System.out.println("A=>test()");
}
}
Application.java
package oop;
import oop.demo05.A;
import oop.demo05.B;
public class Application {
public static void main(String[] args) {
A a = new A();
a.test();
// 输出:A=>test()
// 父类的引用指向了子类
// 若为静态方法:方法的调用只和左边定义的数据类型有关,和右边new的无关
B b = new A();
b.test();
// 输出:B=>test()
}
}
运行Application.java
输出:
A=>test()
B=>test()
将方法写为非静态,并在A类中进行方法的重写。
B.java
package oop.demo05;
// 重写都是方法的重写,和属性无关
public class B {
public void test(){
System.out.println("B=>test()");
}
}
A.java
package oop.demo05;
public class A extends B{
// ALT+INSERT,选择Override Methods
@Override
public void test() {
System.out.println("A=>test()");
}
}
Application.java
package oop;
import oop.demo05.A;
import oop.demo05.B;
public class Application {
public static void main(String[] args) {
A a = new A();
a.test();
// 输出:A=>test()
// 父类的引用指向了子类
// 若为静态方法:方法的调用只和左边定义的数据类型有关,和右边new的无关
// 非静态:重写
// 子类重写了父类的方法(A重写了B的方法)
B b = new A();
b.test();
// 输出:A=>test()
}
}
此时运行Application.java
输出:
A=>test()
A=>test()
非静态方法才可以进行重写。
重写的前提是需要有继承关系,子类重写父类的方法。
- 方法名必须相同
- 参数列表必须相同
- 修饰符的范围可以扩大,但不能缩小:public>protected>default>private
- 抛出的异常的范围可以被缩小,但不能扩大:ClassNotFoundException<Exception
子类和父类的方法必须一致,方法体不同。
重写都是方法的重写,和属性无关。
为什么需要重写:父类的功能子类不一定需要或不一定满足。
重写快捷键:ALT+INSERT,选择Override Methods。
不能被重写的情况:
- static修饰的方法,属于类,不属于实例
- final常量
- private修饰的方法
多态
多态可实现动态编译,动态编译:类型在执行过程中才可以确定。增强可扩展性。
多态:同一方法可以根据发送对象的不同而采取多种不同的行为方式。
一个对象的实际类型是确定的,但可以指向对象的引用类型有很多。
Person.java
package oop.demo06;
public class Person {
public void run(){
System.out.println("run");
}
}
Student.java
package oop.demo06;
public class Student extends Person{
@Override
public void run() {
System.out.println("son");
}
public void eat(){
System.out.println("eat");
}
}
Application.java
package oop;
import oop.demo06.Person;
import oop.demo06.Student;
public class Application {
public static void main(String[] args) {
// 一个对象的实际类型是确定的
// new Student();
// new Person();
// 对象指向的引用类型不确定
// 实际类型都是确定的,都为Student,但指向这个对象的引用类型可以是任意的父类型
// Student能调用的方法都是自己的或者继承父类的
Student s1 = new Student();
// 父类的引用指向子类的类型
// Person父类型,可以指向子类,但是不能调用子类独有的方法
Person s2 = new Student();
// Object类是所有类的祖宗类
Object s3 = new Student();
// 子类重写了父类的方法,执行子类的方法
s2.run();
// 输出:son
s1.run();
// 输出:son
// 对象能执行哪些方法,主要看对象左边的类型,和右边关系不大
// s2.eat();不能调用,会报错,
s1.eat();
// 输出:eat
}
}
多态存在的条件:
- 有继承关系
- 子类重写父类的方法
- 父类引用指向子类对象
多态注意事项:
- 多态是方法的多态,属性没有多态
- 父类和子类要有联系才能进行类型转换,否则会出现类型转换异常(ClassCastEception)
instanceof
instanceof:判断前面的对象是否属于后面的类,或者属于其子类。
引用类型变量(object) instanceof 类(class)
Person.java
package oop.demo06;
public class Person {
public void run(){
System.out.println("run");
}
}
Student.java
package oop.demo06;
public class Student extends Person{
public void go(){
System.out.println("go");
}
}
Teacher.java
package oop.demo06;
public class Teacher extends Person{
}
Application.java
package oop;
import oop.demo06.Person;
import oop.demo06.Student;
import oop.demo06.Teacher;
public class Application {
public static void main(String[] args) {
// Object > String
// Object > Person > Teacher
// Object > Person > Student
Object object = new Student();
System.out.println(object instanceof Student);// true
System.out.println(object instanceof Person);// true
System.out.println(object instanceof Object);// true
System.out.println(object instanceof Teacher);// False
System.out.println(object instanceof String);// False
System.out.println("==========================");
Person person = new Student();
System.out.println(person instanceof Student);// true
System.out.println(person instanceof Person);// true
System.out.println(person instanceof Object);// true
System.out.println(person instanceof Teacher);// False
//System.out.println(person instanceof String);// 编译报错
System.out.println("==========================");
Student student = new Student();
System.out.println(student instanceof Student);// true
System.out.println(student instanceof Person);// true
System.out.println(student instanceof Object);// true
//System.out.println(student instanceof Teacher);// 编译报错
//System.out.println(student instanceof String);// 编译报错
//System.out.println(X instanceof Y);// 能否编译通过,取决于X和Y是否存在父子关系
}
}
类型之间的转化:
在Application.java中进行修改:
package oop;
import oop.demo06.Person;
import oop.demo06.Student;
public class Application {
public static void main(String[] args) {
// 类型之间的转化: 父 子
// Person > Student
Person obj = new Student();
// 高转低
//student.go();// 会报错
// 将obj这个对象转换为Student类型,就可以使用Student类型的方法
Student student = (Student) obj;
student.go();
// 输出:go
// 或者直接:
((Student)obj).go();
// 输出:go
// 低转高,可以自动转换
// 子类转换为父类,可能丢失自己的本来的一些方法
Student student1 = new Student();
student1.go();
// 输出:go
// 父类引用指向子类的对象
Person person = student;
//person.go();// 此时调用不了
}
}
- 把子类转换为父类,向上转型,直接转换
- 把父类转换为子类,向下转型,强制转换
- 方便方法的调用,减少重复的代码
static
静态属性和静态方法:
Student.java
package oop.demo07;
public class Student {
// 静态变量
private static int age;
// 非静态变量
private double score;
public void run(){
}
public static void go(){
}
public static void main(String[] args) {
Student s1 = new Student();
// 类变量
System.out.println(Student.age);
// 通过对象调用方法
System.out.println(s1.score);
// 非静态方法,new出来才可以调用
new Student().run();
// 静态方法可以直接调用
Student.go();
}
}
静态代码块:
Person.java
package oop.demo07;
public class Person {
// 2:用来赋初始值
{
System.out.println("匿名代码块");
}
// 1:只执行一次
static {
System.out.println("静态代码块");
}
// 3
public Person(){
System.out.println("构造方法");
}
public static void main(String[] args) {
Person person1 = new Person();
System.out.println("==============");
Person person2 = new Person();
// 运行,看加载顺序
/*
输出:
静态代码块
匿名代码块
构造方法
==============
匿名代码块
构造方法
*/
}
}
静态导入包:
package oop.demo07;
import static java.lang.Math.random;
import static java.lang.Math.PI;
public class Test {
public static void main(String[] args) {
System.out.println(random());
// 输出:0.008421153996752806
System.out.println(PI);
// 输出:3.141592653589793
}
}
通过final修饰的类不能被继承(没有子类)。
抽象类
abstract修饰的方法为抽象方法,abstract修饰的类为抽象类。
抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类。
抽象类不能使用new关键字来创建对象,它是用来让子类继承的。
抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的。
子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类。
抽象类中可以写普通方法,抽象方法必须在抽象类中。
Action.java
package oop.demo08;
// 抽象类
public abstract class Action {
// 起到约束作用,有人帮我们实现
// abstract,抽象方法,只有方法名字,没有方法的实现
public abstract void doSomething();
}
A.java
package oop.demo08;
// 抽象类的所有方法,继承了它的子类,都必须要实现它的方法,除非子类也为abstract
public class A extends Action{
@Override
public void doSomething() {
}
}
接口
递进关系:
- 普通类:只有具体实现
- 抽象类:具体实现和规范(抽象方法)都有
- 接口:只有规范,自己无法写方法。约束和实现分离,面向接口编程。
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是…则必须能…”的思想。
接口的本质是契约,制定好后大家都遵守。
OO的精髓是对抽象的抽象,接口最能体现这一点。
实现了接口的类,就需要重写接口中的方法。
接口可以实现多继承。
UserService.java
package oop.demo09;
// 接口都需要有实现类
public interface UserService {
// 属性默认为常量,默认为public static final,可省略,但一般不在接口中定义
int AGE = 99;
// 接口中的所有定义的方法都是抽象的:默认为public abstract,可省略
void add(String name);
void delete(String name);
void update(String name);
void query(String name);
}
TimeService.java
package oop.demo09;
public interface TimeService {
void timer();
}
UserServiceImpl.java
package oop.demo09;
// 类实现接口,implements 接口
// 实现了接口的类,就需要重写接口中的方法
// 利用接口实现多继承
public class UserServiceImpl implements UserService,TimeService{
@Override
public void add(String name) {
}
@Override
public void delete(String name) {
}
@Override
public void update(String name) {
}
@Override
public void query(String name) {
}
@Override
public void timer() {
}
}
接口的作用:
- 约束
- 定义一些方法,让不同的人实现
- 方法默认为public abtract
- 属性默认为public static final
- 接口不能被实例化,接口中没有构造方法
- implements可以实现多个接口
- 必须要重写接口中的方法
内部类
内部类就是在一个类的内部再定义一个类,比如,A类中定义一个B类,那么B类相对A类来说就称为内部类,而A类相对于B类来说就是外部类了。
1.成员内部类
Outer.java
package oop.demo10;
public class Outer {
private int id = 10;
public void out(){
System.out.println("这是外部类的方法");
}
public class Inner {
public void in(){
System.out.println("这是内部类的方法");
}
// 内部类可以获得外部类的私有属性
public void getID(){
System.out.println(id);
}
}
}
Application.java
package oop;
import oop.demo10.Outer;
public class Application {
public static void main(String[] args) {
Outer outer = new Outer();
// 通过这个外部类来实例化内部类
Outer.Inner inner = outer.new Inner();
inner.in();
// 输出:这是内部类的方法
inner.getID();
// 输出:10
}
}
2.静态内部类
Outer.java
package oop.demo10;
public class Outer {
private int id = 10;
public void out(){
System.out.println("这是外部类的方法");
}
public static class Inner {
public void in(){
System.out.println("这是内部类的方法");
}
// 静态内部类无法直接访问外部类非静态的属性
// 内部类为static时,无法访问
// public void getID(){
// System.out.println(id);
// }
}
}
3.局部内部类
Outer.java
package oop.demo10;
public class Outer {
// 局部内部类
public void method(){
class Inner{
public void in(){
}
}
}
}
4.匿名内部类
Test.java
package oop.demo10;
public class Test {
public static void main(String[] args) {
// 没有名字来初始化类,不用将实例保存到变量中
new Apple().eat();
new UserService(){
@Override
public void hello() {
}
};
}
}
class Apple{
public void eat(){
System.out.println(1);
}
}
interface UserService{
void hello();
}
异常机制
异常发生在程序运行期间,它影响了正常的程序执行流程。
三种类型的异常:
- 检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法遇见的。
- 运行时异常:运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
- Error错误:错误不是异常,而是脱离程序员控制的问题。例如,当栈溢出时,一个错误就发生了,它们在编译时检查不到。
Java把异常当作对象来处理,并定义一个基类java.lang.Throwable作为所有异常的超类。
在Java API中已经定义了许多异常类,这些异常类分类两大类,错误Error和异常Exception。
ERROR:
- ERROR类对象由Java虚拟机生成抛出,大多数错误与代码编写者所执行的操作无关。
- Java虚拟机运行错误(Virtual MachineError),当JVM不再有继续执行操作所需的内存资源时,将出现OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。
- 发生在虚拟机试图执行应用时,如类定义错误(NoClassDefFoundError)、链接错误(LinkageError)。这些错误是不可查的,因为它们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况。
Exception:
- 在Exception分支中有一个重要的子类RuntimeException(运行时异常):
- ArrayIndexOutOfBoundsException(数组下标越界)
- NullPointerException(空指针异常)
- ArithmeticException(算术异常)
- MissingResourceException(丢失资源)
- ClassNotFoundException(找不到类)等异常,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。
- 这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
- Error和Exception的区别:Error通常是灾难性的致命的错误,是程序无法控制和处理的,当出现这些异常时,Java虚拟机(JVM)一般会选择终止线程;Exception通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这些异常。
异常处理机制:
- 抛出异常
- 捕获异常
异常处理的5个关键字:
try、catch、finally、throw、throws
Test.java
package exception;
public class Test {
public static void main(String[] args) {
int a = 1;
int b = 0;
try { // 监控区域
System.out.println(a/b);
}catch (ArithmeticException e){ // catch捕获异常,(想要捕获的异常类型)
System.out.println("程序出现异常,变量b不能为0");
}finally { // 处理善后工作,不管是否出现异常都会执行
System.out.println("finally");
}
}
}
throw、throws主动抛出异常,throw一般用在方法中,throws用在方法上。
Test.java
package exception;
public class Test {
public static void main(String[] args) {
// 此处的算术异常ArithmeticException属于运行时异常,正常情况下此异常不需要程序抛出,会自动抛出
try {
new Test().test(1, 0);
} catch (ArithmeticException e) {
throw new RuntimeException(e);
}
}
// throws 假设在这个方法中,处理不了这个异常,方法上抛出异常
public void test(int a,int b) throws ArithmeticException{
if (b==0){
throw new ArithmeticException();// throw 主动抛出异常,一般在方法中使用
}
System.out.println(a/b);
}
}
捕获多个异常,可以通过写多个catch来实现,层级结构高的异常写在后面,finally可以没有。
CTRL+ALT+T,选择try catch,可以生成对应代码块
自定义异常
用户自定义异常,只需要继承Exception类。
在程序中使用自定义异常,分为以下步骤:
- 创建自定义异常类。
- 在方法中通过throw关键字抛出异常对象。
- 如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理;否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常,继续下一步操作。
- 在异常方法的调用者中捕获并处理异常。
MyException.java
package exception.demo02;
// 自定义的异常类
public class MyException extends Exception {
// 传递数字,若>10,则抛出异常
private int detail;
public MyException(int a) {
this.detail = a;
}
// toString,进行输出,ALT+INSERT-toString
// 异常的打印信息
@Override
public String toString() {
return "MyException{" + detail + '}';
}
}
Test.java
package exception.demo02;
public class Test {
// 可能会存在异常的方法
static void test(int a) throws MyException {
System.out.println("传递的参数为:"+a);
if(a>10){
throw new MyException(a);
}
System.out.println("OK");
}
public static void main(String[] args) {
try {
test(11);
} catch (MyException e) {
System.out.println("MyException:"+e);
}
}
/**
* 输出:
* 传递的参数为:11
* MyException:MyException{11}
*/
}
异常处理经验:
- 处理运行异常时,采用逻辑去合理规避,同时辅助try-catch处理
- 在多重catch块后面,可以加一个catch(Exception)来处理可能会被遗漏的异常
- 对于不确定的代码,也可以加上try-catch,处理潜在的异常
- 尽量去处理异常,切忌只是简单调用printStackTrace()去打印输出
- 具体如何处理异常,要根据不同的业务需求和异常类型去决定
- 尽量添加finally语句块去释放占用的资源(如在IO流中)
IDEA快捷键
1.注:
public static void main(String[] args) {
}
的IDEA快捷键为psvm
System.out.println();
的IDEA快捷键为sout,可以直接输出内容.sout
3.CTRL+D复制当前行到下一行
4.100.for
生成
for (int i = 0; i < 100; i++) {
}
fori
生成
for (int i = 0; i < ; i++) {
}
ALT+INSERT-点Constructor-选中-OK,生成有参构造器。
ALT+INSERT-点Constructor-选中-Select None,生成无参构造器。
ALT+INSERT-Getter and Setter-选中属性-OK,自动生成get和set方法。
6.CTRL+H显示当前类继承树
7.重写快捷键:ALT+INSERT,选择override。
8.CTRL+ALT+T,选择try catch,可以生成对应代码块
9.选中变量,SHIFT+F6,进行更改,回车,所有使用到的地方便都进行了修改。