预备知识
Windows常用快捷键
Ctrl+C:复制
Ctrl+V:粘贴
Ctrl+A:全选
Ctrl+X:剪切
Ctrl+Z:撤销
Ctrl+S:保存
Alt+F4:关闭
Shift+Delete:永久删除
Win+E:打开此电脑
Ctrl+Shift+Esc:打开任务管理器
Win+Tab:切换应用程序
Idea常用快捷键
Ctrl+Alt+O 优化导入的类和包
Alt+Insert 生成代码(如get,set方法,构造函数等) 或者右键(Generate)
Ctrl+Alt+T 生成try catch
ALT+回车 导入包,自动修正
CTRL+SHIFT+SPACE 自动补全代码
CTRL+ALT+L 格式化代码
CTRL+ALT+I 自动缩进
Ctrl+D 复制行
Ctrl+X 删除行
CTRL+Z 倒退(撤销)
CTRL+Shift+Z 反撤销
Ctrl+Shift+上下键 上下移动代码
基本Dos命令
Win+R 输入cmd 打开控制台窗口
鼠标右键为粘贴(不能用Ctrl+C Ctrl+V)
//盘符与文件的切换
D: E: C: F: 盘符切换
cd /d E:\xxx 跨盘符切换文件夹
dir 查看当前目录下所有文件
cd .. 返回上一级
cd xxx 进入xxx文件夹
//清理与退出
cls (clear screen)清理屏幕
exit 退出终端
//查看电脑ip
ipconfig 查看电脑ip
//打开应用
calc 打开计算器
mspaint 打开画图工具
notepad 新建记事本
ping www.baidu.com 获得百度的ip地址
//文件操作
md 创建文件夹
rd 删除文件夹
cd>a.txt 创建a.txt文件
del a.txt 删除a.txt文件
Java基础
JDK、JRE、JVM
JDK:Java Development Kit Java开发工具
JRE:Java Runtime Environment Java运行环境
JVM:Java Virtual Machine Java虚拟机
JDK------包含------>JRE------包含------> JVM
数据类型
Java是一种强类型语言,强类型语言要求变量的使用要严格符合规定,所有的变量都需要先定义后才能使用。
Java的数据类型分为两大类
- 基本类型
- 数值类型
- 整数类型
- byte 占1个字节 范围:-128-127
- short 占2个字节 范围:-32768-32767
- int 占4个字节 范围:-2147483648-2147483647
- long 占8个字节 范围:-9223372036854775808-9223372036854775807
- 浮点类型
- float 占4个字节
- double 占8个字节
- 字符类型char 占2字节(0~65535)
- 整数类型
- boolean(布尔)类型:占1位其值只有true和false两个
- 数值类型
- 引用类型
- 类
- 接口
- 数组
数据类型扩展知识
二进制(逢2进1):2 = 0010
十进制(逢10进1):10 = 10
八进制(逢8进1):8 = 010
十六进制(逢16进1):16 = 0x10
最好完全避免使用浮点数(float、double)进行比较,浮点数有限、离散,有舍入误差。
所有的字符本质还是数字,由Unicode编码决定(16位2进制,一共有65536个字符)。
\t 制表符 \n 换行符
1Byte = 8 bit (1bit = 1位,8位 = 1字节)
1KB = 1024 Byte
1MB = 1024 KB
1GB = 1024 MB
1TB = 1024 GB
类型转换
由于Java是强类型的语言,所以要进行有些运算时,需要用到类型转换
低----------------------------------------------------------->高
byte,short,char,int,long,float,double
运算中,不同类型的数据先转化为同一类型再进行运算。低到高自动转,高到低强制转,浮点数变整数时小数部分消失。
注意点:
- 不能对布尔值进行转换。
- 不能把对象类型转换为不相干的类型。
- 转换时可能存在内存溢出,或者精度问题。
jdk1.7之后新特性,数字之间可以用下划线进行分割
int money = 10_0000_0000;
变量、常量、作用域
静态变量:声明在类里方法外外,加static,static修饰的成员变量为静态变量或全局变量,不依赖特定实例,而是被所有实例共享。调用时直接用类名调用。
static double salary = 2500;
public static void main(String[] args) {
//在方法中调用静态变量
System.out.println(salary);
}
实例变量(成员变量):声明在类里方法外,布尔值默认为false,基本类型默认为0,其他默认都为null。
该变量作用范围与类的实例化对象作用范围相同,当类被实例化时,成员变量就会在内存中分配空间并初始化,直到这个实例化对象的生命周期结束时,实例变量(成员变量)的生命周期才会结束。需要将类实例化后才能调用。
package com.luo.base;
public class Demo08 {
String name;
int age;
public static void main(String[] args) {
Demo08 demo08 = new Demo08();//调用时需先创建对象
System.out.println(demo08.age);//0
System.out.println(demo08.name);//null
}
}
局部变量:声明在方法内,作用范围由方法的括号决定。
public static void main(String[] args) {
int i = 10;
System.out.println(i);//10
}
//其他方法
public void add(){
//System.out.println(i); 无法调用局部变量
}
驼峰原则:
实例变量:首字母小写,后面单词字母大写。 int birthDay;
局部变量:首字母小写,后面单词字母大写。 int birthDay;
方法名:首字母小写,后面单词字母大写。 Public void addNum(){}
常量:所有字母大写以及下划线。 final double PI = 3.14;
类名:每一个单词首字母都大写。 class Inner{}
基本运算符
-
算术运算符:+,-,*,/,%,++,–
-
赋值运算符:=
-
关系运算符(返回布尔值):>,<,>=,<=,==,!=,instanceof(判断A对象是否为B对象的子类对象)
-
逻辑运算符:&&逻辑与,||逻辑或,! 非
-
位运算符:1. &(与,两个都为1时才为1),
2. |(或,有一个为1时就为1),
3. ^(异或,相同为0,不同为1),
4. ~(按位取反),
5. >>(二进制右移一位,则十进制除以2) ,
6. <<(二进制左移一位,则十进制乘以2)
- 扩展赋值运算符:+=,-=,*=,/=
特殊情况:字符串连接
//输出内容里有String类型时会把后面全部转换成String类型输出,前面的则依旧会计算。
int a = 10;
int b = 20;
//字符串连接符 + String
System.out.println(a+b);//30
System.out.println(""+a+b);//输出1020,有String类型则把后面全部转换成String类型
System.out.println(a+b+"");//输出30,前面的依旧会计算
- 三元运算符:
//x ? y : z
//如果x==true 则结果为y 否则结果为z
int score = 50;
String type = score <60 ? "不及格":"及格";//必须掌握
System.out.println(type);//输出为不及格
Java流程控制
Scanner的用法
通过Scanner,我们可以获取用户的输入
基本语法:
- 首先创建一个扫描器对象,用于接收键盘数据
Scanner s = new Scanner(System.in);
- 通过Scanner类的next()与nextLine()方法获取输入的字符串,在读取前我们一般需要使用hasNext()与hasNextLine()来判断是否还有输入的数据。
next():
1.读取到有效字符后会结束输入。
2.对输入有效字符之前遇到的空白,next()方法会自动将其去掉
3.只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符。
4.next()不能得到带有空格的字符串
nextLine():
1.以Enter为结束符,也就是说nextLine()方法返回输入回车之前的所有字符。
2.可以获得空白。
代码演示:
//有判断语句的
Scanner scanner = new Scanner(System.in);//从键盘接收数据
System.out.println("使用nextLine方式接收");
//判断是否还有输入
if (scanner.hasNextLine()){
String str = scanner.nextLine();
System.out.println("输出的内容为"+str);
}
//没有判断语句的
Scanner scanner = new Scanner(System.in);//从键盘接收数据
String str = scanner.nextLine();
System.out.println("输出的内容为:"+str);
顺序结构
Java的基本结构就是顺序结构,除非特别指明,否则就按照顺序一句一句,从上到下地执行。
选择结构
选择结构有if选择结构、Switch选择结构。
if选择结构
- if单选择结构
if(布尔表达式){
//如果布尔表达式为true将执行的语句
}
- if双选择结构
if(布尔表达式){
//如果布尔表达式的值为true
}else{
//如果布尔表达式的值为false
}
- if多选择结构
if(布尔表达式){
//如果布尔表达式的值为true执行代码
}else if(布尔表达式2){
//如果布尔表达式2的值为true执行代码
}else if(布尔表达式3){
//如果布尔表达式3的值为true执行代码
}else {
//如果以上布尔表达式都不为true执行代码
}
- 嵌套的if结构
if(布尔表达式1){
//如果布尔表达式1的值为true执行代码
if(布尔表达式2){
//如果布尔表达式2的值为true执行代码
}
}
Switch选择结构
switch case语句判断一个变量与一系列值中某个值是否相等,每个值称为一个分支。
switch语句中的变量类型可以是:byte、short、int、char,JavaSE7开始支持String类型。同时case标签必须为字符串常量或者字面量。
//case穿透:没有break时以下全部输出 //switch匹配一个具体的值
char grade = 'C';
switch (grade){
case 'A':
System.out.println("优秀");
break;
case 'B':
System.out.println("良好");
break;
case 'C':
System.out.println("及格");
break;
case 'D':
System.out.println("挂科");
break;
default:
System.out.println("未知等级");//都匹配不到时输出default里的语句
}
循环结构
while循环
while是最基本的循环,它的结构为
while(布尔表达式){
//循环内容
}
//只要布尔表达式为true,循环就会一直执行下去,直到为false停止。若一直为true则陷入死循环,正常业务应该尽量避免死循环,否则会造成程序卡死崩溃
do…while循环
对于while循环而言,若不满足条件则不能进入循环,但有时我们需要即使不满足条件,也至少执行一次,此处就会用到do…while循环
do{
//循环内容
}while(布尔表达式);
/*
While和do...while的区别:
1.while是先判断后执行,do...while是先执行后判断
2.Do...while总是保证循环体会被至少执行一次,这是他们的主要差别*/
for循环
for循环语句是支持迭代的一种通用结构,是最有效也最灵活的循环结构。
for循环执行的次数是在执行前就确定的,语法格式如下:
for(初始化;布尔表达式;更新){
//循环语句
}
增强for循环
//快捷输入:foreach
for(声明语句 : 表达式)
{
//循环语句
}
break、continue
break:在任何循环语句的主体部分,均可用break控制循环的流程。break用于强行退出循环,不执行循环中剩余的语句。(break语句也可以在switch语句中使用)
continue:continue用在循环语句体中,用于终止某次循环过程,即跳过循环体中尚未执行的语句,接着进行下一次是否执行循环的判定。
Java方法
Java方法是语句的集合,它们在一起执行一个功能
- 方法是解决一类问题的步骤的有序结合
- 方法包含于类或对象中
- 方法在程序中被创建,在其他地方被引用
方法的定义与调用
方法包含一个方法头和一个方法体,下面是一个方法的所有部分
- 修饰符:修饰符是可选的,告诉编译器如何调用该方法,定义了该方法的访问类型,常用访问控制修饰符有:public、protected、private,非访问控制修饰符有abstract、static、final
- 返回值类型:方法可能会返回值,类型为对应的数据类型,没有返回值的方法返回值类型为void
- 参数类型:当方法被调用时,传递值给参数,参数可以分为实参和形参。参数是可选的,方法可以不包含任何参数。
- 方法体:方法体包含具体的语句,定义该方法的功能
修饰符 返回值类型 方法名(参数类型 参数名){
...
方法体
...
return 返回值;
}
举例:
//main方法
public static void main(String[] args) {
//实际参数:实际调用传递给他的参数
int sum = add(1, 2);//调用方法:对象名.方法名(实参列表)
System.out.println(sum);
//当方法返回一个值的时候,方法调用通常被当作一个值
//若方法返回值是void,方法调用一定是一条语句
}
//形式参数,用来定义作用的
public static int add(int a,int b){
return a+b;
}
方法的重载
重载就是在一个类中,有相同的函数名称,但形参不同的函数。
方法的重载规则:
1.方法名称必须相同
2.参数列表必须不同(个数不同、或类型不同、参数列表排序不同)
3.方法的返回类型可以相同也可以不同
4.仅仅返回类型不同不足以成为方法的重载
实现理论:方法名称相同时,编译器会根据调用方法的参数个数、参数类型等去逐个匹配,以选择对应的方法,如果匹配失败,则编译器会报错。
例子:数据比大小,相同返回0,不同则返回较大的数,int和double方法都有
public static void main(String[] args) {
//方法重载 方法名相同 参数列表不同
int max = max(10, 10);//调用时根据传入的实参自动匹配对应的方法
System.out.println(max);
double max2 = max(10.0,20.0);
System.out.println(max2);
}
//int比大小
public static int max(int num1,int num2){
int result = 0;
if (num2 == num1){
System.out.println("num1==num2");
return 0;//return 0 为终止方法
}
if (num1 > num2){
result = num1;
}else {
result = num2;
}
return result;
}
//double比大小
public static double max(double num1,double num2){
double result = 0.0;
if (num2 == num1){
System.out.println("num1==num2");
return 0;//终止方法
}
if (num1 > num2){
result = num1;
}else {
result = num2;
}
return result;
}
命令行传参
有时我们希望运行一个程序时再传递给它消息,这要靠传递命令行参数给main()函数实现。
public class Demo03 {
public static void main(String[] args) {//main方法也可以传递参数
//args.length 数组长度
for (int i = 0; i < args.length ; i++) {
System.out.println("args["+i+"]:"+args[i]);
}
}
}
将此代码在控制台窗口用命令运行
需要找到包的路径再运行,否则不可运行
可变参数
jdk1.5开始,Java支持传递同类型的可变参数给一个方法。可变参数中,同类型的参数数量可以随意变换。
在方法声明中,在指定参数类型后面加一个省略号(…)。
一个方法中只能定义一个可变参数,它必须在参数列表的最后面,任何普通的参数必须在它之前声明。
public static void main(String[] args) {
Demo04 demo04 = new Demo04();
demo04.test(1,2,3,4,5,6,2);
}
public void test(int x,int... i){//只需加三个点...,且必须定义在参数列表的最后面
System.out.println(x);
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方法中调用B方法我们容易理解,递归则是在A方法中调用A方法。
利用递归可以用简单的程序解决一些复杂的问题,通常把一个大型复杂问题转化为一个与原问题相似的规模较小的问题来解决。递归只需少量的程序就可描述出解题过程所需要的多次重复计算,大大减少了程序的代码量。
递归的结构包括两个部分:
- 递归头:什么时候不调用自身方法,如果没有头,将陷入死循环
- 递归体:什么时候需要调用自身方法
//举个例子:阶乘
public static void main(String[] args) {
System.out.println(f(10));
}
public static int f(int n){
if (n==1){
return 1;
}else {
return n*f(n-1);
}
}
Java数组
数组是相同类型数据的有序集合。
数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成。
其中每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问他们。
数组的声明和创建
声明数组变量的语法:
//方法一(常用方法)
int[] nums;//声明一个数组,此时数组并不存在
nums = new int[5];
//方法二(不常用)
int nums[];
//方法三
int[] nums = new int[5];//把声明和创建写在一起,5表示内存有5个元素
//给数组元素中赋值,没被赋值的元素默认值为0
nums[0] = 1;
nums[1] = 2;
nums[2] = 3;
nums[3] = 4;
nums[5] = 6;
三种初始化及内存分析
Java内存分析:
- 堆:存放new的对象和数组。可以被所有的线程共享,不会存放别的对象引用。
- 栈:存放基本变量类型(会包含这个基本类型的具体数值)、引用对象的变量(会存放这个引用在堆里面的具体地址)
- 方法区:可以被所有的线程共享、包含了所有的class和static变量
数组的内存分析:
int[] nums;//数组在声明过后,会在栈中压入一个名为"nums"的引用,但数组并不存在
nums = new int[5];//经过new关键字创建过后,会在堆中开辟一处有5个小块的内存,此时数组才存在,数组没有初始化时每个数都为初始值0
三种初始化:
- 静态初始化
//静态初始化:创建+赋值
int[] a = {1,2,3,4,5,6,7,8};
- 动态初始化
//动态初始化包含了默认初始化
int[] b = new int[8];
- 默认初始化
数组是引用类型,它的元素相当于类的实例变量,所以在分配空间过后,所有的元素都像初始化实例变量一样被隐式地初始化
下标越界及小结
数组的四个基本特点:
- 数组长度是确定的,数组一旦被创建,它的大小是不可以改变的
- 数组的元素必须是相同类型,不能出现混合类型。
- 数组中的元素可以是任何数据类型,包括基本类型和引用类型
- 数组变量属于引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量。数组本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中的。
数组边界:
数组下标的合法区间:[0,length-1],如果越界就会报错;
int[] a = new int[2];
System.out.println(a[2]);
//此时会报错:ArrayIndexOutOfBoundsException:数组下标越界异常
小结:
-
数组是相同数据类型(数据类型可以为任意类型)的有序集合
-
数组也是对象,数组元素相当于对象的成员变量
-
数组长度是确定的,不可变的,如果越界,则报: ArrayIndexOutofBounds
多维数组
多维数组可以看成数组的数组,比如二维数组就是一个特殊的一维数组,其中每个元素都是一个一维元素。
二维数组的定义:
//定义一个三行两列的数组
//方法一
int a[][] = new int[3][2];
//方法二
/*
a[0]={1,2}
a[0][0]=1 、 a[0][1]=2
a[1]={2,3}
a[1][0]=2 、 a[1][1]=3
a[2]={3,4}
a[2][0]=3 、 a[2][1]=4
*/
int[][] a = { {1,2} , {2,3} , {3,4} };
//关于数组长度和下标
a.length=3
a[0].length=2
Arrays类
数组的工具类为java.util.Arrays
由于数组对象本身并没有什么方法可以供我们调用,但API中提供了一个工具类Arrays供我们使用,从而可以对数据对象进行一些基本的操作
Arrays类中的方法都是static修饰的静态方法,在使用的时候可以直接使用类名进行调用,而不用(是"不用"不是"不能")
Arrays类中具有以下常用功能
- 打印数组元素
System.out.println(Arrays.toString(a))//打印数组元素
//System.out.println(a)//这里仅仅打印数组的内存地址值
//重复造轮子
public static void printArray(int[] a){
for (int i = 0; i < a.length; i++) {
if (i==0){
System.out.print("[");
}
if (i==a.length-1){
System.out.print(a[i]+"]");
}else{
System.out.print(a[i]+", ");
}
}
}
- 给数组赋值:通过fill方法
Arrays.fill(a,val:0);//把该数组全部赋值为0
Arrays.fill(a,fromIndex:2,toIndex:4,val:0);//把数组中2到4的元素赋值为0(含头不含尾
- 给数组排序(升序):通过sort方法
Arrays.sort(a);
- 比较数组:通过equals方法比较数组中元素值是否相等
System.out.println(Arrays.equals(a,b));//比较两数组元素是否相等
System.out.println(a.equals(b));//这里比较的是两数组的内存地址值
冒泡排序
冒泡排序的逻辑:
两层循环,外层冒泡轮数,里面依次比较。若对n个数进行排序,则比较的次数为(n-1)+(n-2)+…+1 = n*(n-1)/2。
每一轮比较,都会产生一个最大,或者最小的数字。
每一轮比较,都比上一轮少比较一次。依次循环到结束
public static int[] sort(int[] array){
//临时变量,交换元素值时起作用
int temp = 0;
//外层循环 判断我们这个要走多少次
for (int i = 0; i < array.length-1; i++) {
boolean flag = false;//通过falg标识位减少没有意义的比较
//内层循环 判断两个数,如果第一个数比第二个数大,则交换两数位置
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;//经过一轮比较后若flag依旧是false,说明数组为有序数组,接下来的比较都不用进行了,所以退出外层循环
}
}
return array;
}
面向对象(OOP)
什么是面向对象
面向过程思想(执行者):
步骤清晰简单,第一步做什么…第二步做什么…,面向过程适合处理一些较为简单的问题如:把大象装冰箱。第一步,打开冰箱门;第二部,把大象放进去;第三步,关上冰箱门
面向对象思想(指挥者):
分类的思维模式,思考问题首先思考问题需要哪些分类,再对这些分类进行单独思考。最后才对某个分类下的细节进行面向对象的思索。适合处理复杂的、适合多人协作的问题。如:把大象装冰箱。什么样的冰箱?什么样的大象?谁负责把大象装进去?而不是关注那个负责人怎么把大象装进去
面向对象编程的本质就是:以类的方式组织代码,以对象的方式封装数据
面向对象三大特性:
- 封装
- 继承
- 多态
从认识论角度考虑是先有对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽象。而从代码运行的角度考虑是先有类后有对象,类是对象的模板
值传递与引用传递的区别
值传递(形式参数类型是基本数据类型):方法调用时,实际参数把它的值传递给对应的形式参数,形式参数只是用实际参数的值初始化自己的存储单元内容,他俩是两个不同的存储单元,所以方法执行中形式参数值的改变不影响实际参数的值
引用传递(形式参数类型是引用数据类型参数):也成为传地址,方法调用时,实际参数是对象(或数组),这时实际参数与形式参数指向同一个地址。在方法执行时,对形式参数的操作实际上就是对实际参数的操作,这个结果在方法结束后被保留下来,所以方法执行中形式参数的改变将会影响实际参数。
代码举例:
//值传递
public static void main(String[] args) {
int a = 1;
Demo04.change(a);
System.out.println(a);//此时输出的a仍然为1
}
//返回值为空
public static void change(int a){
a = 10;
}
//引用传递
public static void main(String[] args) {
Person person = new Person();//初始化的person.name
Demo05.change(person);
System.out.println(person.name);//此时输出的内容为"罗骏嵩"
}
public static void change(Person person){
//person是一个对象 指向的是一个具体的人,可以改变属性
person.name = "罗骏嵩";
}
类与对象的创建
类与对象的概念
类是一种抽象的数据类型,它是对某一类事物的整体描述/定义,但是并不能代表某一个具体的事物。
对象是抽象概念的实例,如张三是人的一个具体实例,张三家里的旺财是狗的一个实例。
创建与初始化对象
创建对象时,需使用new关键字创建对象
使用new关键字创建对象时,除了分配内存空间之外,还会给创建好的对象进行默认的初始化 以及对类中构造器的调用
类中构造器也称为构造方法,创建对象时必须调用,并且构造器有以下两个特点:
- 必须和类名相同
- 必须没有返回值,但也不能写void
构造器
构造器的规则:和类名相同且没有返回值
构造器的作用:1.使用new关键字,本质是在调用构造器 2.初始化一些对象的值
注意点:无参构造默认存在,但定义了有参构造之后,如果想使用无参构造,必须显示地定义一个无参构造,不然就没有无参构造。
Alt+Insert 快速定义构造器
//定义类
public class Person{
String name;
public Person(){//无参构造
}
public Person(String name){//有参构造
this.name = name;//this.name表示当前类的name,后面的name为传进来的形参
}
}
//main方法
public static void main(String[] args) {
//new 实例化了一个对象
Person person = new Person("罗骏嵩");
System.out.println(person.name);//null
}
创建对象的内存分析
内存可简单分为堆、栈,方法区在堆中
类在创建时首先存放在堆的方法区中,静态数据陪同类一起加载到静态方法区,可供所有的对象调用。
类生成的对象放在堆里,对象的引用放在栈里
简单小结类、对象、方法
1.类与对象
类是一个模板,一种抽象,而对象是一个类的具体实例
2.方法
方法是用来定义、调用的
3.对象的引用
引用的类型:基本类型(8个)
对象是通过引用来操作的:栈:栈中存放对象的引用--->堆:真实的对象放在堆中
4.对象的属性:指对象中的成员变量(实例变量)
属性在创建时会默认进行初始化:
数字初始化为0,浮点型数字一般初始化为0.0
char类型一般初始化为u0000
boolean类型一般初始化为false
引用类型一般初始化为null
创建属性:修饰符 属性类型 属性名 = 属性值; int a = 0;
5.对象的创建和使用
必须使用new关键字创建对象(创建时自动调用类中的构造器)
一般语句:Person kuangshen = new Person();
调用对象的属性 kuangshen.name
调用对象的方法 kuangshen.sleep()
6.类
类中只含有以下两种元素:
一是静态的属性:属性
二是动态的行为:方法
三大特性之一:封装
- 该露的露,该藏的藏
程序设计要求“高内聚,低耦合”。高内聚就是类的内部数据操作细节由自己完成,不允许外部干涉;低耦合就是仅暴露少量的方法给外部使用
- 封装(数据的隐藏)
通常,应该禁止直接访问一个对象中数据的实际表示,而应该通过操作接口来访问,这成为信息隐藏
- 只需记住这句话:属性私有(private),get/set
public class Student {
//属性私有 用private
private String name;//名字
//get获得这个数据
public String getName(){
return this.name;
}
//set给这个数据设置值
public void setName(String name){
this.name = name;
}
}
//在main方法中测试
public static void main(String[] args) {
Student s1 = new Student();//首先new一个对象
s1.setName("罗骏嵩");//调用set方法给name设置值
System.out.println(s1.getName());//调用get方法得到这个name
}
//alt + insert 自动生成get/set方法
三大特性之二:继承
继承的本质是对某一批类的抽象,从而实现世界更好的建模。
extends的意思是“扩展”。子类是父类的扩展。
Java中只有单继承,没有多继承!(一个儿子只能有一个爸爸,一个爸爸可以有多个儿子)
继承是类与类之间的一种关系,继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示。
子类和父类之间,从意义上讲应该具有“is a”的关系
子类继承了父类,就会拥有父类的全部方法
父类的private元素不能被子类拥有,需要使用get、set方法才能被子类使用
在Java中,所有的类,都默认直接或者间接继承Object类
使用 ctrl + h 可以查看继承树
//人 父类
//在java中 所有的类 都直接或间接继承object类
public class Person {
private int money = 10_0000_0000;//private私有属性
public void say(){
System.out.println("说了一句话")
}
public int getMoney(){
return money;
}
public void setMoney(int money){
this.money = money;
}
}
//学生是人 子类
//子类继承了父类 就会拥有父类的全部方法
public class Student extends Person{
}
super
super调用变量:
//父类
public class Person{
protected String name = "kuangshen";
}
//子类
public class Student extends Person{
private String name = "qinjiang";
public void test(String name){
System.out.println(name);//打印的是传入的参数"秦疆"
System.out.println(this.name);//打印的是本类的实例变量"qinjiang"
System.out.println(super.name);//打印的是父类的变量"kuangshen"
}
}
//测试类
public class Application{
public static void main(String[] args){
Student student = new Student();//创建子类对象
student.test(name:"秦疆");//调用子类方法并传参
}
super调用构造方法:
//父类
public class Person(){//若写成了有参构造,则子类无法调用,必须手动添加无参构造
public Person(){//父类无参构造
System.out.println("Person"无参执行了);
}
}
//子类
public class Student extends Person{
public Student(){
//子类无参会自动调用父类无参(有个隐藏代码:super();),有参也是
System.out.println("Student"无参执行了);
}
}
//测试类
public class Application{
public static void main(String[] args){
Student student = new Student();//创建对象时会自动调用无参构造
}
}
super总结:
-
super注意点
1. super调用父类构造方法,必须在构造方法的第一个
2. super必须只能出现在子类的方法或者构造方法中
3. super和this不能同时调用构造方法 -
super和this相比:
- 代表的对象不同
this: 本身调用的这个对象
super: 代表父类对象的引用 - 二者使用的前提:
this: 没有继承也可以使用
super: 只能在继承条件下才可以使用 - 二者调用的构造方法:
this():本类的构造
super():父类的构造
- 代表的对象不同
重写
重写都是方法的重写,和属性无关
//父类
public class B {
public void test(){
System.out.println("B=>test()");
}
}
//子类
public class A extends B{
//override 重写
@Override//注解 有功能的注释
public void test() {
System.out.println("A=>test");
}
}
//测试类
public class Application{
//静态方法和非静态方法区别很大
//静态方法是类的方法,非静态方法是对象的方法
//有static时,b调用了B类的方法,因为b是用B类定义的
//没有static时,b调用的是对象的方法,而b是用A类new出来的,所以为A=>test()
public static void main(String[] args){
//方法的调用只和左边,定义的数据类型有关
A a = new A();
a.test();//A=>test()
//父类的引用指向了子类
B b = new A();//子类重写了父类的方法
b.test();//A=>test()。A、B为静态时为B=>test()
}
}
重写总结:需要有继承关系。子类重写父类的方法!
1. 方法名必须相同
2. 参数列表必须相同(否则就是重载了)
3. 修饰符:范围可以扩大但不能缩小:public > protected > default > private
4. 抛出的异常:异常的范围,可以被缩小,但不能扩大
5. 父类和子类的方法都为非静态
重写,子类的方法名、参数列表和父类必须一致,但方法体不同!
为什么需要重写:父类的功能,子类不一定需要,或者不一定满足
重写快捷键:Alt+Insert 之后点击Override
三大特性之三:多态
多态的含义:同一方法可以根据发送对象的不同而采用多种不同的行为方式。一个对象的实际类型是确定的,但可以指向对象的引用类型有很多
多态存在的条件:
- 有继承关系
- 子类重写父类方法
- 父类引用指向子类对象
//父类
public class Person{
public void run(){
System.out.println("run")
}
}
//子类
public class Student extends Person{
@Override
public void run() {
System.out.println("son");
}
public void eat(){
System.out.println("eat");
}
}
//测试类
public static void main(String[] args){
//一个对象的实际类型是确定的
//new Student();
//new Person();
//可以指向的引用类型就不确定了;比如父类的引用指向子类
//Student子类能调用的方法都是自己的或继承父类的
//Person父类型可以指向子类,但是不能调用子类独有的方法
Student s1 = new Student();
Person s2 = new Student();
Object s3 = new Student();
s2.run();//son 子类重写了父类的方法,执行子类的方法
s1.run();//son
s2.eat();//父类不能调用子类独有的方法
s1.eat();//eat
((Student)s2).eat();//将s2强制转换成Student子类可以调用子类独有方法
}
多态的注意事项:
- 多态是方法的多态,属性没有多态
- 必须是父类和子类之间才能存在多态(父类的引用指向子类)
- 多态的存在条件:一:继承关系 二:方法需要重写
- static静态方法属于类,不属于实例,不能重写。final属于常量,不能重写。private属于私有变量,不能重写。
instanceof关键字
instanceof关键字可以用来判断两个类之间知否存在父子关系
public class Person{//父类
}
public class Student extends Person{//子类1
}
public class Teacher extends Person{//子类2
}
public class Application{//测试类
public static void main(String[] args){
//System.out.println(X instanceof Y); //能不能编译通过 取决于X与Y之间是否存在父子关系
//Object --> String
//Object --> Person --> Student
//Object --> Person --> Teacher
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);//False
}
}
类型转换
//父类
public class Person {
public void run() {
System.out.println("run");
}
}
//子类
public class Student extends Person{
public void go(){
System.out.println("go");
}
}
//测试类
public class Application{
public static void main(String[] args){
//类型之间的转化: 父 子
//高 低
Person obj = new Student();
//student.go();//无法执行
//将student对象转换为Student类型,我们就可以使用Student类型的方法了
//低转高自动转
Person person = obj;//子类转换为父类可能会丢失一些方法
//高转低强制转
(Student)obj.go();//转换后就可执行
}
}
static关键字
被static修饰的元素为静态元素,随着类的加载而加载,不用创建对象就能访问
静态变量可以通过类名访问,非静态变量必须创建对象来访问,静态方法也同理
静态方法中可以访问静态方法,但不能访问非静态方法
非静态方法中可以访问静态方法
public class Student extends Person{
private static int age;//静态的变量
private double score;//非静态变量
public static void go(){//静态方法
//静态方法中可以访问静态方法,但不能访问非静态方法
}
public void run(){//非静态方法
go();//非静态方法中可以访问静态方法
}
public static void main(String[] args) {
Student s1 = new Student();
System.out.println(Student.age);//静态变量可以通过类名访问
//System.out.println(Student.score); 非静态变量不能通过类名访问
System.out.println(s1.age);//非静态变量只能通过创建对象来访问
System.out.println(s1.score);
Student.go();//可以直接通过类名访问静态方法
s1.run();//非静态方法只能通过创建对象来访问
}
}
public class Person {
//2 每次创建对象都要执行(可以用来赋初始值)
{
//代码块(匿名代码块)
System.out.println("匿名代码块");
}
//1 static只执行一次
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();/*打印出的是:
匿名代码块
构造方法*/
}
}
抽象类
abstract修饰符可以用来修饰方法也可以修饰类,如果修饰方法那么该方法就是抽象方法,如果修饰类,那么该类就是抽象类
抽象类中可以没有抽象方法,但有抽象方法的类一定要声明为抽象类
抽象类不能用来创建对象,只能继承。抽象方法只有方法的声明,没有方法的实现,需要靠子类来实现。
子类继承抽象类,就必须实现抽象类没有实现的抽象方法,否则该子类也为抽象类。
//抽象类
//abstract 抽象类只能单继承 接口可以多继承
public abstract class Action {
//abstract 抽象方法只有方法的名字 没有方法的实现
public abstract void doSomething();
//1.不能new这个抽象类,只能靠子类去实现它(相当于一种约束!)
//2.抽象类中可以写普通方法
//3.抽象方法必须在抽象类中(抽象的抽象 约束)
//抽象类存在的意义:把对象的公有属性抽象出来~ 提高开发效率
}
//子类
//继承了抽象类的子类都必须要实现抽象类的方法 除非子类也是抽象类
public class A extends Action{
@Override
public void doSomething() {
}
}
接口的定义与实现
普通类:只有具体实现
抽象类:具体实现和(规范)抽象方法都有
接口:只有规范!自己无法写方法专业的约束!约束和实现分离:面向接口编程
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是…则你必须能…”的思想,如果你是天使则必须能飞,如果你是汽车则必须能跑…
接口的本质是契约,就像人间的法律一样,制定好后大家都遵守
面向对象的精髓就是对对象的抽象,最能体现这一点的就是接口
//接口1
//interface 定义的关键字,接口都需要有实现类
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);//查
}
//接口2
public interface TimeService {
void timer();
}
//实现类
//类 可以实现接口 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 abstract
- 接口中的常量都是public static final
- 接口不能被实例化,接口中没有构造方法
- 可以实现多个接口 用implements关键字
- 必须重写接口中的方法
内部类的预习
内部类就是在一个类的内部再定义一个类,比如,A类中定义一个B类,那么B类相对A类来说就称为内部类,而A类相对B类来说就是外部类了
内部类分为:1.成员内部类 2.静态内部类 3.局部内部类 4.匿名内部类
//成员内部类
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);//10
}
}
}
//注意:一个java类中可以有多个class类,但只能有一个public class
//测试类
public class Application{
public static void main(String[] args){
Outer outer = new Outer();
//通过这个外部类来实例化内部类
outer.Inner inner = outer.new Inner();
inner.in();//调用内部类中方法
}
}
//局部内部类:方法里定义的类
public class Outer{
public void method(){
class Inner{
public void in(){
}
}
}
}
//匿名内部类
public class Test{
public static void main(String[] args){
//没有名字初始化类,不用将实例保存到变量中
new Apple().eat();
//这个也是匿名内部类(了解即可)
UserService userService = new UserService(){
@Override
public void hello(){
}
}
}
}
class Apple{
public void eat(){
System.out.println("1");
}
}
interface UserService{
void hello();
}
异常
软件程序再运行过程中非常可能遇到如硬盘满了、格式不对、输入不合规等类型的问题,我们叫做异常,英文是:Exception,意思是例外。
异常是指程序运行中出现的不期而至的各种状况,如:文件找不到、网络连接失败、非法参数等
异常发生在程序运行期间,它影响了正常的程序执行流程
异常分类:
- 检查性异常:用户错误或问题引起的异常,这是程序员无法预见的,例如要打开一个不存在的文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
- 运行时异常:运行时异常是可能被程序员避免的异常,与检查性异常相反,运行时异常可以在编译时被忽略
- 错误ERROR:错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译时也检查不到。
异常体系结构理论(了解)
Java把异常当作对象来处理,并定义一个基类java.lang.Throwable作为所有异常的超类
在JavaAPI中定义了许多异常类,这些异常类分为两大类,错误Error和异常Exception
Error:
- Error类对象由Java虚拟机生成并抛出,大多数错误与代码编写者所执行的攥作无关。
- Java虚拟机运行错误(Virtual MachineError),当JVM不再有继续执行操作所需的内存资源时,将出现OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止;
- 还有发生在虚拟机试图执行应用时,如类定义错误(NoQassDefFoundError)、链接错误 (LinkageError) .这些错误是不可查的,因为它们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况。
Exception:
-
在Exception分支中有一个重要的子类RuntimeException(运行时异常)
-
RuntimeException(运行时异常)包括如:ArraylndexOutOfBoundsException (数组下标越界)NullPointerException (空指针异常)ArithmeticException (算术异常)MissingResourceException (丢失资源)ClassNotFoundException (找不到类)等异常,这些异常是不检查异常,程序中可以选 择捕获处理,也可以不处理。
-
这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生;
Error和Exception的区别:Error通常是灾难性的致命的错误,是程序无法控制和处理的,当出现这些异常时,Java虚拟机( JVM) 一般会选择终止线程;Exception通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这些异常。
异常处理机制
- 抛出异常
- 处理异常
异常处理五个关键字:try、catch、finally、throw、throws
//简单例子
public static void main(String[] args) {
int a =1;
int b = 0;
try {//try监控区域
System.out.println(a/b);
}catch (ArithmeticException e) {//catch(想要捕获的异常类型) 捕获异常
System.out.println("程序出现异常,变量b不能为0");
}finally{//处理善后工作,不管异常捕获与否,finally都会执行
System.out.println("finally");
}//可以不要finally,但try、catch必须有 finally可以用来关闭io口
}
//假设要捕获多个异常,则需从小到大去捕获
public static void main(String[] args) {
int a = 1;
int b = 0;
try {//try监控区域
System.out.println(a/b);
}catch (Error e){//catch(想要捕获的异常类型) 捕获异常
System.out.println("程序出现异常,变量b不能为0");
}catch (Exception e){
System.out.println("Exception");
}catch (Throwable t){
System.out.println("Throwable");
} finally {//处理善后工作
System.out.println("finally");
}
}
//可以用Ctrl+Alt+T生成try、catch框架
//catch中除了e.printStackTrace();打印错误的栈信息,还可以添加一些自己的语句,比如System.exit(1);让当前系统结束
//主动抛出异常
public static void main(String[] args) {
try {//首先捕获异常
test(1,0);
} catch (ArithmeticException e) {
e.printStackTrace();
}
}
//假设这个方法中处理不了这个异常,就需要在方法上抛出异常 用throws
public void test(int a,int b) throws ArithmeticException{
if (b == 0){//此时即使没有System.out.println(a/b);方法体,也会将异常抛出
throw new ArithmeticException(); //主动抛出异常(在方法中用throw),一般在方法中使用
}
}
//算数异常属于运行时异常,正常情况下会自动抛出。在没用try、catch时遇到异常程序会主动停止,用了try、catch后程序会依旧正常往下执行
自定义异常
使用Java内置的异常类可以描述在编程时出现的大部分异常情况。除此之外,用户还可以自定 义异常。用户自定义异常类,只需继承Exception类即可。
在程序中使用自定义异常类,大体可分为以下几个步骤:
1 .创建自定义异常类。
2 .在方法中通过throw关键字抛出异常对象。
3 .如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理;否则在方法
的声明处通过throws关健宇指明要抛出给方法调用者的异常,继续进行下一步操作。
4 .在出现异常方法的调用者中捕获并处理异常。
举例:传入的参数>10时抛出异常
//自定义的异常类
public class MyException extends Exception{
//传递数字>10时抛出异常
private int detail;//同Exception里的message
public MyException(int a) {//构造器
this.detail = a;
}
//toString: 异常的打印信息
@Override
public String toString() {
return "MyException{" + detail + '}';
}
}
//测试类
public class Test {
//可能会存在异常的方法
static void test(int a) throws MyException {
System.out.println("传递的参数为:"+a);
if (a>10){
//可以用try-catch在此捕获异常,也可以用throw抛出异常,并在方法名旁用throws抛出,等待调用方法时捕获异常
throw new MyException(a);//抛出
}
System.out.println("OK");//若有抛出异常则不执行此语句
}
public static void main(String[] args) {
try {
test(11);
} catch (MyException e) {//e是指to String打印的字符串
//增加一些处理异常的代码
System.out.println("MyException=>"+e);
}
}
}
throw 和 throws 的区别:
1.throws 跟在方法声明后面,后面跟的是异常类名
throw 用在方法体内,后面跟的是异常类对象名
2.throws 可以跟多个异常类名,用逗号隔开
throw 只能抛出一个异常对象名
3.throws 表示抛出异常,由该方法的调用者来处理
throw 表示抛出异常,由该方法体内的语句来处理
4.throws 表示有出现异常的可能性,并不一定出现这些异常
throw 则是抛出了异常,执行throw一定出现了异常
实际应用中的经验总结:
处理运行时异常时,采用逻辑去合理规避同时辅助try-catch处理
在多重catch块后面,可以加一个catch (Exception)来处理可能会被遗漏的异常
对于不确定的代码,也可以加上try-catch ,处理潜在的异常
尽量去处理异常,切忌只是简单地调用printStackTrace()去打印输出
具体如何处理异常,要根据不同的业务需求和异常类型去决定
尽量添加finally语句块去释放占用的资源