# Java基础
# java的应用
java分为三个版本 javaSE javaEE javaME
- javaSE 是java语言的标准版,可用于桌面端的开发,是其他两个版本的基础
- javaEE 是java语言的企业版,可用于web方向的网站开发,如web开发、分布式开发、移动开发等
- javaME 是java语言的小型版,可用于嵌入式电子系统,如手机、平板等
# 数据类型
# 基本数据类型
# 整数数据类型(取值范围不同)常用 int
- byte (取值范围为:-128~127)
- short (取值范围为:-32768~32767)
- int (取值范围为:-2147483648~2147483647)
- long (取值范围为:-9223372036854775808~9223372036854775807)
注意:
long类型数据需要加L或l作为后缀
# 浮点数(小数类型常用 double)
- float (取值范围为:-3.4028235E38~3.4028235E38)
- double (取值范围为:-1.7976931348623157E308~1.7976931348623157E308)
注意:
float类型数据需要加F或f作为后缀
# 字符
- char (取值范围为:0~65535)
拓展
小数和整数数据类型取值范围:double>float>int>long>short>byte
# 布尔值
- boolean (取值为:true、false)
# 处理数字精度的数据类型
BigDecimal(高精度数据类型) 是Java中用于精确计算的高精度数值类型,特别适合处理金融、货币等需要避免浮点数精度误差的场景。
# 为什么需要BigDecimal
- 浮点数精度问题
System.out.println(0.1 + 0.2); // 输出 0.30000000000000004
float/double基于IEEE 754标准,无法精确表示所有十进制小数,会导致金额计算等场景出现精度丢失
BigDecimal优势
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.2");
System.out.println(a.add(b)); // 精确输出 0.3
# 创建 BigDecimal 的正确方式
// 1. 字符串构造(最安全)
BigDecimal d1 = new BigDecimal("0.1");
// 2. 使用 valueOf(内部调用 toString)
BigDecimal d2 = BigDecimal.valueOf(0.1);
注意
- 浮点数构造会带入精度误差!
BigDecimal d3 = new BigDecimal(0.1);- 实际值可能是 0.100000000000000005551115...
# 基本运算与精度控制
- 四则运算(返回新对象)
BigDecimal num1 = new BigDecimal("10");
BigDecimal num2 = new BigDecimal("3");
BigDecimal sum = num1.add(num2); // 加法: 13
BigDecimal diff = num1.subtract(num2); // 减法: 7
BigDecimal product = num1.multiply(num2); // 乘法: 30
- 除法必须指定精度和舍入模式
注意
除法必须指定舍入,否则遇到无限小数会抛出 ArithmeticException。
// 错误:无限小数会抛出 ArithmeticException
// BigDecimal result = num1.divide(num2);
// 正确:指定保留3位小数,四舍五入
BigDecimal quotient = num1.divide(num2, 3, RoundingMode.HALF_UP); // 3.333
//设置小数位数
BigDecimal num = new BigDecimal("3.14159");
// 保留两位小数(四舍五入)
BigDecimal scaled = num.setScale(2, RoundingMode.HALF_UP); // 3.14
// 直接截断
BigDecimal truncated = num.setScale(2, RoundingMode.DOWN); // 3.14(若为 3.149→3.14)
舍入模式
| 模式 | 规则 | 示例 |
|---|---|---|
HALF_UP | 四舍五入(常用) | 2.35 → 2.4 |
HALF_DOWN | 五舍六入 | 2.35 → 2.3 |
HALF_EVEN | 银行家舍入(四舍六入五取偶) | 2.35 → 2.4,2.45 → 2.4 |
UP | 远离零方向舍入 | -1.1 → -1.2 |
DOWN | 向零方向舍入 | -1.1 → -1.0 |
CEILING | 向正无穷方向舍入 | 1.1 → 2.0,-1.9 → -1.0 |
FLOOR | 向负无穷方向舍入 | 1.9 → 1.0,-1.1 → -2.0 |
- 比较与相等性
- 比较值大小(用
compareTo())
BigDecimal x = new BigDecimal("1.0");
BigDecimal y = new BigDecimal("1.00");
x.compareTo(y); // 返回 0(数值相等)
这个方法返回一个整数,表示两个BigDecimal对象在数值上的大小关系。
返回
-1表示当前对象小于参数对象。返回
0表示当前对象等于参数对象。返回
1表示当前对象大于参数对象。
- 避免使用
equals()
x.equals(y); // false(因为精度不同:1.0的scale=1, 1.00的scale=2)
# 运算符
# 算数运算符
- 加减乘除 + - * /
注意
除法运算符 /
- 当运算符两边都是整数时,结果是整数
- 除法运算符通常是向下取整
- 当运算符两边都是小数时,结果是小数 :::
- 取余 %
注意
在代码中如果有小数参与运算,结果可能是不精确的
# 隐式转换
在进行算数运算时,数据类型不一样不能直接进行计算,需转成一样的才能进行运算
# 算数运算符的隐式转换
算数运算符隐式转换的两种提升规则
- 取值范围小的,和取值范围大的进行运算,小的会提升为大的,再进行运算
- byte < short < int < long < float < double
- byte、short、char 三种类型的数据在运算的时候,都会提升为int类型,然后再进行运算
# 字符串的“+”操作
- 当“+”操作中出现字符串时,“+”是字符串连接符,而不是算数运算符了,会将前后数据进行拼接,并产生一个新的字符串
- 连续进行“+”操作时,从左到右依次执行
# 字符的“+”操作
- 当字符+字符或数字时,会将字符转换为对应的ASCII码,然后进行运算
# 自增自减运算符
- a++ 先用再加
- ++a 先加再用
public class Test {
public static void main(String[] args) {
int a = 1;
int b = 1;
System.out.println(a++);//1
System.out.println(++b);//2
}
}
# 赋值运算符
- = 赋值
- += 累加
- -= 累减
- *= 累乘
- /= 累除
- %= 累取余
注意
除等号运算符外 以上所有运算符都隐藏了一个强制类型转换
# 关系运算符
- ==
- !=
- <
=
- <= 比较运算符的结果都是boolean类型,要么是true要么是false
# 逻辑运算符
- & 与
- | 或
- ^ 异或 相同返回false 不同返回true
- ! 非 逻辑运算符当两边为数字时,会将两边的数字转为二进制进行与或运算
# 短路运算符
- && 逻辑与
- || 逻辑或
# 三元运算符
格式 :条件表达式?表达式1:表达式2
# 源码、反码、补码
# 源码
定义:十进制数据的二进制表现形式,最左边是符号位,符号位为0表示正数,符号位为1表示负数
# 反码
定义:源码的符号位取反,符号位为0表示正数,符号位为1表示负数 反码是解决源码不能计算负数而出现的
计算规则:
- 正数的反码不变
- 负数的反码在源码的基础上,符号位不变。数值取反,0变1,1变0
# 补码
补码的出现是为了解决负数计算跨0的问题而出现的
反码+1
一个字节的取值范围是-128~127
补码的计算规则
- 正数的补码不变 负数的补码在反码的基础上+1
- 补码还能多记录一个特殊值-128,该数据在1个字节下,没有源码和反码 补码注意点
- 计算机中的存储和计算都是以补码的形式进行的
# 循环练习
# 求1~5之间的和
public class Test {
public static void main(String[] args) {
//1.求1-5之间的和
int count = 0;
for (int i = 1; i <= 5; i++) count += i;
System.out.println(count);//15
}
}
# 求1~100之间的偶数和
public class Test {
public static void main(String[] args) {
int count = 0;
for (int i = 1; i <= 100; i++) {
if (i % 2 == 0) count += i;
}
System.out.println(count);//2550
}
}
# 键盘录入一个大于等于2的整数x,要求计算该整数的平方根
public class Test {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个整数");
int x = sc.nextInt();
if (x < 2) throw new Error("请输入一个大于等于2的整数");
int result = 0;
while (result * result <= x) result++;
System.out.println(result - 1);
}
}
# 猜数字小游戏
import java.util.Random;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
int num = r.nextInt(100) + 1;
Scanner sc = new Scanner(System.in);
System.out.println("值为" + num);
while (true) {
System.out.println("请输入一个整数");
int inputNum = sc.nextInt();
if (inputNum > num) System.out.println("猜大了");
else if (inputNum < num) System.out.println("猜小了");
else {
System.out.println("猜对了");
break;
}
}
}
}
# 数组
定义:是一种容器,可以用来存储同种数据类型的多个值
- 数组容器在存储数据的时候,需要结合隐式转换考虑
- int 类型的数组容器(boolean(不能)、byte(能)、short(能)、int(能)、double(不能) )
- 建议 容器的类型和存储的数据类型保持一致
# 数组的定义
数组的静态初始化
- 静态初始化以后数组长度不可变
- 数组的元素类型必须和数组容器的类型一致
- 格式:数据类型[] 数组名 = new 数据类型[数组长度]{数组元素1,数组元素2,数组元素3,...}
- 简写格式:数据类型[] 数组名 = {数组元素1,数组元素2,数组元素3,...}
public class Test {
public static void main(String[] args) {
int[] arr = new int[]{1, 2, 3, 4, 5};
//简写
int[] arr2 = {1, 2, 3, 4, 5};
}
}
# 地址值格式的含义
在定义数组以后,打印该数组发现是地址值
public class Test {
public static void main(String[] args) {
int[] arr = new int[]{1, 2, 3, 4, 5};
System.out.println(arr);//[I@1b6d3586
}
}
- [ 表示该地地址值存储一个数组
- I 表示该地址值存储的是一个int类型数组
- @ 表示一个间隔符号。(固定格式)
- 1b6d3586 表示该地址值存储的数组的hash值(十六进制)
- 平时我们习惯将这个整体叫做数组的地址值
# 数组元素的获取
格式 :数组名[下标值]
- 数组下标从0开始
- 数组下标不能超过数组长度
# 将数据存储到数组中
格式 :数组名[下标值] = 值
- 一但覆盖后,原来的数据就不存在了
# 数组遍历
- 数组遍历:遍历数组中的每一个元素
- 格式:for(数据类型 变量名 : 数组名){}
public class Test {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5};
for (int i : arr) System.out.println(i);
}
}
# 遍历数组并求和
public class Test {
public static void main(String[] args) {
int[] arr1 = {1, 2, 3, 4, 5};
int count = 0;
for (int j : arr1) count += j;
System.out.println(count);
}
}
# 统计数组里面一共有多少个能被3整除的数
public class Test {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int count = 0;
for (int i : arr) i % 3 == 0 && count++;
System.out.println(count);
}
}
# 数组的动态初始化
- 初始化时只指定数组的长度,由系统为数组分配初始值
- 格式:数据类型[] 数组名 = new 数据类型[数组长度];
# 数组默认初始化规律
- 整数类型默认初始化值为0
- 浮点类型默认初始化值为0.0
- 布尔类型默认初始化值为false
- 字符类型默认初始化值为\u0000 (空字符串)
- 引用类型默认初始化值为null
# 遍历数组求和
需求:生成10个1~100的随机数存入数组
- 求出所有数据的总和
- 求出所有数据的平均数
- 统计有多少个数据比平均值小
public class Test {
public static void main(String[] args) {
int[] arr = new int[10];
int count = 0;
Random r = new Random();
for (int i : arr) {
int num = r.nextInt(100) + 1;
count += num;
arr[i] = num;
}
//和
System.out.println("和" + count);
//平均值
int average = count / arr.length;
System.out.println("平均值" + average);
//统计有多少个数据比平均值小
int count1 = 0;
for (int i : arr) if (i < average) count1++;
System.out.println("总共有" + count1 + "个数比平均值小");
}
}
# 方法重载
- 在同一个类中,定义了多个同名的方法,这些同名的方法具有相同的功能
- 每个方法具有不同的参数类型或参数个数,这些同名的方法就构成了重构关系
# 面向对象 类
类的定义
- 用来描述一类事物的类,专业叫做:javabean类
- 在javabean类中是不写main方法的
- 在以前main方法中编写的类叫做测试类
- 我们可以在测试类中创建javabean类的对象并赋值调用
# 格式
public class Test {
//1.成员变量(代表属性)
//2.成员方法(代表行为)
}
注意
- 类名首字符必须大写,需要见名之意,驼峰模式
- 一个java文件中可以定义多个类,且只能一个类是public修饰的,而且public修饰的类名必须成为代码文件名
- 实际开发中建议还是一个文件定义一个class类
- 成员变量的完整定义的格式是:修饰符 数据类型 变量名称 =初始值一般无需指定初始化值,存在默认值
# 构造方法
- 构造方法也叫构造器、构造函数
- 创建对象的时候由虚拟机自动调用,给成员变量进行初始化的
- 作用:在创建对象的时候给成员变量进行初始化 格式:
public class Text {
int number;
private Phone(int number) {
this.number = number;
}
}
特点
- 方法名与类名相同,大小写也要一致
- 没有返回值类型,连void都没有
- 没有具体的返回值(不能由return带回结果数据)
# idea快速生成空参、有参构造方法、getter、setter
- 方法一:快捷键
alt+ins或fn +alt+ins - 方法二:插件PTG 1秒生成标准javabean 快捷键
ctrl+shift+,
# 格斗文字游戏
//people.java类
package text;
import java.util.Random;
public class People {
//角色姓名
private String name;
//角色血量
private int blod;
//角色性别
private char sex;
//角色长相
private String appearance;
//男生长相数组
String[] manFace = {"风流俊雅", "气宇轩昂", "英俊潇洒", "五官端正", "相貌平平", "喜气洋洋", "大长腿", "高冷", "短发", "长发"};
//女生长相数组
String[] womanFace = {"美奂绝伦", "沉鱼落雁", "婷婷玉立", "身材较好", "相貌平平", "相貌简陋", "长发", "短发"};
//攻击描述
String[] attackDescArr = {"%s使出一招【普心钉】,转到敌方身后,一掌向%s的灵台穴拍去。",
"%s使出了一招【游空探爪】,飞起身形自半空中变掌为抓锁向%s。",
"%s大喝一声,身形下伏,一招【劈雷坠地】,捶向%s双腿",
"%s运气于掌,一瞬间掌心变得血红,一式【掌心雷】,推向%s。",
"%s上步抢身,招中套招,一招【劈挂连环】,连环攻向%s",
"%s阴手翻起阳手跟进,一招【没选拦】,结结实实的捶向%s"
};
//受伤描述
String[] hurtDescArr = {"结果%s退了半步,毫发无损",
"结果给%s造成一处瘀伤",
"结果一击命中,%s痛得弯下腰",
"结果%s痛苦地闷哼了一声,显然受了点内伤",
"结果%s摇摇晃晃,一跤摔倒在地",
"结果『轰』的一声,%s口中鲜血狂喷而出",
"结果%s脸色一下变得惨白,连退了好几步",
"结果%s一声惨叫,像滩软泥般塌了下去"
};
public People(String name, int blod, char sex) {
this.name = name;
this.blod = blod;
setSex(sex);
}
public People() {
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
Random random = new Random();
if (sex == '男') {
int r = random.nextInt(manFace.length);
setAppearance(manFace[r]);
} else if (sex == '女') {
int r = random.nextInt(womanFace.length);
setAppearance(womanFace[r]);
}
}
public String getAppearance() {
return appearance;
}
public void setAppearance(String appearance) {
this.appearance = appearance;
}
/**
* 获取
*
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
*
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
*
* @return blod
*/
public int getBlod() {
return blod;
}
/**
* 设置
*
* @param blod
*/
public void setBlod(int blod) {
this.blod = blod;
}
//角色发起攻击
public void attack(People people) {
Random r = new Random();
//获取随机攻击描述
String attackDesc = attackDescArr[r.nextInt(attackDescArr.length)];
System.out.printf(attackDesc, this.name, people.name);
//获取随机伤害
int hurt = r.nextInt(10) + 1;
//计算剩余血量
int remainBlode = people.getBlod() - hurt;
people.setBlod(Math.max(remainBlode, 0));
//获取受伤描述
String hurtDesc = "";
int blod = people.getBlod();
if (blod > 90) hurtDesc = hurtDescArr[0];
else if (blod > 80) hurtDesc = hurtDescArr[1];
else if (blod > 70) hurtDesc = hurtDescArr[2];
else if (blod > 60) hurtDesc = hurtDescArr[3];
else if (blod > 50) hurtDesc = hurtDescArr[4];
else if (blod > 40) hurtDesc = hurtDescArr[5];
else if (blod > 0) hurtDesc = hurtDescArr[6];
else hurtDesc = hurtDescArr[7];
System.out.printf(hurtDesc, people.name);
System.out.print(",造成" + hurt + "点伤害," + people.name + "剩余" + people.getBlod() + "血量");
System.out.println();
}
//展示角色信息
public void show() {
System.out.println("姓名:" + getName() + " 血量:" + getBlod() + " 性别:" + getSex() + " 长相:" + getAppearance());
}
}
//使用
//main.java
import text.People;
public class Main {
public static void main(String[] args) {
//文字攻击
People people1 = new People("James", 100, '男');
People people2 = new People("Tom", 100, '女');
people1.show();
people2.show();
while (true) {
people1.attack(people2);
if (people2.getBlod() <= 0) {
System.out.println(people1.getName() + "已经死亡,战斗结束");
break;
}
people2.attack(people1);
if (people1.getBlod() <= 0) {
System.out.println(people2.getName() + "已经死亡,战斗结束");
break;
}
}
}
}
# java关键字
- 如class 表示定义类
- 关键字字母全部小写
# class 关键字
用于创建定义一个类 类是java最基本的组成单元
# private 关键字
- 是一个权限修饰符
- 可以修饰成员、(成员变量、成员方法)
- 被private修饰的成员变量、成员方法只能在本类中访问
//声明类
public class phone {
private int price;
private String brand;
private String color;
public void setPrice(int p) {
this.price = p;
}
public int getPrice() {
return this.price;
}
public void call(String name) {
System.out.println("正在给" + name + "打电话");
}
public void sendMessage(String name) {
System.out.println("正在给" + name + "发短信");
}
}
//使用类
public class Main {
public static void main(String[] args) {
phone myPhone = new phone();
myPhone.call("James");//正在给James打电话
myPhone.setPrice(2399);
System.out.println("手机机价格是" + myPhone.getPrice());//手机机价格是2399
}
}
# lambda表达式
lambda表达式最基本的应用就是简化匿名表达式的书写- 它是
jdk8开始后的一种新语法形式 lambda表达式只能简化函数式接口的匿名内部类的写法函数式接口有且仅有一个抽象方法的接口叫函数式接口,接口上方可以加@FunctionalInterface注解,表示该接口是函数式接口
package api;
import java.util.Arrays;
import java.util.Comparator;
public class LambdaText {
public static void main(String[] args) {
Integer[] arr = {1, 4, 2, 6, 4, 3, 8, 9, 7, 5};
Arrays.sort(arr, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
System.out.println(Arrays.toString(arr));
//使用Lambda表达式简化匿名内部类
Integer[] arr2 = {1, 4, 2, 6, 4, 3, 8, 9, 7, 5};
Arrays.sort(arr2, (o1, o2) -> {
return o1 - o2;
});
System.out.println(Arrays.toString(arr2));
}
}
# 使用
public class LambdaText {
public static void main(String[] args) {
scText(new Swim() {
@Override
public void swiming() {
System.out.println("我在游泳");
}
});
//使用lambda表达式简化
scText(() -> System.out.println("我在游泳"));
}
//调用方法的时候如果这个方法的形参是一个接口,那么我们就要传递这个接口的实现类对象
//如果实现类对象只要用到一次,就可以使用Lambda表达式来简化
public static void scText(Swim swim) {
swim.swiming();
}
//使用匿名内部类的前提该要是一个接口 不能是抽象类
@FunctionalInterface //该注解表示这是一个函数式接口,只有一个抽象方法
interface Swim {
public abstract void swiming();
// public abstract void swimings();报错
}
}
# Lambda表达式的省略写法
- 主要思想:可推导,可省略
Lambda表达式的省略写法
- 参数类型可以省略不写
- 如果只有一个参数,参数类型可以省略,同时()也可以省略
- 如果Lambda表达式的方法体只有一行代码,可以省略大括号和return关键字
# 练习
class GF {
String name;
int age;
double height;
public GF(String name, int age, double height) {
this.name = name;
this.age = age;
this.height = height;
}
public class LambdaText {
public static void main(String[] args) {
//练习
//定义第一个存储字符串的数组 字符短的在前面 长的在后面
String[] arr3 = {"aaaa", "aa", "a", "aaa", "aaaa"};
Arrays.sort(arr3, (o1, o2) -> o1.length() - o2.length());
System.out.println(Arrays.toString(arr3));//[a, aa, aaa, aaaa, aaaa]
//定义数组并存储一些女朋友对象,利用Arrays中的sort方法进行排序
//要求:属性有姓名。年龄、身高
//要求2:按照年龄的大小进行排序,年龄一样的按照身高进行排序,身高一样的按照姓名字母进行排序
GF[] arr4 = {new GF("小红", 18, 1.68), new GF("小明", 19, 1.70), new GF("小花", 18, 1.68)};
Arrays.sort(arr4, (o1, o2) ->
{
if (o1.age != o2.age) return o1.age - o2.age;
if (o1.height != o2.height) return (int) (o1.height - o2.height);
//将字符串按照字典的顺序(Ascii码的顺序)进行比较
return o1.name.compareTo(o2.name);
});
System.out.println(Arrays.toString(arr4));
}
}
idea快捷键 →