Java基础
java概述
概述
- java跨平台原理
平台:Windows、Mac、Linux
通过安装不同版本的java虚拟机(JVM)即可。(充当翻译功能)
- JRE和JDK
JRE (Java Runtime Environment ):是java程序的运行时环境,包含JVM和运行时所需要的核心类库
JDK (Java Develpoment Kit):是java程序开发工具包,包含JRE和开发人员使用的工具[编译工具(javac,exe)、运行工具(java.exe)]。
总结:JDK包含JRE和开发工具;JRE包含JVM和核心类库。
- JDK安装
此电脑安装在D:\Java\JDK目录中
安装目录解释:
- bin:该路径下存放了JDK的各种工具命令。javac和java就在这个目录中
- conf:该路径下存放了JDK的相关配置文件
- include:该路径下存放了一些平台特定的头文件
- jmods:该路径下存放了JDK的各种模块
- legal:该路径下存放了JDK各模块的授权文档
- lib:该路径下存放了JDK工具的一些补充JAR包
- 其余文件为说明性文件
- 配置Path环境变量
原因:为了方便使用javac和java等命令
配置成功检测
在命令行窗口分别输入 javac、java、java -version 有对应输出即可
- java程序开发运行流程
编写程序→编译程(编译器生成java字节码文件)→运行程序
- 记事本运行java程序
- 在任意盘下新建一个.txt 文件,写入java代码并保存;
- 将.txt文件改为.java文件
- 在命令行窗口进入到新建的.java路径下
- 编译:javac 新建的文件名(javac后面要加空格,包含.java后缀)
- 运行:java 新建的文件名(java后面要加空格,不包含.java 后缀)
- BUG(虫子)
- 非法字符一般指中英文符号的问题
IDEA
IDEA全称IntelliJ IDEA,是用于java语言开发的集成环境(把代码编写,编译,执行,调试等多种功能综合到一起的开发工具)。
IDEA项目结构
- 以项目为单位来管理java程序的;
- 项目>模块>包>类等java文件
IDEA基本操作
- IntelliJ IDEA Community Edition 2021.1.3 x64为例
- 创建一个空项目
方法: File→New→Project→java(右边的SDK根据自己电脑的JDK版本路径设置)→Next→Next→(设置项目名称和保存路径:设置路径时最好自己添加项目名,就可自动新建文件夹,防止文件混乱)→Finsh→(选择界面,一般选择This window)→即创建了一个项目(第一次需要设置Module)(模块)
- 项目下创建新模块(Module)
Module创建方法: File→Project Structure→Modules→中间加号→New Module→(新界面)点java→(选择安装路径)→Next→(修改模块名称)→Finsh→OK即可
- 模块创建一个包
方法: src→New→Package→(设置包名)→OK
- 包内创建类
方法: 包名右击→New→Java Class→(设置类名)→OK
- 在类中编写代码
- 在idea中执行程序
- 查看方法的源码
方法: 选中方法右击→Go To→Declaration or Usages
快捷键:选中 Ctrl B
- 查看类的方法列表
方法: View→Tool Windows→Structure
- 文件位置
- java文件:在项目里面的包文件夹中src下
- class文件:在项目下的out文件夹下
IDEA模块操作
- 新建模块
见上述Module创建方法
- 删除模块
方法: 选中模块→Remove Module→OK
说明: 硬盘并没有删除,只是在IDEA界面移除了;彻底删除需要在硬盘选中文件夹删除
- 导入模块
方法:File→Project Structure→Modules→中间加号→Import Module→(新界面)(选择要导入的模块名称)→OK→Next…→(Overwrite)→Finsh→OK即可
说明: 导入可能会提示"Project SDK is not defined";直接点击右边Setup SDK→OK即可
IDEA中内容辅助键和快捷键
- 快速生成main方法: psvm,回车 / main,回车
- 快速生成输出语句: sout,回车
- 内容辅助键: Ctrl+Alt+space(空格) 作用: 重新内容提示,代码补全等。
- 快速单行注释: 选中代码,Ctrl+/,再来一次,则取消(可选中多行)
- 快速多行注释: 选中代码,Ctrl+Shift+/,再来一次,则取消
- 格式化: Ctrl+Alt+L 作用: 美化代码格式。
- 方法变量接收: Ctrl+Alt+V
- 方法快速生成: Alt+Insert
- 调用异常处理方法: 选中异常单词,Alt+Enter
- 代码前移: 选中代码段,Shift+Tab
IDEA中常用设置
- 设置字体: File→Settings→Editor→Font
- 设置主题: File→Settings→Appearance & Behavior→Appearance
- 安装插件: File→Settings→Plugins
java基础语法
注释
单行注释://注释信息
多行注释:/* 注释信息 */
文档注释:/** 注释信息 */
关键字
关键字:就是被java语言赋予了特定含义的单词
- 特点
- 关键字的字母全部是小写
- 常用编辑器中一般将其表达为特殊颜色标记
- java中有53个关键字
常量
- 分类
字符串常量 eg:“HelloWorld”(双引号)
整数常量 eg:66
小数常量 eg:13.14
字符常量 eg:‘A’(单引号)
布尔常量 只有两个值:true,false
空常量 一个特殊的值:null
说明:只有空常量不能直接输出,其余均可以直接输出 eg:System.out.println(66)
数据类型
java是强类型语言,对于每种数据都给出了明确的数据类型,不同的数据类型也分配了不同的内存空间
基本数据类型
数据类型 | 关键字 | 内存占用(bit) | 取值范围 |
---|---|---|---|
整数 | byte | 1 | -128~127 |
short | 2 | -32768~32767 | |
int(默认) | 4 | -231~231-1 | |
long | 8 | -263~263-1 | |
浮点数 | float | 4 | 负数:-3.402823*1038~-1.401298 *10-45 |
正数:1.401298*10-45~3.402823 *1038 | |||
double(默认) | 8 | 负数:-1.797693*10308~-4.9000000 *10-323 | |
负数:4.9000000*10-324~1.797693 *10308 | |||
字符 | char | 2 | 0~65535 |
布尔 | boolean | 1 | ture,flase |
变量
变量:在程序运行过程中,其值可以发生改变的量;从本质上将讲,变量是内存中的一小块区域
- 变量定义
格式:数据类型 变量名 = 变量值;
eg:int a = 10;
- 变量的使用
- 取值
格式:变量名
eg:a
- 修改值
格式:变量名 = 变量值
eg:a = 20;
- 变量使用的注意事项
- 变量名不能重复
- 变量未赋值,不能使用
- long类型的变量定义时,为了防止整数过大(默认long int),后面要加L
eg:long l = 1000000000L
- float类型的变量定义时,为了防止类型不兼容,后面要加F
eg:float f = 1000000000F
标识符
标识符定义规则
- 由数字、字母、下划线和美元符($)组成
- 不能以数字开头
- 不能是关键字
- 区分大小写
常见命名约定
- 小驼峰命名法(针对 方法、变量 命名)
- 标识符是一个单词的时候,首字母小写 eg:name
- 标识符有多个单词组成是时候,第一个单词首字母小写,其他单词首字母大写 eg:firstName
- 大驼峰命名法(针对 类 命名)
- 标识符是一个单词的时候,首字母大写 eg:Name
- 标识符有多个单词组成是时候,每个单词首字母大写 eg:FirstName
类型转换
自动类型转换
把一个表示数据范围小的数值或者变量赋值给另一个表示数据范围大的变量
可以自动转换的类型流如下:
byte→short→int→long→float→double
char→int→long→float→double
eg:
byte b = 10;
short s = b;
int i = b;
说明:byte不能自动转换为char,类型不兼容
强制类型转换
把一个表示数据范围大的数值或者变量赋值给另一个表示数据范围小的变量
格式:目标数据类型 变量名 = (目标数据类型)值或者变量;
eg:int k = (int)66.66;
输出值为66,即强制类型转换会有数值丢失
运算符
算术运算符
- 加减乘除取余(+ - * / %)
说明:整数操作只能得到整数,得到小数必须有浮点数参与运算
- 字符的"+"操作
字符进行运算是计算机底层的数值来进行运算的(ASCII码)
说明:算术表达式中包含多个基本数据类型的值的时候,整个算术表达式的类型会自动进行提升
提升规则:整个表达式中的类型自顶提升到表达式中最高等级操作数同样的类型
等级顺序:byte,short,char→int→long→float→double(前三个为同一等级)
- 字符串的"+"操作
当"+“操作出现字符串时,这个”+"是字符串连接符,而不是算术运算
当连续"+"操作中,从左到右逐个执行
赋值运算符
符号 | 作用 | 说明 |
---|---|---|
= | 赋值 | a = 10,将10赋值给变量a |
+= | 加后赋值 | a += 10,将a+b赋值给变量a |
-= | 减后赋值 | a -= 10,将a-b赋值给变量a |
*= | 乘后赋值 | a *= 10,将a×b赋值给变量a |
/= | 除后赋值 | a /= 10,将a÷b赋值给变量a |
%= | 取余后赋值 | a %= 10,将a÷b的余数赋值给变量a |
说明:扩展的赋值运算中隐含了强类型转换!
自增自减运算符
符号 | 作用 | 说明 |
---|---|---|
++ | 自增 | 变量的值加1 |
– | 自减 | 变量的值减1 |
说明:
-
++和–既可以放在变量法后面,也可以放在变量的前边
-
单独使用时,结果一样
-
参与操作的时候,如果放在变量的前面,先拿变量参与操作,再运算做++或–;
参与操作的时候,如果放在变量的后面,先拿变量运算做++或–,再拿变量参与操作。
关系运算符
符号 | 说明 |
---|---|
== | a==b,判断a和b是否相等,成立为true,不成立为false |
基本类型:比较数据值是否相同;引用类型:比较地址值是否相同 | |
!= | a!=b,判断a和b是否不相等,成立为true,不成立为false |
> | a>b,判断a是否大于b,成立为true,不成立为false |
>= | a>=b,判断a是否大于等于b,成立为true,不成立为false |
< | a<b,判断a是否小于b,成立为true,不成立为false |
<= | a<=b,判断a是否小于等于b,成立为true,不成立为false |
说明:输出结果都是Boolean类型,只能是true或者false
逻辑运算符
逻辑运算符:是用来连接关系表达式的运算符,也可以直接连接布尔类型的常量或者变量
- 基本逻辑运算符
符号 | 作用 | 说明 |
---|---|---|
& | 逻辑与 | 全T才为T |
| | 逻辑或 | 有T则为T |
^ | 逻辑异或 | 相同为F,不同为T |
! | 逻辑非 | 非 |
- 短路逻辑运算符
符号 | 作用 | 说明 |
---|---|---|
&& | 短路与 | 作用和&相同,但有短路作用 |
|| | 短路或 | 作用和|相同,但有短路作用 |
-
区别:逻辑&和逻辑|无论左边真假,右边都要执行
短路&&,左边为假,右边不执行(因为已经能判断真假了)
短路||,左边为真,右边不执行(因为已经能判断真假了)
最常用的为:$$和||
三元运算符
格式:关系表达式?表达式1:表达式2
eg:a > b ? a : b;
执行流程:首先计算关系表达式的值;如果为true,则运算结果为表达式1;如果为false,则运算结果为表达式2
数据输入输出
- Scanner用法
- 导包:
import java.util.Scanner;
说明:导包的动作必须出现在类定义的上边
- 创建对象:
Scnanner sc = new Scanner(System.in);
说明:sc为变量名可以改变,其余均不可改变
- 接收数据:
int i = sc.nextInt();
说明:i为变量名可以改变,其余均不可改变
可以接受多个数据,eg:
Scanner sc=new Scanner(System.in);
int a=sc.nextInt();
int b=sc.nextInt();
int c=sc.nextInt();
- 输出数据
System.out.println("i");
- 模板
import java.util.Scanner;//导包
public class Lianxi {
public static void main(String[] args) {
//创建对象
Scanner sc = new Scanner(System.in);
//接收数据
int x = sc.nextInt();
//输出数据
System.out.println("x:"+x);
}
}
- 输出
System.out.println("内容");//输出内容并换行
System.out.print("内容");//输出内容不换行
分支语句
顺序结构
按代码的先后顺序依次执行
分支结构
if语句
- if语句格式1
格式:
if(关系表达式){
语句体;
}
执行流程:
- 首先计算关系表达式的值
- 如果关系表达式的值为true就执行语句体
- 如果关系表达式的值为false就不执行语句体
- 继续执行后面的语句内容
- if语句格式2
格式:
if(关系表达式){
语句体1;
}else{
语句体2;
}
执行流程:
- 首先计算关系表达式的值
- 如果关系表达式的值为true就执行语句体1
- 如果关系表达式的值为false就执行语句体2
- 继续执行后面的语句内容
- if语句格式3
格式:
if(关系表达式1){
语句体1;
}else if(关系表达式2){
语句体2;
}
……
else{
语句体n+1;
}
执行流程:
- 首先计算关系表达式1的值
- 如果值为true就执行语句体1;如果值为false就执行关系表达式2的值
- 如果值为true就执行语句体2;如果值为false就执行关系表达式3的值
- ……
- 如果没有任何关系式为true,就执行语句体n+1
- 测试数据时:一般要测试正确数据、边界数据、错误数据。
switch语句
格式1:
switch(表达式){
case 值1;
语句体1;
break;
case 值2;
语句体1;
break;
……
default
语句体n+1;
}
格式2:
switch(表达式){
case 值1;
case 值2;
语句体1;
break;
……
default
语句体n+1;
}
case穿透语句:如果case后面不写break,则会出现穿透现象,继续执行直到遇到break。
执行流程:
- 首先计算表达式的值
- 依次和case后面的值进行比较,如果有对应的值,就会执行相应的语句,在执行过程中遇到break就会结束
- 如果所有的case后面的值都不匹配,就会执行default里面的语句体,然后程序结束
循环结构
for循环语句
格式:
for(初始化语句;条件判断语句;条件控制语句){
循环体语句;
}
执行流程:
-
执行初始化语句
-
执行条件判断语句,看其结果是true还是false
如果是false,循环结束;
如果是true,继续执行;
-
执行循环体语句
-
执行条件控制语句
-
回到2. 继续
- 水仙花数讲解
水仙花数:一个三位数,个位、十位、百位数的立方和等于原数
任意数字的指定位上的数值求法: 先使用整除操作将要求的数字移动到个位上,再使用取余操作取出最后一位上的值。
水仙花案例
public class hello {
public static void main(String[] args) {
for(int i=100;i<1000;i++) {
int ge=i%10;
int shi=i/10%10;
int bai=i/100;
if(ge*ge*ge+shi*shi*shi+bai*bai*bai==i) {
System.out.println(i);
}
}
}
}
水仙花计数
public class hello {
public static void main(String[] args) {
int count =0;
for(int i=100;i<1000;i++) {
int ge=i%10;
int shi=i/10%10;
int bai=i/100;
if(ge*ge*ge+shi*shi*shi+bai*bai*bai==i) {
count++;
}
}
System.out.println(count);
}
}
while循环语句
格式:
初始化语句;
while(条件判断语句){
循环体语句;
条件控制语句;
}
执行流程:
-
执行初始化语句
-
执行条件判断语句,看其结果是true还是false
如果是false,循环结束;
如果是true,继续执行;
-
执行循环体语句
-
执行条件控制语句
-
回到2. 继续
do…while循环语句格式
格式:
初始化语句;
do{
循环体语句;
条件控制语句;
}while(条件判断语句);
执行流程:
-
执行初始化语句
-
执行循环体语句
-
执行条件控制语句
-
执行条件判断语句,看其结果是true还是false
如果是false,循环结束;
如果是true,继续执行;
-
回到2. 继续
三种循环的区别
- 三种循环的区别
- for循环和while循环先判断条件是否成立,然后决定是否执行循环体(先判断后执行)
- do…while循环先执行一次循环体,然后判断条件是否成立,是否继续执行循环体(先执行后判断)
- for和while的区别
- 条件控制语句所控制的自增变量,因为归属for循环的语法结构中,在for循环结束后,就不能再次被访问到了(局部变量)
- 条件控制语句所控制的自增变量,对于while循环来说不归属其语法结构,在while循环结束后,该变量还可以继续使用
- 死循环格式
for(;;){}
while(true){}
do{}while(true);
跳转控制语句
- continue:用在循环中,基于条件控制,跳过某次循环体内容的执行,继续下一次执行
- break:用在循环中,基于条件控制,终止循环体内容的执行,即结束当前整个循环
- return:使整个函数返回,后面不管是循环里面还是循环外面都不执行
循环嵌套
时间案例:
public class hello {
public static void main(String[] args) {
for(int hour=0;hour<24;hour++) {
for(int minute=0;minute<60;minute++) {
System.out.println(hour+"时"+minute+"分");
}
}
}
}
//外循环控制小时,内循环执行分钟;每经过60分钟,即一个内循环,再进入外循环
Random
作用:用于产生一个随机数
- Random用法
- 导包
import java.util.Random
说明:导包的动作必须出现在类定义的上边
- 创建对象:
Random r = new Random();
说明:r为变量名可以改变,其余均不可改变
- 获取随机数:
int number = r.nextInt(10);
说明:10表示获取数据的范围为[0,10) 包括0,不包括10
i为变量名可以改变,其余均不可改变
猜数字案例
//猜0-100之间的数
import java.util.Scanner;
import java.util.Random;
public class hello {
public static void main(String[] args) {
Random r =new Random();
int number =r.nextInt(100)+1;
while(true) {
Scanner sc= new Scanner(System.in);
System.out.println("请输入0-100之间的数");
int guessNumber = sc.nextInt();
if(guessNumber>number) {
System.out.println("你猜的数字"+guessNumber+"大了");
}else if(guessNumber<number) {
System.out.println("你猜的数字"+guessNumber+"小了");
}else{
System.out.println("恭喜你猜中了");
break;//退出循环,防止进入死循环
}
}
}
}
数组
数组(array)是一种用于存储多个相同类型数据的存储模型
数组定义格式
格式: 数据类型[] 变量名
eg:int[] arr
(定义了一个int类型的数组,数组名是arr)
不常用格式:数据类型 变量名[]
eg:int arr[]
(定义了一个int类型的变量,变量名是arr数组)
数组元素访问
- 数组变量访问方式
格式:数组名 //输出地址
- 数组内部保存的数组的访问格式
格式:数组名 [索引] //输出数组中的元素,初值分配默认为0
- 索引:是数组中的编号方式;用于访问数组中的数据;索引从0开始,连续且逐个加一
数组动态初始化
java中的数组必须先初始化(为数组中的数组元素分配内存空间,并为每个数组元素赋值),然后才能使用。
- 动态初始化:初始化时只指定数组长度,由系统为数组分配初始值
格式: 数据类型 [] 变量名 = new 数据类型[数组长度];
eg: int[] arr = new int[3];
说明:
左边: 右边
int:说明数组中的元素类型是int类型 new:为数组中的元素申请内存空间
[]:说明这个是一个数组 int:说明数组中的元素类型是int类型
arr:这是数组的名称 []:说明这个是一个数组
3:数组长度,即数组中的元素个数
数组静态初始化
- 静态初始化: 即初始化时指定每个数组元素的初始值,由系统决定数值长度
格式:数据类型 [] 变量名 = new 数据类型[]{数据1,数据2,数据3,……};
eg:int[] arr = new int[]{1,2,3};
**简化格式(推荐):**数据类型 [] 变量名 = {数据1,数据2,数据3,……};
eg:int[] arr ={1,2,3};
- 模板:
public class Lianxi {
public static void main(String[] args) {
//定义数组
int[] arr = {1,2,3};
//输出数组名
System.out.println(arr);
//输出数组中的元素
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
}
}
内存分配
java程序在运行时,需要在内存中分配空间。为了提高运算效率,就对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。
eg:int[] arr = new int[3];
- 栈内存:存储局部变量
定义在方法中的变量,eg:arr;使用完毕,立即消失
- 堆内存:存储new出来的内容(实体,对象)
数组在初始化时,会为存储空间添加默认值
整数:0
浮点数:0.0
布尔:false
字符:空字符
引用数据类型:null
每个new出来的东西都有一个地址值;使用完毕,会在垃圾回收器空闲时被回收
栈区:int[] arr | 堆区:new int[3] |
---|---|
指向001 | 001(内存地址,用于表示下面的内存空间) |
0 0 | |
1 0 | |
3 0 |
- 内存分配体会
//单个数组分配内存
public class Lianxi {
public static void main(String[] args) {
//定义数组
int[] arr = new int[3];
//输出数组名
System.out.println(arr);
//输出数组中的元素
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
//给数组中的元素赋值
arr[0] = 100;
arr[2] = 200;
//再次输出
System.out.println(arr);
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
}
}
/*输出:
[I@7c30a502
0
0
0
[I@7c30a502
100
0
200*/
//多个数组分配内存
public class Lianxi {
public static void main(String[] args) {
//定义数组
int[] arr = new int[2];
int[] arr2 = new int[3];
//输出数组名及元素
System.out.println(arr);
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr2);
System.out.println(arr2[0]);
System.out.println(arr2[2]);
//给数组中的元素赋值
arr[0] = 100;
arr2[0] = 200;
arr2[2] = 300;
//再次输出
System.out.println(arr);
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr2);
System.out.println(arr2[0]);
System.out.println(arr2[2]);
}
}
/*输出
[I@7c30a502
0
0
[I@49e4cb85
0
0
[I@7c30a502
100
0
[I@49e4cb85
200
300*/
//多个数组指向相同
public class Lianxi {
public static void main(String[] args) {
//定义数组
int[] arr = new int[3];
//赋值
arr[0] = 100;
arr[1] = 200;
arr[2] = 300;
//输出数组名及元素
System.out.println(arr);
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
//定义第二个数组指向第一个数组
int[] arr2 = arr;
arr2[0] = 111;
arr2[1] = 222;
arr2[2] = 333;
//再次输出
System.out.println(arr);
System.out.println(arr[0]);
System.out.println(arr2);
System.out.println(arr2[0]);
}
}
/*输出
[I@7c30a502
100
200
300
[I@7c30a502
111
[I@7c30a502
111*/
数组操作中两个常见的小问题
- 索引越界
访问了数组中不存在的索引对应的元素,造成索引越界问题
报错:ArrayIndexOutfBoundsException
- 空指针异常
访问的数组已经不在指向堆内存的数据,造成空指针异常
报错:NullPointerException
eg:
public class Lianxi {
public static void main(String[] args) {
int [] arr = new int[3];
arr = null;
System.out.println(arr[0]);
}
}
//null:空值,引用数组类型的默认值,表示不指向任何有效对象
数值常见操作
- 遍历
public class Lianxi {
public static void main(String[] args) {
int [] arr = {11,22,33,44,55};
//通用遍历格式
for(int x=0; x<arr.length; x++){
System.out.println(arr[x]);
}
}
}
说明:获取数组元素数量方法
格式: 数组名.length
eg:arr.length
- 获取最值
//最大值
public class Lianxi {
public static void main(String[] args) {
int [] arr = {11,22,33,44,55};
//定义变量保存最大值,并赋初值
int max = arr[0];
//逐个对比
for(int x=1; x<arr.length; x++){
if(arr[x]>max){
max=arr[x];
}
}
System.out.println("max:"+max);
}
}
//最小值
public class Lianxi {
public static void main(String[] args) {
int [] arr = {11,22,33,44,55};
//定义变量保存最大值,并赋初值
int min = arr[0];
//逐个对比
for(int x=1; x<arr.length; x++){
if(arr[x]<min){
min=arr[x];
}
}
System.out.println("min:"+min);
}
}
方法
方法概述
- 方法是将具有独立功能的代码块组织成为一个整体,使其具有特殊功能的代码集
说明:
- 方法定义: 方法必须先创建才可以使用,该过程称为方法定义
- 方法调用: 方法创建后并不是直接运行的,需要手动使用后才能执行,即为方法调用
方法的定义与调用
- 方法定义格式
public static void 方法名(){
//方法体
}
- 方法调用格式
方法名();//在main方法进行调用,因为main方法是入口方法
- 示例
public class Lianxi {
public static void main(String[] args) {
oushu();//方法调用
}
public static void oushu(){//方法定义
int number = 10;
if (number %2 == 0){
System.out.println(true);
}else{
System.out.println(false);
}
}
}
-
说明: 方法必须先定义再调用
-
方法调用过程
- 首先进入main方法,逐步执行;
- 发现oushu方法,跳转到oushu方法;
- oushu方法逐步执行;
- 执行完毕回到刚main方法跳转处的下一步继续执行,直到main方法结束。
- 练习
public class Lianxi {
public static void main(String[] args) {
getMax();
}
public static void getMax(){
int a =10, b =20;
int c = a > b ? a : b;
System.out.println("Max:"+c);
}
}
带参数方法的定义与调用
- 带参方法定义格式:
public static void 方法名(参数){……}
- 单个参数:
public static void 方法名(数据类型 变量名){……}
- 多个参数:
public static void 方法名(数据类型 变量名1,数据类型 变量名2,……){……}
说明: 参数中数据类型和变量名一个都不能少;多个参数使用逗分隔
- 带参方法调用格式: 方法名(参数);
-
单个参数:方法名(变量名/常量值); eg:
getMax(5);
-
多个参数:方法名(变量名1/常量值1,变量名2/常量值2……); eg:
getMax(5,6,……);
说明: 方法调用时,参数的数量必须和方法定义中的设置相匹配,否则程序将报错。
- 示例
public class Lianxi {
public static void main(String[] args) {
//常量值的调用
getMax(10);
//变量的调用
int number=10;
getMax(number);
}
public static void getMax(int number){
if (number % 2 == 0){
System.out.println(true);
}else{
System.out.println(false);
}
}
}
- 形参和实参
-
形参:方法定义中的参数,等同于变量定义格式;如上示例中的
int number
-
实参:方法调用中的参数,等同于使用的变量或常量;如上示例中的
10 或 number
- 练习
public class Lianxi {
public static void main(String[] args) {
//常量值的调用 调用时,方法类型是什么就必须是什么
getMax(10,20);
//变量的调用
int a = 10;
int b = 20;
getMax(a,b);
}
public static void getMax(int a,int b){
if (a > b){
System.out.println(a);
}else{
System.out.println(b);
}
}
}
带返回值方法的定义和调用
- 带返回值方法定义格式
public static 数据类型 方法名(参数){
return 数据;
}
说明: 方法定义时return后面的返回值与方法定义上的数据类型要匹配,否则程序将报错
- 带返回值方法调用格式
//无意义调用,因为没有变量接收,通常用于无返回值的方法
方法名(参数);
eg:printArray(arr)
//正常调用
数据类型 变量名 = 方法名(参数);
eg:boolean flag = getMax(5);
//数组调用方法:方法名(数组名)
eg:int number = getMax(arr);
说明: 返回值通常要变量值接收,否则无意义
- 示例
public class Lianxi {
public static void main(String[] args) {
//无意义调用
getMax(10);
//正常调用
boolean flag = getMax(10);
}
public static boolean getMax(int a){
if (a % 2 == 0){
return true;
}else{
return false;
}
}
}
- 练习
public class Lianxi {
public static void main(String[] args) {
int flag = getMax(10,20);
System.out.println(flag);//输出变量保存的值
//简化
System.out.println(getMax(10,20));
}
public static int getMax(int a,int b){
if (a > b){
return a;
}else{
return b;
}
}
}
方法的注意事项
- 方法不能嵌套定义
- void表示无返回值,可以省略return,也可以书写return,后面不加数据;return即结束
方法的通用格式
public static 返回值类型 方法名(参数){
方法体;
return 数据;
}
技巧:两个明确
- 明确返回值类型:主要看方法操作完毕后是否有数据返回,没有,写void;有,写对应类型
- 明确参数:主要看参数的类型
方法重载
方法重载是指同一个类中定义的多个方法之间的关系
- 满足条件
- 多个方法在同一个类中
- 多个方法具有相同的方法名
- 多个方法的参数不相同,类型不同或者数量不同
- 说明
- 重载仅对应方法的定义,与方法的调用无关,调用方式参照标准格式
- 重载仅针对同一个类中方法的名称与参数进行识别,与返回值无关
- 示例
public class Lianxi {
public static void main(String[] args) {
System.out.println(sum(10,20));
System.out.println(sum(10.0,20.0));
System.out.println(sum(10,20,30));
}
public static int sum(int a,int b){
return a + b;
}
public static double sum(double a,double b){
return a + b;
}
public static int sum(int a, int b, int c){
return a + b + c;
}
}
说明: 重载与返回值无关;在调用的时候Java虚拟机会通过参数的不同来区分同名的方法
- 练习
public class Lianxi {
public static void main(String[] args) {
//整数只会调用第一个方法,因为整数默认int类型;需要强转才能调用其他的
System.out.println(compare (10,20));
System.out.println(compare((byte) 10,(byte) 20));
System.out.println(compare((short) 10,(short) 20));
System.out.println(compare(10l,20l));
}
public static boolean compare(int a,int b){
return a == b;
}
public static boolean compare(byte a,byte b){
return a == b;
}
public static boolean compare(short a,short b){
return a == b;
}
public static boolean compare(long a,long b){
return a == b;
}
}
方法的参数传递
对于基本数据类型的参数,形式参数的改变,不影响实际参数的值
eg:
public class Lianxi {
public static void main(String[] args) {
int number = 100;
System.out.println("调用change方法前:"+ number);
change(number);
System.out.println("调用change方法后:"+ number);
}
public static void change(int number){
number = 200;
}
}
/*
调用change方法前:100
调用change方法后:100*/
对于引用类型的参数,形式参数的改变,影响实际参数的值
eg:
public class Lianxi {
public static void main(String[] args) {
int [] arr = {10,20,30};
System.out.println("调用change方法前:"+ arr[1]);
change(arr);
System.out.println("调用change方法后:"+ arr[1]);
}
public static void change(int[] arr){
arr[1] = 200;
}
}
/*
调用change方法前:20
调用change方法后:200*/
- 练习
数组遍历: 设计一个方法用于数组遍历,要求在一行输出。eg:[11,22,33,44,55]
public class Lianxi {
public static void main(String[] args) {
int [] arr = {11,22,33,44,55};
printArray(arr);
}
public static void printArray(int[] arr){
System.out.print("[");
for(int x = 0;x < arr.length;x++){
if(x == arr.length-1){
System.out.print(arr[x]);
}else{
System.out.print(arr[x]+",");
}
}
System.out.print("]");
}
}
数组最大值: 设计一个方法用于获取元素的最大值,调用方法并输出结果。
public class Lianxi {
public static void main(String[] args) {
int [] arr = {11,22,33,44,55};
System.out.println("Max:"+getMax(arr));//数组调用方法:方法名(数组名)
}
public static int getMax(int[] arr){
int max = arr[0];
for(int x = 0;x < arr.length;x++){
if(arr[x] > max){
max =arr[x];
}
}
return max;
}
}
静态方法和实例方法的区别
- 在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"对象名.方法名"的方式。而实例方法只有后面这种方式。也就说调用静态方法可以无需创建对象。
- 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法,如果需要调用,则需要先实例化;实例方法则无此限制。
- 静态方法是在类中使用staitc修饰的方法,在类定义的时候已经被装载和分配。而非静态方法是不加static关键字的方法,在类定义时没有占用内存,非静态方法只有在类被实例化成对象时,对象调用该方法才被分配内存;
Debug
Debug: 是供程序员使用的程序调试工具,它可以用于查看程序和执行流程,也可以用于追踪程序执行过程来调试程序。
Debug调试: 又被称为断点调试,断点即一个标记点。
- Debug操作流程
- 加断点:选择要设置断点的代码行,在行号区域后面单机鼠标左键即可
- 运行:在代码区右键Debug即可
- 左边Frames是代码执行位置;右边Variables是执行过程中变量变化区
- 点蓝色向下箭头逐句执行
- 删除断点:选择已经设置断点的代码行,在行号区域后面再次单机鼠标左键即可
- 删除多个断点:点击左边两个红色圆块重叠图标,选中点上方减号即可
- Debug使用
查看循环过程:(偶数和)
public class Lianxi {
public static void main(String[] args) {
int sum = 0;
for (int i = 1; i <= 10; i++) {
if (i % 2 == 0) {
sum += i;
}
}
System.out.println(sum);
}
}
查看方法调用过程:
import java.util.Scanner;
public class Lianxi {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入第一个数据:");
int a = sc.nextInt();
System.out.println("请输入第二个数据:");
int b = sc.nextInt();
System.out.println("Max:"+getMax(a , b));
}
public static int getMax(int a, int b){
if(a > b){
return a;
}else{
return b;
}
}
}
//在Scanner设置断点时,运行后会出现空白,要在console输入数据才能继续执行,因为要录入数据
说明: 如果数据来源于数据输入,一定要输入数据否则就不能继续往下执行了。
基础知识练习
- 减肥计划
import java.util.Scanner;
public class Lianxi {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入星期数:");
int week = sc.nextInt();
if (week < 1 || week > 7) {
System.out.println("你输入的星期数有误");
} else if (week == 1) {
System.out.println("跑步");
} else if (week == 2) {
System.out.println("游泳");
} else if (week == 3) {
System.out.println("慢走");
} else if (week == 4) {
System.out.println("动感单车");
} else if (week == 5) {
System.out.println("拳击");
} else if (week == 6) {
System.out.println("爬山");
} else if (week == 7) {
System.out.println("好好吃一顿");
}
}
}
- 减肥计划Switch版
import java.util.Scanner;
public class Lianxi {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入星期数:");
int week = sc.nextInt();
switch (week) {
case 1:
System.out.println("跑步");
break;
case 2:
System.out.println("游泳");
break;
case 3:
System.out.println("慢走");
break;
case 4:
System.out.println("动感单车");
break;
case 5:
System.out.println("拳击");
break;
case 6:
System.out.println("爬山");
break;
case 7:
System.out.println("好好吃一顿");
break;
default:
System.out.println("你输入的星期数有误");
}
}
}
- 逢七过
public class Lianxi {
public static void main(String[] args) {
for (int x = 1; x < 200; x++) {
if (x % 10 == 7 || x / 10 % 10 == 7 || x % 7 == 0) {
System.out.println(x);
}
}
}
}
// x / 10 % 10 == 7 判断整数 eg:170……
- 不死神兔: 一对兔子,出生后3个月起每个月都生一对兔子,循环往复,假设兔子都不死,问第二十个月的兔子对数
规律:每个月分别为:1,1,2,3,5……
public class Lianxi {
public static void main(String[] args) {
int[] arr = new int[20];
arr[0] = 1;
arr[1] = 1;
for (int x = 2; x < arr.length; x++) {
arr[x] = arr[x - 2] + arr[x - 1];
}
System.out.println("第二十个月兔子对数是:" + arr[19]);
}
}
- 百钱百鸡
public class Lianxi {
public static void main(String[] args) {
for (int x = 0; x <= 20; x++) {
for (int y = 0; y <= 33; y++) {
int z = 100 - x - y;
if (z % 3 == 0 && 5 * x + 3 * y + z / 3 == 100) {
System.out.println(x + "," + y + "," + z);
}
}
}
}
}
- 数组元素求和: 有数组[68,27,95,88,171,996,51,210]求和,要求求和的元素个位十位都不能是7,且只能是偶数
public class Lianxi {
public static void main(String[] args) {
int[] arr = {68, 27, 95, 88, 171, 996, 51, 210};
int sum = 0;
for (int x = 0; x < arr.length; x++) {
if (arr[x] % 10 != 7 && arr[x] / 10 % 10 != 7 && arr[x] % 2 == 0) {
sum += arr[x];
}
}
System.out.println(sum);
}
}
- 数组内容相同: 设计一个方法比较两个数组的内容是否相同
public class Lianxi {
public static void main(String[] args) {
int[] arr = {11, 22, 33, 44, 55};
int[] arr2 = {11, 22, 33, 44, 55};
System.out.println(compare(arr, arr2));
}
public static boolean compare(int arr[], int arr2[]) {
if (arr.length != arr2.length) {
return false;
}
for (int x = 0; x < arr.length; x++) {
if (arr[x] != arr2[x]) {
return false;
}
}
return true;
}
}
- 查找: 已知一个数组,键盘录入一个数据查找该数据在数组中的索引,并在控制台输出找到的索引值
import java.util.Scanner;
public class Lianxi {
public static void main(String[] args) {
int[] arr = {18, 28, 37, 46, 50};
Scanner sc = new Scanner(System.in);
System.out.println("请输入要查找的数据:");
int number = sc.nextInt();
int index = -1;
for (int x = 0; x < arr.length; x++) {
if (arr[x] == number) {
index = x;
break;
}
}
System.out.println("index:" + index);
}
}
//方法
import java.util.Scanner;
public class Lianxi {
public static void main(String[] args) {
int[] arr = {18, 28, 37, 46, 50};
Scanner sc = new Scanner(System.in);
System.out.println("请输入要查找的数据:");
int number = sc.nextInt();
System.out.println(getIndex(arr, number));
}
public static int getIndex(int[] arr, int number) {
int index = -1;
for (int x = 0; x < arr.length; x++) {
if (arr[x] == number) {
index = x;
break;
}
}
return index;
}
}
- 翻转: 数组元素翻转
public class Lianxi {
public static void main(String[] args) {
int[] arr = {18, 28, 37, 46, 50};
reverse(arr);
printArray(arr);
}
//翻转方法
public static void reverse(int arr[]) {
for (int start = 0, end = arr.length - 1; start <= end; start++, end--) {
int temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
}
}
//遍历数组方法
public static void printArray(int arr[]) {
System.out.print("[");
for (int x = 0; x < arr.length; x++) {
if (x == arr.length - 1) {
System.out.print(arr[x]);//为了加逗号更美观
} else {
System.out.print(arr[x] + ",");
}
}
System.out.println("]");
}
}
- 评委打分: 6个评委打分,得分取去掉最高分和最低分,剩余4位评委的平均值
import java.util.Scanner;
public class Lianxi {
public static void main(String[] args) {
int[] arr = new int[6];
Scanner sc = new Scanner(System.in);
for (int x = 0; x < arr.length; x++) {
System.out.println("请输入第" + (x + 1) + "个评委的分数");
arr[x] = sc.nextInt();
}
int Max = getMax(arr);
int Min = getMin(arr);
int Sum = getSum(arr);
int avg =(Sum - Max - Min) / (arr.length - 2);
System.out.println("选手得分为:" + avg);
// printArray(arr);
}
/*
方法的两个明确:
1.返回值类型
2.参数类型
*/
//获取最大值方法
public static int getMax(int[] arr) {//对数组操作即数组为形参
int max = arr[0];
for (int x = 0; x < arr.length; x++) {
if (arr[x] > max) {
max = arr[x];
}
}
return max;
}
//获取最小值方法
public static int getMin(int[] arr) {
int min = arr[0];
for (int x = 0; x < arr.length; x++) {
if (arr[x] < min) {
min = arr[0];
}
}
return min;
}
//数组求和方法
public static int getSum(int[] arr) {
int sum = 0;
for (int x = 0; x < arr.length; x++) {
sum += arr[x];
}
return sum;
}
}
重点类和对象思想
类和对象
概述
- 对象:万物皆对象,客观存在的事物皆对象
- 类:是对现实生活中一类具有共同属性和行为的事物的抽象
- 类是对象的数据类型
- 类是具有相同属性和行为的一组对象的集合
- 属性:对象具有的各种特征,每个对象的每个属性都拥有特定的值
- 行为:对象能够执行的操作
- **类和对象的关系:**类是对象的抽象,对象是类的实体
类
-
类的重要性:类是Java程序的基本组成单位
-
类的组成:属性和行为
- 属性:在类中通过成员变量来体现(类中方法外的变量)
- 行为:在类中通过成员方法来体现(和前面的方法相比去掉static即可)
类的定义
public class 类名{
//成员变量
变量1的数据类型 变量1;
变量2的数据类型 变量2;
……
//成员方法
方法1;
方法2;
……
}
对象的使用
- 创建对象
格式:类名 对象名 = new 类名();
eg:Phone p = new Phone();
- 使用对象
- 使用成员变量
格式:对象名.变量名
eg:p.brand
- 使用成员方法
格式:对象名.方法名()
eg:p.call()
- 示例
- 手机类
//创建类
public class Phone {
//成员变量
String brand;
int price;
//成员方法
public void call(){
System.out.println("打电话");
}
public void sendMessage(){
System.out.println("发短信");
}
}
//使用类
public class PhoneDemo {
public static void main(String[] args) {//相当于测试类,不能忘了main方法!
//创建对象
Phone p = new Phone();//实例化对象
//成员变量赋值
p.brand = "华为";
p.price = 4999;
//使用成员变量
System.out.println(p.brand);
System.out.println(p.price);
//使用成员方法
p.call();
p.sendMessage();
}
}
- 学生类
//定义学生类
public class Student {
String name;
int age;
public void study() {
System.out.println("好好学习,天天向上");
}
public void doHomework() {
System.out.println("键盘敲烂,月薪过万");
}
}
//创建对象并使用
public class StudentDemo {
public static void main(String[] args) {
Student s = new Student();
System.out.println(s.name + "," + s.age);
s.name = "勒布朗";
s.age = 37;
System.out.println(s.name + "," + s.age);
s.study();
s.doHomework();
}
}
对象内存分配
栈区:main方法 | 堆区:new Student |
---|---|
Student s 指向001(调用) | 001(内存地址,用于表示下面的内存空间) |
name null | |
age 0 |
- 对个对象指相同体会
public class Student {
String name;
int age;
public void study() {
System.out.println("好好学习,天天向上");
}
public void doHomework() {
System.out.println("键盘敲烂,月薪过万");
}
}
public class StudentDemo {
public static void main(String[] args) {
Student s1 = new Student();
s1.name = "勒布朗";
s1.age = 37;
System.out.println(s1.name + "," + s1.age);
Student s2 = s1;//此时对象s1,s2都指向同一个地址
s2.name = "浓眉";
s2.age = 30;
System.out.println(s1.name + "," + s1.age);
System.out.println(s2.name + "," + s2.age);
}
}
/*
勒布朗,37
浓眉,30
浓眉,30*/
成员变量和局部变量
区别 | 成员变量 | 局部变量 |
---|---|---|
类中位置不同 | 类中方法外 | 方法内或者方法声明上 |
内存中位置不同 | 堆内存 | 栈内存 |
生命周期不同 | 随着对象的存在而存在,消失而消失 | 随着方法的调用而存在,调用完毕而消失 |
初始化值不同 | 有默认的初始化值 | 没有默认的初始化值,必须先定义,赋值,才能使用 |
封装
private关键字
- 是一个权限修饰符,可以修饰成员(成员变量和成员方法)
- 作用:保护成员不被别的类使用,被private修饰的成员只有在本类中才能访问
private关键字操作: 针对private修饰的成员变量,如果需要被其他类使用,提供如下操作
- 提供"get变量名()"方法,用于获取成员变量的值,方法用public修饰
- 提供"set变量名(参数)"方法,用于设置成员变量的值,方法用public修饰(需要返回值的)
private关键字的使用: 一个标准类的方法
- 把成员变量用private关键字修饰
- 提供相应的getXxx()/setXxx()方法
示例:
//学生类
public class Student {
private String name;
private int age;
//提供get/set方法
public void setName(String n) {
name = n;
}
public String getName() {//get方法用于获取成员变量的值,所以不用设置形参,即不用赋值。
return name;
}
public void setAge(int a) {//set方法用于设置成员变量的值,也可以限制成员变量的范围、用法。
if (a < 0 || a > 120) {
System.out.println("你给的年龄有误");
} else {
age = a;
}
}
public int getAge() {
return age;
}
public void show() {
System.out.println(name + "," + age);
}
}
//学生测试类
public class StudentDemo {
public static void main(String[] args) {
Student s1 = new Student();
s1.setName("勒布朗");
s1.setAge(37);
s1.show();
System.out.println(s1.getName() + "---" + s1.getAge());//get方法获取值可以任意改变输出格式
} //show方法则开始就定义了,后续不能改变
}
this关键字
- this修饰的变量用于指代成员变量
- 方法的形参如果与成员变量同名,不带this修饰指代的只是形参,而不是成员变量
- 方法的形参没有与成员变量同名,则不用this修饰
-
this作用: 当局部变量和成员变量同名时,使用this关键字解决局部变量隐藏成员变量问题
-
方法被哪个对象调用,this就指代哪个变量
栈区:main方法 | 堆区:new Student |
---|---|
Student s 指向001(调用) | 001(内存地址,用于表示下面的内存空间) |
其中this指向001(因为被s调用) | name null |
当有另外一个对象时,则又指向另外一个对象 | age 0 |
示例:
//学生类
public class Student {
private String name;
private int age;
//提供get/set方法
public void setName(String name) {
this.name = name;
}
//public void setName(String name) {//此时不能区分是哪个name,系统默认指代形参赋值给成员变量,则输出默认值null
// name = name;
//}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void show() {
System.out.println(name + "," + age);
}
}
//学生测试类
public class StudentDemo {
public static void main(String[] args) {
Student s1 = new Student();
s1.setName("勒布朗");
s1.setAge(37);
s1.show();
}
}
封装
- 概述
- 是面向对象的三大特征之一(封装、继承、多态)
- 是面向对象编程语言对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外部无法直接操作
- 封装原则
- 将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问
- 成员变量private,提供对应的getXxx/setXxx方法
- 好处
- 通过方法来控制成员变量的操作,提高了代码的安全性
- 把代码用方法进行封装,提高了代码的复用性(调用方便)
构造方法
- 概述:构造方法是一种特殊的方法,主要用于完成对象数据初始化
- 构造方法一般定义天然出生自带的属性,有一个对象建立,构造方法只运行一次,要想再次赋值或者其它操作,则需要使用set和get方法。
格式:
public claas (类名){
修饰符 类名(参数){
//构造方法体
}
}
- 使用注意事项
- 如果没有定义构造方法,系统将会自动给出一个默认的无参构造方法;定义了(无参或有参)系统则不再给出
- 推荐无论是否使用,都手工书写无参构造方法!
示例
//学生类
public class Student {
private String name;
private int age;
//以下为构造方法的重载
//无参构造方法
public Student() {
System.out.println("无参构造方法");
}
//带参构造方法
public Student(String name) {
this.name = name;
}
public Student(int age) {
this.age = age;
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
//普通show方法
public void show() {
System.out.println(name + "," + age);
}
}
//学生测试类
public class StudentDemo {
public static void main(String[] args) {
//普通无参创建对象
Student s1 = new Student();
s1.show();
//含参构造对象,用于赋值
Student s2 = new Student("勒布朗");
s2.show();
Student s3 = new Student(37);
s3.show();
Student s4 = new Student("勒布朗", 37);
s4.show();
}
}
方法对比
//通用方法格式
public static 返回值类型 方法名(参数){
方法体;
return 数据;
}
//成员方法
public 返回值类型 方法名(参数){
方法体;
return 数据;
}
//get/set方法
public 返回值类型 getAge() {
return 变量值;
}
public 返回值类型 setName(参数) {
this.变量 = 变量;
}
//构造方法
public claas (类名){
修饰符 类名(参数){
//构造方法体
}
}
public class 和 class
- public class声明时,类名称必须与文件名称一致
- class声明时,类名称可以与文件名称不一致
标准类制作
标准类
- 成员变量
- 使用private修饰
- 构造方法
- 提供一个无参构造方法
- 提供一个带多个参数的构造方法
- 成员方法
- 提供每一个成员变量对应的setXxx()/getXxx()方法
- 提供一个显示对象信息的方法
测试类
创建对象并为其成员变量赋值
- 使用无参构造方法创建对象后使用setXxx方法赋值
- 使用带参构造方法创建带有属性值的对象
示例
//标准类
public class Student {
private String name;
private int age;
//构造方法
//无参
public Student() {
}
//带参
public Student(String name, int age) {
this.name = name;
this.age = age;
}
//成员方法
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void show() {
System.out.println(name + "," + age);
}
}
//测试类
public class StudentDemo {
public static void main(String[] args) {
//使用无参构造方法创建对象后使用setXxx方法赋值
Student s1 = new Student();
s1.setName("勒布朗");
s1.setAge(37);
s1.show();
//使用带参构造方法创建带有属性值的对象
Student s2 = new Student("勒布朗", 37);
s2.show();
}
}
字符串
API
API(Application Programming Interface)应用程序编程接口
Java API:指的是JDK中提供的各种功能的java类
通过帮助文档查询API如何使用:
- 看在那个包下**(java.lang包是不用导包的)**
- 看类的描述
- 看构造方法(如果没有构造方法,则看类的成员是否为静态的,静态的可以直接通过类名调用)
- 看成员方法
String类
-
Java程序中所有的双引号字符串,都是String类的对象
-
它的值在被创建后不能再被更改,但是可以被共享
-
字符串效果上相当于字符数组(char[]),但底层原理是字节数组(byte[]);
-
有构造方法和直接赋值方法,推荐使用直接赋值方式得到字符串对象:
String s="abc";
String对象内存特点
-
构造方法得到对象,是new出不同的空间,地址各不相同。
直接赋值得到对象,是放在常量池,内容一样的放在一起,所以地址也相同
字符串的比较
- 基本类型:比较数据值是否相同;引用类型:比较地址值是否相同
- 内容比较需要用equals()方法:
public boolean equals(Object anObject)
示例
public class Lianxi {
public static void main(String[] args) {
//构造方法得到对象
char[] chs = {'a', 'b', 'c'};
String s1 = new String(chs);
String s2 = new String(chs);
//直接赋值得到对象
String s3 = "abc";
String s4 = "abc";
//比较字符串对象地址是否相同
System.out.println(s1 == s2);
System.out.println(s1 == s3);
System.out.println(s3 == s4);
System.out.println("------");
//比较字符串内容是否相同
System.out.println(s1.equals(s2));
System.out.println(s1.equals(s3));
System.out.println(s3.equals(s4));
}
}
案例
用户登录
已知用户名和密码,模拟用户登录,三次机会。
import java.util.Scanner;
import java.util.Scanner;
public class Lianxi {
public static void main(String[] args) {
//已知用户名、密码
String username = "勒布朗";
String password = "zmq123";
//次数限定
for (int i = 0; i < 3; i++) {
//录入用户名、密码
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String name = sc.nextLine();
System.out.println("请输入密码:");
String psd = sc.nextLine();
//比较
if (name.equals(username) && psd.equals(password)) {
System.out.println("登录成功");
} else {
if (2 - i == 0) {
System.out.println("你的账户被锁定,请与管理员联系");
} else
System.out.println("登录失败,你还有" + (2 - i) + "次机会");
}
}
}
}
遍历字符串
键盘录入一个字符串,使用程序实现在控制台遍历该字符串
- 获取字符串字符方法:
public char charAt(int index)
索引是从0开始的 - 获取字符串长度方法:
字符串.length()
import java.util.Scanner;
public class Lianxi {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入字符串");
String line = sc.nextLine();
for (int i = 0; i < line.length(); i++) {
System.out.println(line.charAt(i));
}
}
}
统计字符次数
键盘录入一个字符串,统计该字符串中大写字母字符,小写字母字符,数字字词出现的次数
import java.util.Scanner;
public class Lianxi {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入字符串:");
String line = sc.nextLine();
int bigcount = 0;
int smallcount = 0;
int numbercount = 0;
for (int i = 0; i < line.length(); i++) {
char ch = line.charAt(i);
if (ch >= 'A' && ch <= 'Z') {
bigcount++;
} else if (ch >= 'a' && ch <= 'z') {
smallcount++;
} else if (ch >= '0' && ch <= '9') {
numbercount++;
}
}
System.out.println("大写字母:" + bigcount + "个");
System.out.println("小写字母:" + smallcount + "个");
System.out.println("数字:" + numbercount + "个");
}
}
拼接字符串
把数组中的字符拼接为字符串显示
public class Lianxi {
public static void main(String[] args) {
String[] arr = {"我", "爱", "你"};
System.out.println("s:" + ToString(arr));
}
public static String ToString(String[] arr) {
String s = "";//设置接收字符串
s += "[";
for (int i = 0; i < arr.length; i++) {
if (i == arr.length - 1) {
s += arr[i];
} else {
s += arr[i];
s += ",";
}
}
s += "]";
return s;
}
}
字符串反转
import java.util.Scanner;
public class Lianxi {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串:");
String line = sc.nextLine();
System.out.println("结果为:" + reverse(line));
}
public static String reverse(String line) {
String s = "";
for (int i = line.length() - 1; i >= 0; i--) {
s += line.charAt(i);
}
return s;
}
}
StringBuilder类
String类进行字符串拼接操作时,每次拼接都会构造一个新的String对象,既耗时又浪费内存空间,则可以通过StringBuilder类来解决
- StringBuilder是一个可变(对象的内容可变)的字符串类,可以看作是一个容器,在堆存储,地址是相同的
eg:
public class Lianxi {
public static void main(String[] args) {
StringBuilder s1 =new StringBuilder();
System.out.println("s1:"+s1);
System.out.println("s1.length:"+s1.length());
StringBuilder s2 =new StringBuilder("勒布朗");
System.out.println("s2:"+s2);
System.out.println("s2.length:"+s2.length());
}
}
StringBuilder类的添加和翻转方法
public class Lianxi {
public static void main(String[] args) {
StringBuilder s = new StringBuilder();
//添加方法append
//StringBuilder append(任意类型):添加数据,并返回对象本身
StringBuilder s1 = s.append("勒布朗");
System.out.println("s:" + s);
System.out.println("s1:" + s1);
//上面可知s和s1是一样的,因为都砸一个容器池,地址一样在,则简化如下:
s.append("我");
s.append("爱");
s.append("你");
System.out.println("sa:" + s);
//继续简化——链式编程
s.append("勒布朗").append("我").append("爱").append("你");//无限套娃
System.out.println("sb:" + s);
//翻转方法reverse
s.reverse();
System.out.println("sc:" + s);
}
}
StringBuilder和String相互转换
让String使用StringBuilder中的方法
- StringBuilder转换为String
通过ToString方法
- String转换为StringBuilder
通过构造方法
public class Lianxi {
public static void main(String[] args) {
//StringBuilder转换String
StringBuilder s = new StringBuilder();
s.append("勒布朗");
String s1 = s.toString();
System.out.println(s1);
//String转换StringBuilder
String s2 = "勒布朗";
StringBuilder s3 = new StringBuilder(s2);
System.out.println(s3);
}
}
案例
拼接字符串
//节省空间且效率更高
public class Lianxi {
public static void main(String[] args) {
String[] arr = {"我", "爱", "你"};
System.out.println("s:" + ToString(arr));
}
public static String ToString(String[] arr) {
StringBuilder s1 = new StringBuilder();
s1.append("[");
for (int i = 0; i < arr.length; i++) {
if (i == arr.length - 1) {
s1.append(arr[i]);
} else {
s1.append(arr[i]).append(",");
}
}
s1.append("]");
String s = s1.toString();
return s;
}
}
字符串反转
import java.util.Scanner;
public class Lianxi {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串:");
String line = sc.nextLine();
System.out.println("结果为:" + reverse(line));
}
public static String reverse(String line) {
//普通过程
StringBuilder s = new StringBuilder(line);
s.reverse();
String s1 = s.toString();
return s1;
//链式编程
/*解释:new StringBuilder(line)是一个对象,则可以调用reverse()方法,此时是StringBuilder类型,用方法 toString()转换为String类型,再return返回即可*/
return new StringBuilder(line).reverse().toString();
}
}
集合
概述
集合类的特点:提供一种存储空间可变的存储模型,存储是数据容量可以发生改变
集合类举例学习
ArrayList类
- 是可调整大小的数组实现
- ArrayList:是一种特殊的数据类型——泛型;只能是引用数据类型
- eg:ArrayList
ArrayList构造方法和添加方法
import java.util.ArrayList;
public class Lianxi {
public static void main(String[] args) {
//构造方法
ArrayList<String> array = new ArrayList<>();//JDK7以后,后面的<>中不用写类型了
//添加方法boolean add(E e):将指定对象添加到此集合的末尾
array.add("我");
array.add("爱");
array.add("你");
//不可套娃,因为返回值是布尔类型
System.out.println("array:" + array);//输出:array:[我, 爱, 你];可以知道ArrayList集合重写了toString方法
//添加方法void add(int index,E element):在此集合中的指定位置插入指定的元素
array.add(1, "勒布朗");
System.out.println("array:" + array);
}
}
说明: 注意索引越界问题,上述代码"我爱你"索引值到2,后面再添加最多是3,如果array.add(4,"勒布朗");
则报错。
ArrayList集合常用方法
import java.util.ArrayList;
public class Lianxi {
public static void main(String[] args) {
//构造方法
ArrayList<String> array = new ArrayList<>();//JDK7以后,后面的<>中不用写类型了
array.add("我");
array.add("爱");
array.add("你");
//不可套娃,因为返回值是布尔类型
//public boolean remove()方法:删除指定的元素,返回是否成功
System.out.println(array.remove("我"));//true
System.out.println(array.remove("l勒布朗"));//false
System.out.println("array:" + array);
array:[爱, 你]
//public E remove(int index):删除索引处的元素,返回被删除的元素
System.out.println(array.remove(1));//爱
System.out.println(array.remove(3));//IndexOutOfBoundsException(索引越界)
System.out.println("array:" + array);//array:[我, 你]
//public E remove(int index,E element):修改指定索引处的元素,返回被修改的元素
System.out.println(array.set(0,"大家"));//我
System.out.println(array.set(3,"大家"));//IndexOutOfBoundsException(索引越界)
System.out.println("array:" + array);//array:[大家, 爱, 你]
//public E get (int index):返回指定索引处的元素
System.out.println(array.get(1));//爱
System.out.println(array.get(3));//IndexOutOfBoundsException(索引越界)
System.out.println("array:" + array);//array:[我, 爱, 你]
//public int size():返回集合中的元素个数
System.out.println(array.size());//3
System.out.println("arrat:"+array);//arrat:[我, 爱, 你]
}
}
案例
- 集合遍历
import java.util.ArrayList;
public class Lianxi {
public static void main(String[] args) {
ArrayList<String> array = new ArrayList<>();//JDK7以后,后面的<>中不用写类型了
array.add("勒布朗");
array.add("浓眉");
array.add("hhh");
//集合遍历通用格式
for (int i = 0; i < array.size(); i++) {
String s = array.get(i);
System.out.println(s);
}
}
}
- 存储学生对象并遍历
//学生类
public class Student {
private String name;
private String age;
public Student() {
}
public Student(String name, String age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
//操作
import java.util.ArrayList;
import java.util.Scanner;
public class StudentDemo {
public static void main(String[] args) {
//定义集合对象
ArrayList<Student> Arry = new ArrayList<>();
//调用方法
addStudent(Arry);
addStudent(Arry);
addStudent(Arry);
//循环输出
for (int i = 0; i < Arry.size(); i++) {
Student s = Arry.get(i);
System.out.println(s.getName() + "," + s.getAge());
}
}
public static void addStudent(ArrayList<Student> Arry) {
//输入学生姓名、年龄
Scanner sc = new Scanner(System.in);
System.out.println("请输入学生姓名");
String name = sc.nextLine();
System.out.println("请输入学生年龄");
String age = sc.nextLine();
//新建学生对象
Student s = new Student();
//给学生对象赋值
s.setName(name);
s.setAge(age);
//添加输入的学生信息
Arry.add(s);
}
}
学生管理系统
//学生类
public class Student {
private String sid;
private String name;
private String age;
private String address;
public Student() {
}
public Student(String sid, String name, String age, String address) {
this.sid = sid;
this.name = name;
this.age = age;
this.address = address;
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
//操作
import java.util.ArrayList;
import java.util.Scanner;
public class StudentManager {
public static void main(String[] args) {
ArrayList<Student> array = new ArrayList<>();
while (true) {//使界面能重复进行
System.out.println("--------欢迎来到学生管理系统--------");
System.out.println("1.添加学生");
System.out.println("2.删除学生");
System.out.println("3.修改学生");
System.out.println("4.查看所有学生");
System.out.println("5.退出");
System.out.println("请输入你的选择:");
Scanner sc = new Scanner(System.in);
String line = sc.nextLine();
switch (line) {//line类型的需要"1",字符型才行,int可以直接用1.
case "1":
addStudent(array);
break;
case "2":
deleteStudent(array);
break;
case "3":
changeStudent(array);
break;
case "4":
findStudent(array);
break;
case "5":
System.out.println("谢谢使用");
System.exit(0);//JVM退出
}
}
}
public static void addStudent(ArrayList<Student> array) {
Scanner sc = new Scanner(System.in);
//外部定义变量,使后续set方法能访问
String sid;
//为了让程序能重新回到这里
while (true) {
System.out.println("请输入学生学号:");
sid = sc.nextLine();
boolean flag = isUsed(array, sid);
if (flag) {
System.out.println("你输入的学号已经被使用了,请重新输入");
} else {
break;
}
}
System.out.println("请输入学生姓名:");
String name = sc.nextLine();
System.out.println("请输入学生年龄:");
String age = sc.nextLine();
System.out.println("请输入学生居住地:");
String address = sc.nextLine();
//创建学生对象,并赋值
Student s = new Student();
s.setSid(sid);
s.setName(name);
s.setAge(age);
s.setAddress(address);
//添加到集合中
array.add(s);
System.out.println("添加学生成功");
}
public static boolean isUsed(ArrayList<Student> array, String sid) {
boolean flag = false;
for (int i = 0; i < array.size(); i++) {
Student s = array.get(i);
if (s.getSid().equals(sid)) {
flag = true;
break;
}
}
return flag;
}
public static void findStudent(ArrayList<Student> array) {
if (array.size() == 0) {
System.out.println("无信息,请先添加信息");
return;//可以到此停止程序
}
System.out.println("学生\t\t姓名\t\t年龄\t\t居住地");//\t就是Tab键
for (int i = 0; i < array.size(); i++) {
Student s = array.get(i);
System.out.println(s.getSid() + "\t\t" + s.getName() + "\t\t" + s.getAge() + "岁\t" + s.getAddress());
}
}
public static void deleteStudent(ArrayList<Student> array) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入你要删除的学生学号:");
String Sid = sc.nextLine();
int index = -1;
for (int i = 0; i < array.size(); i++) {
Student s = array.get(i);
if (s.getSid().equals(Sid)) {
index = i;
break;
}
}
if (index == -1) {//判断学生信息在集合中是否存在
System.out.println("该信息不存在,请重新输入:");
} else {
array.remove(index);
System.out.println("删除学生成功");
}
}
public static void changeStudent(ArrayList<Student> array) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入你要修改的学生学号:");
String Sid = sc.nextLine();
int index = -1;
for (int i = 0; i < array.size(); i++) {
Student s = array.get(i);
if (s.getSid().equals(Sid)) {
index = i;
break;
}
}
if (index == -1) {//判断学生信息在集合中是否存在
System.out.println("该信息不存在,请重新输入:");
return;//后续不再执行
}
System.out.println("请输入学生新姓名:");
String name = sc.nextLine();
System.out.println("请输入学生新年龄:");
String age = sc.nextLine();
System.out.println("请输入学生新居住地:");
String address = sc.nextLine();
Student s = new Student();
s.setSid(Sid);
s.setName(name);
s.setAge(age);
s.setAddress(address);
//遍历集合修改学生信息
for (int i = 0; i < array.size(); i++) {
Student student = array.get(i);
if (student.getSid().equals(Sid)) {
array.set(i, s);
break;
}
}
System.out.println("修改学生成功");
}
}
面向对象
继承
概述
格式: public class 子类名 extends 父类名{}
说明: 父类也被称为基类、超类;子类也被称为派生类
继承中子类特点: 子类可以继承父的内容;子类还可以有自己特有的内容
继承的好处和弊端
- 好处:提高了代码的复用性;提高来了代码的维护性
- 弊端:继承让类与类之间产生了关系,类的耦合性增强了,当父类发生变化时子类实现也不得不跟着变化,削弱了子类的独立性
什么时候使用继承:当存在从属关系时使用; 例如苹果类是水果类的一种、猫类是动物类的一种。
继承中变量的访问特点
在子类方法中访问一个变量
- 先在子类内部找,即子类方法内部找
- 再在子类成员变量找
- 再在父类成员范围找
- 可能再往父类的父类找……
继承中构造方法的访问特点
- 子类中所有的构造方法默认都会访问父类中无参的构造方法
- 因为子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,会先完成父类数据的初始化
- 每一个子列=类构造方法法第一条语句默认都是:
super()
即默认访问无参构造方法
继承中成员方法的访问特点
在子类对象中访问一个方法
- 先在子类成员范围找
- 再在父类成员范围找
- 可能再往父类的父类找……
super关键字
super关键字与this关键字用法相似
- this表示本类对象的引用
- super表示父类存储空间的标识(可以理解为父类对象引用;即传给父类)
关键字 | 访问成员变量 | 访问构造方法 | 访问成员变量 |
---|---|---|---|
this | this.成员变量;访问本类成员变量 | this(…);访问本类构造方法 | this.成员方法(…);访问本类成员方法 |
super | super.成员变量;访问父类成员变量 | super();访问父类构造方法 | super.成员方法(…);访问父类成员方法 |
示例
//父类
public class Fu {
public int age = 40;
}
//子类
public class Zi extends Fu {
public int age = 20;
public void show() {
int age = 30;
//利用this访问本类的成员变量的访问
System.out.println(this.age);
//利用super访问父类的成员变量
System.out.println(super.age);
}
}
//测试类
public class Demo {
public static void main(String[] args) {
Zi z = new Zi();
z.show();
}
}
super内存分配
栈区:main方法 | 堆区:初始化数据 |
---|---|
子方法 | |
父方法 | |
普通方法 |
方法重写
概述:子类中出现了和父类一模一样的方法声明
方法重写的的应用
- 当子类需要父类的功能,而功能主体子类有自己特定的内容时,可以重写父类中的方法,这样既沿袭了父类的功能,又定义了子类特有的内容
注解:@Override
- 可以帮助我们检查重写方法的方法声明正确性
重写快捷键:输入重写的方法名即会有提示
示例
//初始手机类
public class Phone {
public void call(String name) {
System.out.println("给" + name + "打电话");
}
}
//重写手机类
public class NewPhone extends Phone {
@Override//注解
public void call(String name) {
System.out.println("开启视频功能");
super.call(name);
}
}
//测试类
public class Demo {
public static void main(String[] args) {
Phone p = new Phone();
p.call("勒布朗");
NewPhone np = new NewPhone();
np.call("勒布朗");
}
}
/*输出:
给勒布朗打电话
开启视频功能
给勒布朗打电话*/
方法重写注意事项
- 私有方法不能被重写(父类的私有成员子类是不能被继承的)
- 子类方法访问权限不能比父类低(
public>protected>default(默认,不写)>private
)
Java中继承注意事项
- Java中类只支持单继承,不支持多继承
- Java中类支持多层继承(子类继承父类,父类再作为子类继承父类,则开始子类则继承了父类以及父类的父类的方法)
案例
- 老师和学生
//Person类
public class Person {
private String name;
private int age;
//无参构造
public Person() {
}
//带参构造
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
//Teacher类
public class Teacher extends Person {
//无参构造
public Teacher() {
}
//带参构造
public Teacher(String name, int age) {
super(name, age);
}
public void teach() {
System.out.println("教书育人");
}
}
//Student类
public class Student extends Person {
//无参构造
public Student() {
}
//带参构造
public Student(String name, int age) {
super(name, age);
}
public void study() {
System.out.println("好好学习,天天向上");
}
}
//测试类
public class Test {
public static void main(String[] args) {
//老师类
//无参
Teacher t1 = new Teacher();
t1.setName("勒布朗");
t1.setAge(37);
System.out.println(t1.getName() + "," + t1.getAge());
t1.teach();
//有参继承
Teacher t2 = new Teacher("包老师", 36);
System.out.println(t2.getName() + "," + t2.getAge());
t2.teach();
//学生类
//无参
Student s1 = new Student();
s1.setName("小居");
s1.setAge(21);
System.out.println(s1.getName() + "," + s1.getAge());
s1.study();
//有参继承
Student s2 = new Student("小居", 21);
System.out.println(s2.getName() + "," + s2.getAge());
s2.study();
}
}
- 猫和狗
//Animal类
public class Animal {
private String name;
private int age;
public Animal() {
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
//Cat类
public class Cat extends Animal {
public Cat() {
}
public Cat(String name, int age) {
super(name, age);
}
public void catchMouse() {
System.out.println("抓老鼠");
}
}
//Dog类
public class Dog extends Animal{
public Dog() {
}
public Dog(String name, int age) {
super(name, age);
}
public void lookDoor(){
System.out.println("看门");
}
}
//测试类
public class Test {
public static void main(String[] args) {
Cat c1 = new Cat();
c1.setName("加菲猫");
c1.setAge(5);
System.out.println(c1.getName() + "," + c1.getAge());
c1.catchMouse();
Cat c2 = new Cat("折耳猫", 4);
System.out.println(c2.getName() + "," + c2.getAge());
c2.catchMouse();
Dog d1 = new Dog();
d1.setName("牧羊犬");
d1.setAge(7);
System.out.println(d1.getName() + "," + d1.getAge());
d1.lookDoor();
Dog d2 = new Dog("茶杯犬", 3);
System.out.println(d2.getName() + "," + d2.getAge());
d2.lookDoor();
}
}
修饰符
包
概述
包其实就是文件夹;用于对类的分类管理
包的格式定义
格式: package 包名 (多级包用.分开)
eg: packag Demo;
包的使用(记事本)
导包
概述
使用不用包下的类时,使用的时候要写类的全路径,eg:java.util.Scanner sc=new java.util.Scanner(System.in);
写起来麻烦,所以就可以利用导包
导包格式
格式: import 包名
权限修饰符
修饰符 | 同一个类中 | 同一个包中子类/无关类 | 不同包的子类 | 不同包的无关类 |
---|---|---|---|---|
private | √ | |||
默认(default,不写) | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
状态修饰符
final 关键字
是最终的意思,可以修饰成员方法,成员变量,类
- 特点
- 修饰方法:表明该方法是最终方法,不能被重写
- 修饰变量:表明该变量是常量,不能被再次赋值
- 修饰类:表明该类是最终类,不能被继承
-
特别
-
变量时基本类型:final修饰指的是基本类型的数据值不能发生改变
-
变量是引用类型:final修饰指的是引用类型的地址值不能发生改变,但是地址里面的内容是可以发生改变的
static 关键字
是静态的意思,可以修饰成员方法,成员变量
- 特点
- static修饰的被类所有对象共享;便于使用类名调用
- 可以通过对象名调用,但建议使用类名调用
eg:
//学生类
public class Student {
public String name;
public int age;
public static String university;
public void show() {
System.out.println(name + "," + age + "," + university);
}
}
//测试类
public class StudentManager {
public static void main(String[] args) {
//类名调用
Student.university = "南航";
Student s1 = new Student();
s1.name = "勒布朗";
s1.age = 37;
//s1.university = "南航";
s1.show();
Student s2 = new Student();
s2.name = "小居";
s2.age = 21;
//s2.university = "南航";
s2.show();
}
}
static 关键字访问特点
- 非静态的成员方法可以访问静态或非静态的成员方法/变量
- 静态成员方法只能访问静态成员
多态
概述
同一个对象,在不同时刻表现出来的不同形态
eg:猫是猫:猫 cat = new 猫();
、猫是动物:动物 animal = new 猫();
多态的前提和体现:
- 有继承/实现关系
- 有方法重写
- 有父类引用子类对象:
动物 animal = new 猫();
多态类形式:具体类多态(几乎不用)、抽象类多态、接口多态
多态中成员访问特点
- 成员变量: 编译看左边,执行看左边
- 成员方法: 编译看左边,执行看右边
编译均从左边(父类)看起
原因: 成员方法有重写,而成员变量没有
示例:
//Animal类,父类
public class Animal {
public int age = 5;
public void eat() {
System.out.println("动物吃东西");
}
}
//Cat类,子类
public class Cat extends Animal {
//重新定义成员变量
public int age = 7;
public int weight = 10;
//重写方法
@Override
public void eat() {
System.out.println("猫吃鱼");
}
public void playGame() {
System.out.println("猫捉迷藏");
}
}
//Test类
public class Test {
public static void main(String[] args) {
Animal a = new Cat();
System.out.println(a.age);//输出5,因为成员变量执行父类
//System.out.println(a.weight);会报错,因为成员变量均从父类看起
a.eat();//输出猫吃鱼,因为方法重写了
//a.playGame;会报错,因为父类没有这个方法
}
}
多态的好处和弊端
- 好处:提高了程序的扩展性(定义的方法的时候使用父类型作为参数,使用的时候,具体子类型参与操作)
- 弊端:不能使用子类的特有功能
多态中的转型
- 向上转型: 从子到父,父类引用指向子类对象
- 向下转型: 从父到子,父类引用转为子类对象;则可以消除弊端,能访问子类功能了
示例
//Animal类
public class Animal {
public void eat() {
System.out.println("动物吃东西");
}
}
//Cat类
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
public void playGame() {
System.out.println("猫捉迷藏");
}
}
//Test类
public class AnimalDemo {
public static void main(String[] args) {
//向上转型,即多态
Animal a = new Cat();
a.eat();
//向下转型
Cat c = (Cat) a;//(Cat) a即把a强转为子类(Cat类)对象,然后赋值给c;类似于强制转换
//a能转型,因为a是Cat的方法,如果是其他方法则会报错,不能强制转换
c.eat();
c.playGame();
}
}
多态转型的内存图解
案例
- 猫和狗
//Animal类
public class Animal {
private String name;
private int age;
public Animal() {
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void eat() {
System.out.println("动物吃东西");
}
}
//Cat类
public class Cat extends Animal {
public Cat() {
}
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
//Dog类
public class Dog extends Animal {
public Dog() {
}
public Dog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("狗吃骨头");
}
}
//Test类
public class AnimalDemo {
public static void main(String[] args) {
//猫类,无参
Animal a = new Cat();
a.setName("加菲");
a.setAge(5);
System.out.println(a.getName() + "," + a.getAge());
a.eat();
//有参
a = new Cat("加菲", 5);
// 不用Animal a=new Cat("折耳猫",3);因为Animal是新建了一类对象,不同于继承,只new了一个对象
System.out.println(a.getName() + "," + a.getAge());
a.eat();
//狗类,无参
Animal d = new Dog();
d.setName("哈巴");
d.setAge(6);
System.out.println(d.getName() + "," + d.getAge());
d.eat();
//有参
d = new Dog("哈巴", 6);
System.out.println(d.getName() + "," + d.getAge());
d.eat();
}
}
抽象类
概述
在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类
特点
- 抽象类和抽象方法必须使用
abstract
关键字修饰
eg:public abstract class 类名{}
;public abstract void eat();
- 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
- 抽象类不能直接实例化,但可以参照多态方式,通过子类对象实例化,即抽象类多态
- 作为抽象类的子类,要么重写抽象类中的所有抽象方法,要么是抽象类
代码解释:
//Animal类
//抽象类
public abstract class Animal {
//抽象方法,无方法体
public abstract void eat();
//普通方法,有方法体
public void sleep() {
System.out.println("睡觉");
}
}
//Cat类
public class Cat extends Animal {//重写抽象类中的所有抽象方法作为子类
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
//Dog类
public abstract class Dog extends Animal {//抽象类作子类
}
//Test类
public class AnimalDemo {
public static void main(String[] args) {
//Animal a=new Animal();报错,抽象类不能直接实例化
Animal a = new Cat();//通过子类对象实例化
a.eat();
a.sleep();
}
}
成员特点
- 成员变量可以是变量,也可以是常量
- 构造方法可以有,但不能实例化,用于子类访问父类数据的初始化
- 成员方法可以是抽象方法,用于限定子类必须完成某类操作;可以是非抽象方法,提高代码复用性
代码解释:
//Animal类
public abstract class Animal {
private int age = 20;//成员变量
private final String city = "上海";//成员常量,因为有final修饰
//无参构造方法
public Animal() {
}
//带参构造方法
public Animal(int age) {
this.age = age;
}
//非抽象方法
public void show() {
age = 40;
System.out.println(age);
//city="北京";不能重新赋值,因为final修饰后为常量,不可更改
System.out.println(city);
}
//抽象方法
public abstract void eat();//限定子类完成eat操作
}
//Cat类
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
//Test类
public class AnimalDemo {
public static void main(String[] args) {
//Animal a=new Animal();报错,抽象类不能直接实例化
Animal a = new Cat();//通过子类对象实例化
a.eat();
a.show();
}
}
案例
- 猫和狗
//Animal抽象类
public abstract class Animal {
private String name;
private int age;
public Animal() {
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public abstract void eat();//限定子类完成eat操作
}
//Cat类
public class Cat extends Animal {//重写抽象类中的所有抽象方法作为子类
public Cat() {
}
public Cat(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
//Dog类
public class Dog extends Animal {//重写抽象类中的所有抽象方法作为子类
public Dog() {
}
public Dog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("狗吃骨头");
}
}
//Test类
public class AnimalDemo {
public static void main(String[] args) {
//猫类,无参
Animal a = new Cat();
a.setName("加菲");
a.setAge(5);
System.out.println(a.getName() + "," + a.getAge());
a.eat();
//有参
a = new Cat("加菲", 5);
System.out.println(a.getName() + "," + a.getAge());
a.eat();
//狗类,无参
Animal d = new Dog();
d.setName("哈巴");
d.setAge(6);
System.out.println(d.getName() + "," + d.getAge());
d.eat();
//有参
d = new Dog("哈巴", 6);
System.out.println(d.getName() + "," + d.getAge());
d.eat();
}
}
接口
概述
接口就是一种公共的规范标准,只要复合规范标准,大家都可以使用
Java中的接口更多体现在对行为的抽象
特点
- 接口用关键子interface修饰; eg:
public interface 接口名{}
- 类实现接口用implements表示; eg:
public class 类名 implements 接口名{}
- 抽象类不能直接实例化,但可以参照多态方式,通过子类对象实例化,即接口多态
- 作为接口的实现类,要么重写抽象类中的所有抽象方法,要么是抽象类
代码解释:
//Jumping接口
public interface Jumping {
//定义抽象方法
public abstract void jump();
}
//Cat类
public class Cat implements Jumpping {//实现接口并重写方法作为接口的实现类
@Override
public void jump() {
System.out.println("猫可以跳高了");
}
}
//Dog类
public abstract class Dog implements Jumpping {//抽象类作为接口的实现类
}
//Test类
public class JumppingDemo {
public static void main(String[] args) {
Jumpping j = new Cat();//通过子类对象实例化;多态方法
j.jump();
}
}
成员特点
- 成员变量只能是变量,默认修饰符为:
public staic final
- 接口没有构造方法,因为接口主要是对行为进行抽象的,是没有具体存在的
说明:一个类如果没有父类,默认继承子Object类(祖宗类)
- 成员方法只能是抽象方法,默认修饰符为:
public abstract
代码解释:
//接口
public interface Inter {
/*
public int num = 10;
public final int num2 = 20;
public static final int num3 = 30;//等价为int num3 =30;
*/
int num4 = 40;//默认修饰,则只能是变量
//public Inter(){} 接口不存在构造方法
//public void show(){} 接口不存在非抽象方法
public abstract void method();//抽象方法
void show();//可以这么写,因为默认public abstract修饰
}
//接口实现类
public class Interimpl implements Inter {
//等价于public class Interimpl extends Object implements Inter
public Interimpl() {
super();//继承自Object类
}
@Override
public void method() {//重写method方法
System.out.println("method");
}
@Override
public void show() {//重写show方法
System.out.println("show");
}
}
//Test类
public class Demo {
public static void main(String[] args) {
Inter i = new Interimpl();
//i.num=20;
System.out.println(i.num);
//i.num2=40;
System.out.println(i.num2);
System.out.println(Inter.num);
}
}
案例
- 猫和狗
//接口
public interface Jumping {
void jump();//定义一个接口jump,这是简写,等价于public abstract void jump();
}
//Animal抽象类
public abstract class Animal {
private String name;
private int age;
public Animal() {
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public abstract void eat();//限定子类完成eat操作
}
//
public class Cat extends Animal implements Jumping {//继承父类并实现接口,都需要重写方法
public Cat() {
}
public Cat(String name, int age) {
super(name, age);
}
@Override
public void jump() {
System.out.println("猫会跳高");
}
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
//Dog类
public class Dog extends Animal implements Jumping {//继承父类并实现接口,都需要重写方法
public Dog() {
}
public Dog(String name, int age) {
super(name, age);
}
@Override
public void jump() {
System.out.println("狗会跳高");
}
@Override
public void eat() {
System.out.println("狗吃骨头");
}
}
//Test类
public class Demo {
public static void main(String[] args) {
//多态方法实例化接口
Jumping j = new Cat();
j.jump();
//j.eat();报错,因为接口多态只能调用接口中的方法
//且接口无法调用get/set方法,因为接口中没有
//猫类,无参
Animal a = new Cat();
a.setName("加菲");
a.setAge(5);
System.out.println(a.getName() + "," + a.getAge());
a.eat();
//a.jump();报错,抽象类多态只能调用抽象类中的方法
//有参
a = new Cat("加菲", 5);
System.out.println(a.getName() + "," + a.getAge());
a.eat();
//狗类,无参
Animal d = new Dog();
d.setName("哈巴");
d.setAge(3);
System.out.println(d.getName() + "," + d.getAge());
d.eat();
//有参
a = new Dog("哈巴", 3);
System.out.println(d.getName() + "," + d.getAge());
d.eat();
//以上方法不是习惯用法,因为不能一次完整调取所有方法
//一般用具体类实现,因为具体的类既继承了方法又实现了接口,可以调取更多的方法;如下
Cat c = new Cat();
c.setName("加菲");
c.setAge(5);
System.out.println(c.getName() + "," + c.getAge());
c.eat();
c.jump();//均可以调用
Dog s = new Dog();
s.setName("哈巴");
s.setAge(3);
System.out.println(s.getName() + "," + s.getAge());
s.eat();
s.jump();//均可以调用
}
}
类和接口的关系
- 类与类的关系:继承关系;只能单继承,但是可以多层继承
- 类与接口的关系:可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
- 接口和接口的关系:继承关系;可以单继承,也可以多继承
代码解释:
//接口1
public interface Inter1 {
}
//接口2
public interface Inter2 {
}
//接口3
public interface Inter3 extends Inter1, Inter2 {//接口继承关系
}
//接口实现类
public class Interimpl extends Object implements Inter1, Inter2, Inter3 {//类的继承,实现关系
}
抽象类和接口的区别
- 成员区别
抽象类:变量、常量;构造方法、抽象方法、非抽象方法
接口:常量;抽象方法
- 关系区别
类与类:继承;单继承、多层继承
类与接口:实现;单实现、多实现
接口与接口:继承;单继承、多继承
抽象类:对类抽象,即对事物的抽象;包括行为、属性
接口:对行为抽象,主要是行为
案例:运动员和教练
有乒乓球运动员和篮球运动员,乒乓球教练和篮球教练,要求与乒乓球相关的人员都要学习英语
//Person类
public abstract class Person {//抽象父类
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public abstract void eat();//限定子类完成eat操作
}
//Player类
public abstract class Player extends Person {//抽象类继承Person类
public Player() {
}
public Player(String name, int age) {
super(name, age);
}
public abstract void study();//抽象方法,便于子类实现
}
//Coach类
public abstract class Coach extends Person {//抽象类继承Person类
public Coach() {
}
public Coach(String name, int age) {
super(name, age);
}
public abstract void teach();//抽象方法,便于子类实现
}
//SpeakEnglish接口
public interface SpeakEnglish {
public abstract void speak();//提供接口
}
//PingPangPlayer类
public class PingPangPlayer extends Player implements SpeakEnglish {//抽象类继承Player类并实现接口
public PingPangPlayer() {
}
public PingPangPlayer(String name, int age) {
super(name, age);
}
//重写方法
@Override
public void eat() {
System.out.println("乒乓球运动员吃营养餐");
}
@Override
public void study() {
System.out.println("乒乓球运动员学习乒乓球");
}
@Override
public void speak() {
System.out.println("乒乓球运动员说英语");
}
}
//PingPangCoach类
public class PingPangCoach extends Coach implements SpeakEnglish {//抽象类继承Coach类并实现接口
public PingPangCoach() {
}
public PingPangCoach(String name, int age) {
super(name, age);
}
//重写方法
@Override
public void eat() {
System.out.println("乒乓球教练吃营养餐");
}
@Override
public void teach() {
System.out.println("乒乓球教练教乒乓");
}
@Override
public void speak() {
System.out.println("乒乓球教练说英语");
}
}
//BasketballPlayer类
public class BasketballPlayer extends Player {//抽象类继承Player类
public BasketballPlayer() {
}
public BasketballPlayer(String name, int age) {
super(name, age);
}
//重写方法
@Override
public void eat() {
System.out.println("篮球运动员吃营养餐");
}
@Override
public void study() {
System.out.println("篮球运动员学习篮球");
}
}
//BasketballCoach类
public class BasketballCoach extends Coach {//抽象类继承Coach类
public BasketballCoach() {
}
public BasketballCoach(String name, int age) {
super(name, age);
}
//重写方法
@Override
public void eat() {
System.out.println("篮球教练吃营养餐");
}
@Override
public void teach() {
System.out.println("篮球教练教篮球");
}
}
//Test类
public class Demo {
public static void main(String[] args) {
//乒乓球运动员无参
PingPangPlayer p = new PingPangPlayer();
p.setName("马龙");
p.setAge(33);
p.eat();
p.study();
p.speak();
//有参
PingPangPlayer p1 = new PingPangPlayer("马龙", 33);
p1.eat();
p1.study();
p1.speak();
//乒乓球教练无参
PingPangCoach c = new PingPangCoach();
c.setName("刘国梁");
c.setAge(45);
c.eat();
c.teach();
c.speak();
//有参
PingPangCoach c1 = new PingPangCoach("刘国梁", 45);
c1.eat();
c1.teach();
c1.speak();
//篮球运动员无参
BasketballPlayer b = new BasketballPlayer();
b.setName("勒布朗");
b.setAge(37);
b.eat();
b.study();
//有参
BasketballPlayer b1 = new BasketballPlayer("勒布朗", 37);
b1.eat();
b1.study();
//篮球教练无参
BasketballCoach c2 = new BasketballCoach();
c2.setName("杜峰");
c2.setAge(46);
c2.eat();
c2.teach();
//有参
BasketballCoach c3 = new BasketballCoach("杜峰", 46);
c3.eat();
c3.teach();
}
}
内部类
形参和返回值
类名作为形参和返回值
- 方法的形参为类名,其实需要的就是该类的对象
- 方法的返回值为类名,其实返回的就是该类的对象
代码演示:
//Cat类
public class Cat {
public void eat() {
System.out.println("猫吃鱼");
}
}
//中间类
public class CatOperator {
//方法的形参为类名
public void useCat(Cat c) {//相当于接收了Cat c = new Cat();
c.eat();//对象c调用方法
}
//方法的返值为类名
public Cat getCat() {//要求返回Cat类型
Cat c = new Cat();//实质还是返回对象,没对象,所以要new一个
return c;//实质还是返回对象;这里c相当于new Cat()
}
}
//Test类
public class AnimalDemo {
public static void main(String[] args) {
CatOperator co = new CatOperator();
Cat c = new Cat();//实质要调用对象,没对象,所以要new一个
co.useCat(c);//相当于把new Cat传递给这个方法
Cat c1 = co.getCat();//相当于new Cat()赋值给了c1
c1.eat();
}
}
抽象类名作为形参和返回值
- 方法的形参是抽象类名,其实需要的就是该抽象类的子类对象
- 方法的返回值是抽象类名,其实返回的就是该抽象类的子类对象
说明:因为抽象类不能直接实例化,则参照多态方式,通过子类对象实例化;理解与类名作为形参和返回值相同
代码演示:
//Animal抽象类
public abstract class Animal {
public abstract void eat();
}
//Cat继承并重写方法,因为抽象列不能直接实例化,要借助子类实例化对象
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
//中间类
public class AnimalOperator {
//方法的形参为类名
public void useAnimal(Animal a) {//相当于传过来Animal a = new Cat();
a.eat();//对象a调用方法;
}
//方法的返回值为类名
public Animal getAnimal() {//要求返回Animal类型
Animal a = new Cat();//实质还是返回对象,没对象,所以要new一个;多态新建对象方法
return a;
}
}
//
public class AnimalDemo {
public static void main(String[] args) {
AnimalOperator ao = new AnimalOperator();
Animal a = new Cat();//实质要调用对象,没对象,所以要new一个;多态新建对象方法
ao.useAnimal(a);//相当于把new Cat传递给这个方法
Animal a2 = ao.getAnimal();//相当于new Cat()赋值给了c1
a2.eat();//编译看左边,执行看右边
}
}
接口作为形参和返回值
- 方法的形参是接口名,其实需要的就是该接口的实现类对象
- 方法的返回值是接口名,其实返回的就是该接口的实现类对象
说明:因为接口不能直接实例化,则参照多态方式,通过子类对象实例化;理解与类名作为形参和返回值相同
代码演示:
//Jumping接口
public interface Jumping {
void jump();
}
//Cat类实现接口
public class Cat implements Jumping {
@Override
public void jump() {
System.out.println("猫可以跳高了");
}
}
//中间类
public class JumpingOperator {
//方法的形参为接口名
public void useJumping(Jumping j) {//相当于传过来umping j = new Cat();
j.jump();//对象j调用方法;
}
//方法的返回值为接口名
public Jumping getJumping() {//要求返回Jumping接口类型
Jumping j = new Cat();//实质还是返回对象,没对象,所以要new一个;多态新建对象方法
return j;
}
}
//
public class Demo {
public static void main(String[] args) {
JumpingOperator jo = new JumpingOperator();
Jumping j = new Cat();//实质要调用对象,没对象,所以要new一个;多态新建对象方法
jo.useJumping(j);//相当于把new Cat传递给这个方法
Jumping j1 = jo.getJumping();//相当于new Cat()赋值给了j1
j1.jump();//编译看左边,执行看右边
}
}
内部类
概述
- 内部类:就是在一个类中定义一个类
- 内部类的访问特点:
- 内部类可以直接访问外部类的成员,包括私有
- 外部类要访问内部类的成员,必须创建对象
代码演示:
public class Lianxi {
private int num = 10;
//内部类
public class Inter {
public void show() {//不能直接输出,因为没有main方法
System.out.println(num);//内部类访问外部类成员
}
}
//外部类访问内部类成员
public void method() {
Inter i = new Inter();
i.show();
}
}
成员内部类
- 在类的成员位置
- 外界创建对象访问内部类
格式:外部类名.内部类名 对象名 = 外部类对象. 内部类对象;
eg:Outer.Inner io = new Outer().new Inner();
代码演示:
//定义类
public class Lianxi {
private int num = 10;
//内部类通常是为了隐藏不让外部看见,所以通常不用public修饰
/*public class Inter {
public void show() {
System.out.println(num);
}
}*/
//通常用private修饰
private class Inter {
public void show() {
System.out.println(num);
}
}
//外部类方法
public void method() {
Inter i = new Inter();
i.show();
}
}
//Test类
public class Demo {
public static void main(String[] args) {
/*Lianxi.Inter io = new Lianxi().new Inter();外界创建对象访问内部类形式
io.show();*/
Lianxi o = new Lianxi();
o.method();//常用形式:间接调用;调用method方法,method方法内部又调用内部类方法
}
}
局部内部类
局部内部类是在方法中定义的类,外界是无法直接使用的,需要在方法内部创建对象并使用;
该类可以直接访问外部类的成员,也可以访问方法内的局部变量
//定义类
public class Lianxi {
private int num = 10;//外部类的成员
public void method() {
int num1 = 20;//方法内的局部变量
//在方法内部定义的类
class Inter {//class声明时,类名称可以与文件名称不一致
public void show() {
System.out.println(num);
System.out.println(num1);
}
}
//需要在方法内部创建对象并使用
Inter i = new Inter();
i.show();
}
}
//Test类
public class Demo {
public static void main(String[] args) {
Lianxi o = new Lianxi();
o.method();
}
}
匿名内部类
匿名内部类是局部内部类一种特殊形式
前提:存在一个类或者接口,这里的类可以是具体类也可以是抽象类
本质:是一个继承了该类或者实现了该接口的子类匿名对象,只是对象没有名字而已
格式:
new 类名或者接口名(){
重写方法;
};//注意分号,匿名内部类的本质是一个继承了该类或者实现了该接口的子类匿名对象
代码演示:
//接口
public interface Inter {
void show();
}
//定义类
public class Lianxi {
private int num = 10;
public void method() {
new Inter() {
@Override
public void show() {
System.out.println("匿名内部类");
}
}.show();//对象调用方法
Inter i = new Inter() {//把对象赋值给接口;多态形式
@Override
public void show() {
System.out.println("匿名内部类");
}
};
i.show();//多次调用
i.show();
}
}
//Test类
public class Demo {
public static void main(String[] args) {
Lianxi o = new Lianxi();
o.method();
}
}
匿名内部类在开发中的使用
代码演示:
//接口
public interface Jumping {
void jump();
}
//普通方法:使用实现类实现接口
public class Cat implements Jumping{
@Override
public void jump() {
System.out.println("猫可以跳高了");
}
}
中间类
//
public class JumpingOperator {
public void method(Jumping j) {
j.jump();
}
}
//Test类
public class Demo {
public static void main(String[] args) {
JumpingOperator jo = new JumpingOperator();
Jumping j = new Cat();//类名作为传入值,参照上面解释
jo.method(j);//普通形式:利用实现类调用方法
/*
new Jumping() {
@Override
public void jump() {
System.out.println("猫可以跳高了");
}
}
*/
//相当把上面的匿名内部类作为对象,实现的重写方法并且调用,就可以不必写实现类了
jo.method(new Jumping() {
@Override
public void jump() {
System.out.println("猫可以跳高了");
}
});
//参照上面,不必创建Dog类,也可以实现重写方法并被调用
jo.method(new Jumping() {
@Override
public void jump() {
System.out.println("狗可以跳高了");
}
});
}
}
常用API
Math类
Math包含执行基本数字运算的方法
常用方法:
方法名 | 说明 |
---|---|
public static int abs(int a) | 返回参数的绝对值 |
public static double ceil(double a) | 返回大于或等于参数的最小double值,等于一个整数 |
public static double floor(double a) | 返回小于或等于参数的最大double值,等于一个整数 |
public static int round(float a) | 按照四舍五入返回最接近参数的int值 |
public static int max(int a,int b) | 返回两个int值中的较大者 |
public static int min(int a,int b) | 返回两个int值中的较小者 |
public static double pow(double a,double b) | 返回a和b次幂的值 |
public static double random() | 返回值为double的正值,[0.0,1.0) |
示例:
System.out.println((int) (Math.random() * 100) + 1);//形成1-100的随机数
System类
System包含几个有用的类字段和方法,它不能被实例化
常用方法:
方法名 | 说明 |
---|---|
public static void exit(int status) | 终止当前运行的Java虚拟机,非零表示异常终止 |
public static long currentTimeMillis() | 返回当前时间,当前时间与UTC时间1970年1月1日午夜之间的差异,以毫秒为单位。 |
示例:
System.exit(0);//终止程序
//当前时间与1970年1月1日的差值
System.out.println(System.currentTimeMillis() * 1.0 / 1000 / 60 / 60 / 24 / 365 + "年");
//计算运行这个for循环所需要的时间
long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
System.out.println(i);
}
long end = System.currentTimeMillis();
System.out.println("共耗时" + (end - start) + "毫秒");
Object类
Object是类层次结构的根,每个类都可以将Object作为超类(父类)。所有类都直接或者间接的继承该类;
有唯一构造方法:public Object()
;即所有类的顶级父类只有无参构造方法
常用方法:
方法名 | 说明 |
---|---|
public String toString() | 返回对象的字符串表示形式;建议所有子类重写该方法(自动生成) |
public boolean equals(Object obj) | 比较对象是否相等;默认比较地址,重写可以比较内容(自动生成) |
Object类中toString方法
public String toString()
返回对象的字符串表示形式。 通常, toString
方法返回一个“文本表示”此对象的字符串。 结果应该是简洁但信息丰富的表示,便于人们阅读。 建议所有子类都重写此方法。
代码演示:
//Student类
public class Student {//默认继承Object类
private String name;
private int age;
//构造方法
//无参
public Student() {
}
//带参
public Student(String name, int age) {
this.name = name;
this.age = age;
}
//成员方法
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void show() {
System.out.println(name + "," + age);
}
//重写toString方法
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
//Test类
public class Demo {
public static void main(String[] args) {
Student s = new Student();
s.setName("勒布朗");
s.setAge(37);
System.out.println(s);
//原本输出:全路径名@地址
//重写toString方法后输出:Student{name='勒布朗', age=37}
/*
println方法
public void println(Object x) {
String s = String.valueOf(x);
synchronized (this) {
print(s);
newLine();
}
}
valueOf方法
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
toString方法
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
*/
}
}
Object类中to String方法
代码演示:
//Student类
public class Student {
private String name;
private int age;
//构造方法
//无参
public Student() {
}
//带参
public Student(String name, int age) {
this.name = name;
this.age = age;
}
//成员方法
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void show() {
System.out.println(name + "," + age);
}
@Override
public boolean equals(Object o) {
/*
这里 this 指 s;
o 指 s1
*/
//比较地址是否相同
if (this == o) return true;
//判断参数是否相同 || 判断两个对象是否来自同一个类
if (o == null || getClass() != o.getClass()) return false;
//向下转型
Student student = (Student) o;//此时Student = s1
//比较s和s1的年龄是否相同
if (age != student.age) return false;
//比较s和s1的姓名是否相同
return name != null ? name.equals(student.name) : student.name == null;
}
}
//Test类
public class Demo {
public static void main(String[] args) {
Student s = new Student();
s.setName("勒布朗");
s.setAge(37);
Student s1 = new Student();
s1.setName("勒布朗");
s1.setAge(37);
System.out.println(s.equals(s1));//不重写equals方法则输出false,因为Object中equals方法默认比较的是地址值
}
}
Arrays类
Arrays类包含用于操作数组的各种方法
常用方法:
方法名 | 说明 |
---|---|
public static String toString(int[] a) | 返回指定数组的内容的字符串表示形式 |
public static void sort(int[] a) | 按照数字顺序排列指定的数组 |
代码演示:
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
int[] arr = {24, 69, 80, 57, 13};
System.out.println("排序前:" + Arrays.toString(arr));
Arrays.sort(arr);
System.out.println("排序后:" + Arrays.toString(arr));
}
}
工具类的设计思想:
- 构造方法用
private
修饰,为了防止外界创建对象 - 成员用
public static
修饰,为了可以使用类名访问成员方法
基本类型包装类
将基本数据类型封装成对象的好处在于可以在对象中定义更多的功能方法操作该数据
常用操作之一:用于基本数据类型与字符串之间的转换
基本数据类型和包装类型的区别
- 包装类是对象,拥有方法和字段,对象的调用都是通过引用对象的地址;基本类型不是
- 包装类型是引用的传递;基本类型是值的传递
- 声明方式不同:
基本数据类型不需要new关键字;
包装类型需要new在堆内存中进行new来分配内存空间 - 存储位置不同:
基本数据类型直接将值保存在值栈中;
包装类型是把对象放在堆中,然后通过对象的引用来调用他们 - 初始值不同:
int的初始值为 0 、 boolean的初始值为false
包装类型的初始值为null - 使用方式不同:
基本数据类型直接赋值使用就好;
包装类型是在集合如 coolectionMap时使用
基本对应关系:
基本数据类型 | 包装类 |
---|---|
byteB | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
Integer类
Integer:包装一个对象中原始类型int的值
常用方法:
方法名 | 说明 |
---|---|
public static Integer valueOf(int i) | 返回表示指定的int值的Integer实例 |
public static Integer valueOf(String s) | 返回一个保存指定值的Integer对象String |
代码演示:
public class Test {
public static void main(String[] args) {
Integer i1 = Integer.valueOf(100);
System.out.println(i1);
Integer i2 = Integer.valueOf("abc");//报错:NumberFormatException;只能是数字类型的字符串
Integer i2 = Integer.valueOf("123");
System.out.println(i2);
}
}
int和String的相互转换
- int转换为String
方法:public static String valueOf(int i)
:返回int参数是字符串表示形式;该方法是String类中的方法
- String转为int
方法:public static int parseInt(String s)
:将字符串解析为int类型;该方法是Integer类中的方法
代码演示:
public class Test {
public static void main(String[] args) {
//int转为String
int number = 100;
//方式1
String s = "" + number;//空字符串连接
System.out.println(s);
//方式2(推荐)
String s2 = String.valueOf(number);
System.out.println(s2);
//String转为int
String s3 = "200";
//方式1 String---Integer---int
Integer i = Integer.valueOf(s3);
int x = i.intValue();//Integer转为int,这个方法不是静态的,所以要用对象名调用
System.out.println(x);
//方式2(推荐)
int y = Integer.parseInt(s3);
System.out.println(y);
}
}
案例
- 字符串中的数据排序: 有一个字符串:“91 27 46 38 50”,程序实现输出:“27 38 46 50 91”
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
String s = "91 27 46 38 50";
//split方法将此字符串拆分为给定的匹配项,此处以空格作为分割
String[] strArray = s.split(" ");
//吧字符串存到int数组里
int[] arr = new int[strArray.length];
for (int i = 0; i < arr.length; i++) {
arr[i] = Integer.parseInt(strArray[i]);
}
//排序
Arrays.sort(arr);
//拼接字符串
StringBuilder s1 = new StringBuilder();
for (int i = 0; i < arr.length; i++) {
if (i == arr.length - 1) {
s1.append(arr[i]);
} else {
s1.append(arr[i]).append(" ");
}
}
String result = s1.toString();
System.out.println("result:" + result);
}
}
自动拆箱和装箱
装箱:把基本数据类型转换为对应的包装类类型
拆箱:把包装类类型转换为对应的基本数据类型
注意:在使用包装类类型的时候,如果是做操作,最好要先判断是否问null(判断空指针);
推荐:只要是对象,在使用前就要判断是否为null
代码演示:
public class Test {
public static void main(String[] args) {
Integer i = Integer.valueOf(100);//装箱
Integer i1 = 100;//自动装箱
int i2 = i1.intValue() + 200;//拆箱并做加法操作
i1 += 200;//自动拆箱并做加法操作
System.out.println(i1);
Integer i3 = null;
//i3+=300; //报错:NullPointerException 空指针
if (i3 != null) {//实际开发中必须判断是否为空
i3 += 300;
}
System.out.println(i3);//输出null,但不报错
}
}
日期类
Date类
Date类代表了一个特定的时间,精确到毫秒
构造方法如下:
方法名 | 说明 |
---|---|
public Date() | 分配一个Date对象,并初始化,以便代表它被分配的时间,精确到毫秒 |
public Date(long date) | 分配一个Date对象,并初始化为表示从基准时间(1970,1,1,00:00:00)起指定的毫秒数 |
代码演示:
import java.util.Date;
public class Test {
public static void main(String[] args) {
Date d1 = new Date();
System.out.println(d1);//输出:Wed Jul 28 16:14:45 CST 2021(CST表示中国标准时间)
long date = 1000 * 60 * 60;
Date d2 = new Date(date);
System.out.println(d2);//输出:Thu Jan 01 09:00:00 CST 1970(因为时区,所以差了八个小时)
}
}
常用方法:
方法名 | 说明 |
---|---|
public long getTime() | 获取的是日期对象是从1970年1月1日00:00:00到现在的毫秒值 |
public void setTime(long time) | 设置时间,给的是毫米值 |
代码演示:
import java.util.Date;
public class Test {
public static void main(String[] args) {
Date d = new Date();
System.out.println(d.getTime() * 1.0 / 1000 / 60 / 60 / 24 / 365 + "年");
//输出:51.60648806180239年
long time = 1000 * 60 * 60;
d.setTime(time);
System.out.println(d);//输出:Thu Jan 01 09:00:00 CST 1970(时区差异所以是九点)
}
}
SimpleDateFormat类
SimpleDateFormat是一个具体的类,用于以区域设置敏感的方式格式化和解析日期;用首字母代表
构造方法:
方法名 | 说明 |
---|---|
public SimpleDateFormat() | 构造一个SimpleDateFormat,使用默认模式和日期格式 |
public SimpleDateFormat(String pattern) | 构造一个SimpleDateFormat,使用给定的模式和日期格式 |
- 格式化(从Date到String)
方法:public final String format(Date date)
:将日期格式化成日期/时间字符串
- 解析(从String到Date)
方法:public Date parse(String source)
:从给定字符串的开始解析文本以生成日期
代码演示:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Test {
public static void main(String[] args) throws ParseException {
//格式化
Date d = new Date();
//无参默认模式
SimpleDateFormat sdf = new SimpleDateFormat();
String s = sdf.format(d);
System.out.println(s);//输出:2021/7/28 下午5:27
//有参设置模式,按指定格式创建
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");//字母代表年月日,可以查API了解
String s1 = sdf1.format(d);
System.out.println(s1);//输出:2021年07月28日 17:30:09
//解析
String s2 = "2020-07-28 22:22:22";
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//此处模式一定要和所给的格式一样,否则就解析错误
Date d1 = sdf2.parse(s2);
System.out.println(d1);//输出:Tue Jul 28 22:22:22 CST 2020
}
}
案例
- 日期工具类: 定义一个日期工具类,包含两个方法:把日期转为字符串;把字符串解析为指定格式的日期
//工具类
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/*
工具类一般要求:
构造方法私有
成员方法静态
*/
public class DateUtils {
private DateUtils() {
}
/*
把日期转为指定格式的字符串方法:
返回值类型:String
参数:Date date,String format
*/
public static String dateToString(Date date, String format) {
SimpleDateFormat sdf = new SimpleDateFormat(format);//有参设置模式,按指定格式创建
String s = sdf.format(date);
return s;
}
/*
把日字符串解析为指定格式方法:
返回值类型:Date
参数:String s,String format
*/
public static Date stringToDate(String s, String format) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat(format);
Date d = sdf.parse(s);
return d;
}
}
//Test类
import java.text.ParseException;
import java.util.Date;
public class Test {
public static void main(String[] args) throws ParseException {
Date d = new Date();
String s = DateUtils.dateToString(d, "yyyy年MM月dd日 HH:mm:ss");
System.out.println(s);//输出:2021年07月28日 17:55:41
String s1 = DateUtils.dateToString(d, "yyyy年MM月dd日");
System.out.println(s1);//输出:2021年07月28日
String s2 = DateUtils.dateToString(d, "HH:mm:ss");
System.out.println(s2);//输出:17:55:41
String s3 = "2020-07-28 22:22:22";//上下格式要匹配才能成功解析
Date d1 = DateUtils.stringToDate(s3, "yyyy-MM-dd HH:mm:ss");
System.out.println(d1);//输出:Tue Jul 28 22:22:22 CST 2020
}
}
Calendar类
概述: Calendar为某一时刻和一组日历字段之间的转换提供了一些方法,并为操作日历提供了一些方法
Calendar提供了一个类方法getInstance用于获取Calendar对象,其日历字段已使用当前日期和时间格式化
常用方法:
方法名 | 说明 |
---|---|
public int get(int field) | 返回给定日历字段的值 |
public abstract void add(int field,int amount) | 根据日历规则,将指定的时间量添加或减去给定的日历字段 |
public final void set(int year,int mounth,int date) | 设置当前日历的年月日 |
代码演示:
import java.text.ParseException;
import java.util.Calendar;
public class Test {
public static void main(String[] args) throws ParseException {
//获取对象
//该方法的返回值是一个抽象类,其实它需要的是子类的对象
Calendar c = Calendar.getInstance();//多态形式;用于获取Calendar对象
System.out.println(c);
//输出日历字段如下:
// java.util.GregorianCalendar[time=1627467786905,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=31,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2021,MONTH=6,WEEK_OF_YEAR=31,WEEK_OF_MONTH=5,DAY_OF_MONTH=28,DAY_OF_YEAR=209,DAY_OF_WEEK=4,DAY_OF_WEEK_IN_MONTH=4,AM_PM=1,HOUR=6,HOUR_OF_DAY=18,MINUTE=23,SECOND=6,MILLISECOND=905,ZONE_OFFSET=28800000,DST_OFFSET=0]
//注意点:月份是从0开始计数的,所以使用时要加1
//修改日历字段
c.add(Calendar.YEAR, -3);
c.add(Calendar.DATE, 5);
//输出:2018年8月2日
//设置日历字段
c.set(1999, 11, 23);//输出:1999年11月23日,输出设置月份会原样输出的
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH) + 1;//月份是从0开始计数的,所以使用时要加1
int date = c.get(Calendar.DATE);
System.out.println(year + "年" + month + "月" + date + "日");//输出:2021年7月28日
}
}
案例:
- 二月天:输出输入年份的二月份天数
import java.text.ParseException;
import java.util.Calendar;
import java.util.Scanner;
public class Test {
public static void main(String[] args) throws ParseException {
Scanner sc = new Scanner(System.in);
System.out.println("请输入年份");
int year = sc.nextInt();
//获取日历字段
Calendar c = Calendar.getInstance();
//设置年月日
c.set(year, 2, 1);
//修改日历字段;3月1号前一天就是2月最后一天
c.add(Calendar.DATE, -1);
//获取时间
int date = c.get(Calendar.DATE);
System.out.println(year + "年的2月份有" + date + "天");
}
}