FUNDAMENTAL01
Java开发环境与搭建
一、Java
编程语言就是人类与计算机沟通的桥梁,Java只是编程语言中的一种,我们可以编程语言操作计算机。
.java源文件 -------编译 -----------.class字节码文件
JVM(Java Virtual Machine):Java虚拟机 ,主要就是用来加载运行.class字节码文件
JRE (Java Runtime Environment):Java运行环境,包含了JVM,还包含运行程序的环境
JDK(Java Development Kit):Java开发工具包,包含了JRE,还包含了Java开发所需的工具。
二、编译器工具
IDEA 是JetBarins 公司 提供的 提供两个版本 : 社区版(免费) 终极版(收费)
Java这款语言就是开源 , Eclispe (开源的) , IDEA社区版(开源的)
开发步骤:
1.新建一个Java项目/工程 ----------------- 小区
2.新建一个Java的包 ---------------- XX号楼XX单元
3.新建一个Java的类 ----------------房子
Java中的注释分为三种:
Java中的编译方式分为三种:
Compile------------对类进行编译(不管类中内容是否修改过,一定会编译)
Rebuild ------------对项目进行编译(不管项目是否修改过,一定全部编译)
Make ------------默认的编译方式(只编译修改过的文件)
package day01;// 包名 day01 ---------- 具体楼+单元
public class HelloWorld { //类名 HelloWorld --------房子
//main功能的快捷键 main回车即可自动生成
//main 功能 ----程序的主入口 :
// 代码只能放在main的功能里面 才能运行
//且代码是逐语句往下运行
public static void main(String[] args) { //-------大门 入口
/*
* 1.严格区分大小写!
* 2.写代码期间,所用到的符号 全部都要保证英文状态下输入,否则也会报错
* 3.每句话如果写完 要用分号结尾
* */
//想输出一句话 显示在屏幕
// 输出的功能 快捷键 sout 回车
//文本性的内容:如果要输出 需要加双引号!
System.out.println("Hello Java!!");//双引号中的内容所见即所得
System.out.println(3);
System.out.println(2);
System.out.println(1);
}//执行到这里 就代表 出口(程序结束)
}
day02变量
早期的计算机 ---------------------------------算数(数字的加减乘除)
现在的计算机 ---------------------------------算数(数字、文字、图片、音频、视频)
int a;//声明一个int(整数)的变量 变量名为a
int b,c,d;//声明三个int(整数)的变量 变量名分别为 b c d
//int a; 编译错误 :因为已经声明过一个名为 a的变量了。不能重复声明
int e = 300;//声明了一个int(整数) 变量 名为e e里面存了300
int f;//声明一个int(整数) 变量名 为f
f = 150;//为f这个变量里面存 150
int b = 5;//声明一个int(整数) 的变量名为 b 为b里面存了5
int c = b + 10;//声明一个int(整数)的变量名为c 里面存了 b + 10 的和,b是5 + 10 最后15存到变量c里面去
System.out.println("c");//c -----因为双引号 所见即所得
System.out.println(c);//15
//1. 变量的声明
// int a;//声明一个int(整数)的变量 名为叫做 a
// int b, c, d;//声明三个 int(整数)的变量 名分别为 b c d
int a; 编译错误 :因为已经声明过一个名为 a的变量了。不能重复声明
// //2.变量的初始化
// int e = 300;//声明一个int(整数)的变量 名为e 里面存了300
// int f;//声明
// f = 100;
// //3.变量的使用
// int g = 5;//声明一个int(整数)的变量 名为g 存了5
// int h = g + 10;//声明一个int(整数)的变量名为h 里面存了 g + 10 的和,g是5 + 10 最后15存到变量h里面去
// System.out.println(h);//使用并输出h里存的15
// int o = 10;
// System.out.println(o); //10 使用变量前要声明+初始化
// o = 20;
// System.out.println(o);//使用变量 是以最后一次赋值的数据为准!
//
// System.out.println("o");//打印输出的是o的字母 并不是用o这个变量!! 双引号的内容所见即所得
// Ctrl + / 注释代码的快捷键
4.1 变量的命名 只能包含数字、字母、_和$ 注意不能以数字开头!、
//4.变量的命名规范
// int 1a; 编译错误:变量名不能以数字开头
int _01$;
int $abc_;
//Java严格区分大小写
int a = 1;
System.out.println(a);
// System.out.println(A); 此A非彼 a
// int void; 不能以关键字进行命名 关键字就是蓝色的字!
// int static;
//命名要见名知意!
//建议大家下载有道词典 .....
int score = 100;//用英文单词的见名知意
//int 分数 = 100;//不能用 中文的见名知意 ------行业规范!
// int fenshu = 100;//不能用 拼音的见名知意
//变量的命名 遵循 小驼峰命名法 :小驼峰命名法 多个单词之间 首字母小写 后续第二单词首字母大写....第三....
int myScore = 100;
//类名 遵循 大驼峰命名法: 每个单词首字母大写.
day03:数据类型
short / float 保留c语言中的类型 工作用不到。
GB(吉字节)
MB(兆字节)
KB(千字节)
b(字节)
整数类型:
int: 在内存占用4个字节, 存储的范围 -21亿~21亿多(2147483647)!
1.整数的直接量默认为int类型,直接量是不能够超出当前类型所存储的范围,否则会报编译错误.
2.如果两个整数进行运算!结果是不会保留小数的!(会截断删除,不会四舍五入!)
3.整数如果运算时,若超出范围则会溢出,(溢出不是错误)但是需要避免,溢出的数据没有参考价值!
package day03;
/**
* 数据类型的使用演示类:
* 整数类型:
* int类型 占4个字节 可存储范围-2147483648 ~ 2147483647
* long类型
* */
public class DateTypeDemo01 { //房子
public static void main(String[] args) {//大门
//整数直接量 默认为int类型
int a = 100;//声明一个int类型的变量 变量名为a 存了100;
// int b = 2147483648; 编译错误:整数直接量超过了int 这个类型可存储的范围
int c = 5;
int d = 2;
// System.out.println(c / d );//2 (整数运算时不会保留小数位)
//--------------------------------
int number = 2147483647;//number已经存储的int 最大值
int result = number + 3;
System.out.println(result);//溢出的数据 -2147483646
/**
* +1 -------------------(-2147483648)
* +1+1 -----------------(-2147483647)
* +1+1+1 ---------------(-2147483646)
* */
}
}
long:在内存中占用8个字节,可存储的范围 -900万万亿~900万万亿
2.长整数类型在进行运算时,建议要在第一个数据后面加上L即可! 代表所有的结果都是long类型的.
//长整数直接量
long a = 10000000000L;//100亿 需要明确加L表示是long类型的直接量
// 10亿 * 2 * 10
long b = 1000000000 * 2 * 10L;
//System.out.println(b);//200亿
//30亿 超出了int的范围
long c = 1000000000 * 3 * 10L;
// System.out.println(c);//溢出:-12949672960
long d = 1000000000L * 3L * 10L;
System.out.println(d);//300亿
变量占多大内存,取决的是数据类型 而并不是装载的数据!
int a = 5;
int b = 2147483647;
小数类型:
double(双倍精度):占8个字节 ,可以存储的数据很大很大很大的....
3.double类型的数据在进行运算时,可能存在误差! Math.round()四舍五入的功能
/**
* 小数类型的使用演示类:
* double 占8个字节
*/
public class DateTypeDemo02 {//房子
public static void main(String[] args) { //大门
//小数直接量 默认为double
double a = 3.14;//声明一个double类型的变量 变量名为a 存了3.14
double b = 3.0;
double c = 2.9;
//System.out.println(b - c);// 0.10000000000000009 可能存在误差
double e = 6.0;
double f = 4.9;
//System.out.println(e - f);//1.0999999999999996 可能存在误差 无限接近
double g = 6.0;
double h = 1.9;
System.out.println(g - h);//4.1 可能存在误差
double r = Math.round(4.5);//可以四舍五入
System.out.println(r);
}
}
字符类型
Unicode:统一字符码,万国码.Unicode中划分了很多个子集,Java中char用的就是Unicode最常用的子集 ASCII码表.
1.一个字符对应一个码:表象是字符 本质是码(0~65535)之间.
2.字符类型的直接量,只能用单引号来进行存储,且只能存一个字符!
3.Java提供特殊符号,如果想要存储的话,需要转义(转换特殊含义) , 转义符号\
char temp = 'a';//声明一个char类型的变量 名为temp 存了字符a
System.out.println(temp);//a
System.out.println((int)temp);//97
char temp1 = '我';
System.out.println(temp1);
// char temp2 = 'ni'; 编译错误:char类型只能存单个值!
char temp3 = '\'';//转义
System.out.println(temp3);// '
char temp4 = '\\';//转义
System.out.println(temp4);// \
布尔类型
boolean占用内存1个字节,要么存true(真/成立) 要么存false(假/不成立).
boolean a = true;
System.out.println(a);//true
boolean b = false;
System.out.println(b);//false
类型转换
从小到大顺序: byte --->short---->int ---->long---->float---->double
强制类型转换:将大的类型装载到小类型的过程.(强转有风险,使用须谨慎)
//自动类型转换:
// int a = 100;//没有发生!
// long b = a ;//发生自动类型转换
// double c = b;//发生自动类型转换
//强制类型转换:
long a = 1000000000L;//10亿
int b = (int)a;//使用强制语法:在需要强制转换的变量前面加上小括号 填上要强转的类型即可
System.out.println(b);//10亿
long c = 100000000000000000L;
int d = (int)c;
System.out.println(d);//一定会溢出!!
double e = 3.1415926;
int f = (int)e;//将小数强制转换为整数时 截断删除小数
System.out.println(f);//3
规则:
1.整数直接量是可以直接赋值给 byte short char类型的!但是不能超过当前类型 的范围!
2.byte short char 如果进行运算时,系统会自动将其结果转换为int 类型。
3.小类型 与大类型进行运算时,系统会自动将类型和结果转换为大类型。
byte a = 1;
byte b = 2;
byte c = (byte) (a + b);//byte运算 其结果会自动转换为int类型
char d = '2'; //50
char e = '2'; //50
int f = d + e; //char运算时 本质上 运算的是字符对应的码
System.out.println(f);
char d1 = 50;//也可以存整数直接量的码
System.out.println(d1);// ‘2’
byte b1 = 100;
int r = b1 + 20;//byte(小类型) 与 int(大类型) 运算 结果为 大类型
day04:运算符
符号:加(+) 减(-) 乘(*) 除( / ) 取余(%)
package day04;
/**
* 数学运算符:
* 自增自减运算符:
*/
public class OperationDemo01 {
public static void main(String[] args) {
int a = 10;
int b = 5;
System.out.println(a + b);//15
System.out.println(a - b);//5
System.out.println(a * b);//50
System.out.println(a / b);//2
//10 取余 5
System.out.println(10 % 5);//10除以5 的余数为 0
System.out.println(5 % 2);
//---------------------------
//取余操作时,若左边的数据小于右边的数据 其取余结果还是左边的数据
System.out.println(1 % 3);//1
System.out.println(2 % 3);//2
System.out.println(3 % 3);//0
System.out.println(8 % 15);//8
}
}
1. 单独运算时(只有当前变量被运算),符号在前在后都一样!
//1.自增自减 单独运算时在前在后都一样。
// int a = 5;
// a--;
// System.out.println(a);
//2.参与运算时,自增自减在前在后不一样。
/** 符号如果在前,就先执行符号自增或自减,然后再参与其它运算。
符号如果在后,就会先参与其它运算,在下次使用变量时 则变为自增或自减的后的值
* */
// int a = 5;
// int b = 5;
// int c = a++;
// int d = ++b;
// System.out.println(a);
// System.out.println(b);
// System.out.println(c);
// System.out.println(d);
// int a = 5;
// int b = 5;
// int c = a--;
// int d = --b;
// System.out.println(a);
// System.out.println(b);
// System.out.println(c);
// System.out.println(d);
//
// int a = 5;
// int b = 5;
// int c = --a - b--;
// System.out.println(a);//4
// System.out.println(b);//4
// System.out.println(c);//-1
//面试题:
// int a = 1;
// a = a++; //相当于没有进行自增过 a值还是原来的数据
// System.out.println(a);//1
关系运算符指的就是 大于( > ) 小于(<) 大于等于( >= ) 小于等于( <= )
关系运算符的结果 一定是boolean类型,要么成立(true)要么不成立 (false)
int a = 50; int b = 40;
boolean result = a > b;
a的数据 大于 b的数据吗?----------------true
int c = 50; int d = 50;
boolean result1 = c >= d;
c的数据 大于等于 d的数据吗?------------true
int e = 100; int f = 99;
f++;
boolean result2 = e == f;
e的数据 等于 f的数据吗?----------------true
int a = 50;
int b = 40;
boolean result = a > b;
System.out.println(result);//true
System.out.println(a >= b);//true
System.out.println(10 < 10);//false
System.out.println(10 != 10);//false
System.out.println( 5 == 4);//false
逻辑与(并且) 现象:在逻辑与(并且)的关系中,两个或多个条件 有一个条件不成立,其结果一定不成立
一假俱假 ----- 见 false 即 false
能毕业吗?
条件一:课程学完了吗? true false false true
并且&&
条件二:考试及格了吗? false true false true
结果: false false false true
逻辑或(或者) 现象:在逻辑或(或者)的关系中,两个或多个条件 有一个条件成立,其结果一定成立
一真俱真 ------ 见 true 即 true
能结账吗?
条件一:微信有钱吗? false true true false
或者 ||
条件二:带现金了吗? true false true false
结果: true true true false
逻辑非(取反)
true: !true 非真即假
结果: false
false: !false 非假即真
结果: true
//逻辑运算符
int a = 5;
int b = 10;
int c = 5;
boolean result = a > b && b > c;// fasle && true ---->并且关系中见fasle
System.out.println(result);//false 即是false
System.out.println(a > b || b > c);//false || true --->或者关系中见true即true
System.out.println(a == ++c && c >5);// fasle && true ---->并且关系中见fasle即是false
System.out.println(c);//6
System.out.println(!true);//false ----结果取反
System.out.println(!false);//true ----结果取反
//判断年份是否为闰年案例:
/**
* 闰年的判定条件:
* 条件一:年份能被4 整除 并且 年份不能被100整除
* 或者
* 条件二:年份能被400 整除
* */
Scanner s = new Scanner(System.in);//创建一个扫描器
System.out.println("请输入要判断的年份");
int year = s.nextInt();//使用扫描器读取控制台输入的int数据的功能
boolean r1 = year % 4 == 0 && year % 100 != 0; //闰年条件一
boolean r2 = year % 400 == 0;//闰年条件二
boolean result = r1 || r2;//条件1 或 条件2 的结果 只要有一个成立 结果则成立
System.out.println(result);//2000年是闰年
//方式二:
// boolean result1 = (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
// System.out.println(result1);
短路逻辑
短路与(并且):
相亲成功的两个条件!
1.相亲对象是女生 true false
并且
2.勤俭持家 true
结果: true false
短路与现象:如果前面条件不成立,程序则立刻返回不成立的结果,根本不去再执行判断后面的条件了。
短路或 (或者)
相亲成功的两个条件!
1.长得好看 true
或者
2.勤俭持家
短路或现象:如果前面条件成立,程序立刻返回成立的结果,根本不去再执行判断后面的条件了。
int a = 5;
int b = 20;
//发生短路与
// System.out.println(a > b && ++b > a ); // false
// System.out.println(b);//20
//发生短路或
System.out.println( ++a < b || ++b >19 );//true
System.out.println(b);//20
//未发生短路
System.out.println(a < b && ++a > 19 );//false
System.out.println(a);//7
//扩展赋值号的使用:
int a = 10;
a += 10; // 等价于: a = a + 10;
System.out.println(a);//20
a -= 10; // 等价于: a = a - 10;
System.out.println(a);//10
a *= 2; // 等价于: a = a * 2;
System.out.println(a);//20
a /= 5; // 等价于: a = a / 5;
System.out.println(a);//4
a %= 2; // 等价于 a = a % 2;
System.out.println(a);//0
判断的逻辑 ? 若为true 返回值1 :若为false 返回值2;
//三元表达式的写法
int number1 = 10;
int number2 = 20;
// 判断的逻辑 ? 若为true 返回值1 :若为false 返回值2;
int max = number1 > number2 ? number1 : number2;
System.out.println(max);//最大值:20
//使用三元表达式,用什么类型来接收结果 主要看值1 和值2的 类型
int num = 10;
boolean result = num % 2 == 0 ? true : false;
System.out.println(result);//是偶数: true
7.2 使用+号时,如果有一方 为字符串类型,则会进行拼接操作,变成一个字符串整体。
int num = 10;
boolean result = num % 2 == 0 ? true : false;
System.out.println("num是否是偶数:" + result);//是偶数: true
int age = 30;
String name = "张三";
System.out.println("我叫:"+name + "我的年龄是:"+age);
System.out.println(10+10+""+30);//2030
System.out.println(""+10+10+30);//101030
System.out.println(10+10+30+"");//50
---------------------------------------------------------
byte b = 5;
// b = b + 5;//编译错误:小类型+大类型 系统会自动将结果转换为大类型
b += 5;//使用扩展赋值 系统会自动进行强转 等价于 b = (byte)(b + 5);
day05:流程控制
Java中流程控制有三种结构,任何复杂的业务逻辑都可以通过这三种方式来实现。
例如:
购物满500 打8折 ---------购物满500 -------满足---打8折 单路分支
购物满500 打8折 不满打9折-------满500 打8折 ----不满足打9折 双路分支
分支结构:
package day05;
import java.util.Scanner;
/**
* 单路分支结构的使用演示类:
*/
public class IFDemo {
public static void main(String[] args) {
/** 语法结构
* 条件
* if(boolean){
* //条件成立执行的代码块
* }
* 执行过程:
* 判断if小括号中的boolean 值
* 若为true 则执行条件成立执行的代码块
* 若为false 代码跳过if的代码块... 继续往后执行
* */
Scanner s = new Scanner(System.in);
System.out.println("请输入购物的金额..");
double price = s.nextDouble();//金额 500
if (price >= 500) { //如果金额 大于等于 500
price *= 0.8; // price = price * 0.8;
System.out.println("打完8折后应付金额:" + price);
}
System.out.println("已付款:" + price);
}
}
package day05;
/**
* 双路分支的使用演示类:
*/
public class IFElseDemo {
public static void main(String[] args) {
/** 双路分支的语法结构:
* if(boolean){
* //成立执行的代码块
* } else{ 否则
* // 不成立执行的代码块
* }
* 执行过程:
* 先去判断if小括号中的条件 是否成立
* 若成立 执行 --成立执行的代码块
* 若不成立 执行 --不成立执行的代码块
* */
//购物满500 打8折 不满打9折
double price = 900;//金额
if (price >= 500) {
price *= 0.8;
System.out.println("打完8折应付:" + price);
} else { //否则
price *= 0.9;
System.out.println("打完9折应付:" + price);
}
System.out.println("已付款:" + price);
}
}
package day05;
import java.util.Scanner;
/**
* 多路分支的使用演示类:
*/
public class IFElseIFDemo {
public static void main(String[] args) {
/**多路分支的语法结构:
* if(条件1){
* //若条件1成立则执行的代码块
* } else if(条件2){
* //若条件2成立则执行的代码块
* }
* else if(条件3){
* //若条件3成立则执行的代码块
* }
* 执行过程:
* 先判断 条件1 若条件1满足 则执行条件1的代码块 否则
* 再判断 条件2 若条件2满足 则执行条件2的代码块 否则
* 再判断 条件3 若条件3满足 则执行条件3的代码块....
* */
/**
* 需求: 根据用户输入的分数 来判断分数的等级。
* A. 分数 大于等于 90 输出优秀!
* B. 分数 小于90 并且 分数大于等于 80 输出良好!
* C. 分数 小于80 并且 分数大于等于 70 输出一般!
* D. 分数 小于70 并且 分数大于等于 60 输出及格!
* E. 分数小于 60 输出 不及格!
* */
Scanner s = new Scanner(System.in);
int score = s.nextInt();//分数
if (score >= 90) { //如果分数 大于等于 90
System.out.println("优秀!");
} else if (score >= 80) {//否则 如果分数大于等于80
System.out.println("良好");
} else if ( score >= 70) {//否则 如果分数大于等于70
System.out.println("一般");
} else if (score >= 60) {// 否则 如果分数大于等于60
System.out.println("及格");
} else {//否则 如果能执行到这里 说明上述条件没有一个 成立 则表示不及格
System.out.println("不及格!");
}
System.out.println("程序继续往后执行.....");
/** 练习: 通过控制台的方式 获取用户输入的年龄 根据年龄判断人生阶段
* int age = ?;
* A. 如果 age 大于等于 0 并且小于 5 输出幼年!
* B. 如果 age 大于等于 5 并且小于18 输出青年!
* C. 如果 age 大于等于 18 并且小于 35 输出成年!
* D. 如果 age 大于等于 35 并且小于 50 输出中年!
* E. 如果 age 大于等于 50 并且小于 65 输出中老年!
* F. 如果 age 大于等于 65 并且小于 150 输出老年!
* 否则 年龄不合法!
* */
}
}
适用性:对数值的数据进行匹配判断是否相等,不能使用逻辑运算和关系运算。JDK1.7版本以后支持对String类型 判断是否相等。
优点:如果只对单个整数数据判断是否匹配某个数值时,用switch case结构做分支效率更高。
default 代表默认的意思 上述case都没匹配上 则会走到default
Scanner s = new Scanner(System.in);
System.out.println("1 取款 2 存款 3 转账 请输入指令");
int userInput = s.nextInt();//接收用户再控制台输入的指令
//判断的变量类型 需要是整数类型(不包括long) 或 String类型
switch (userInput){
case 1:
System.out.println("取款业务开始办理中...");
break;//退出switch 结构的代码块
case 2:
System.out.println("存款业务开始办理中...");
break;//退出switch 结构的代码块
case 3:
System.out.println("转账业务开始办理中...");
break;
default://默认..走到这里 代表上述没有一个匹配上。
System.out.println("输入指令有误");
}
System.out.println("代码继续往后执行....");
循环结构:
循环:字面意思指的就是重复执行某个事情(代码),反反复复的过程,称之为循环!
Java中循环分为3中:1. while 2. do..while 3. for(常用)
循环的三要素:1. 循环变量的初始化 2.基于循环变量的条件 3.循环变量的改变(朝着目标改变)
跑三圈
初始圈数 圈数为0
够三圈吗? 不够 跑一圈 圈数为1
够三圈吗? 不够 跑一圈 圈数为2
够三圈吗? 不够 跑一圈 圈数为3
够三圈吗? 够了 不跑了
三要素:
1. int count = 0; //循环变量初始值
2. count < 3; //基于循环变量的条件
3. count++; //循环变量的改变
package day06;
/**
* while 循环的使用演示类:
* */
public class WhileDemo {
public static void main(String[] args) {
/** while 循环语法结构:
* 1.循环变量初始值
* while(2.boolean){
* //若条件成立 则循环的代码块
* //3.循环变量的自增
* }
* 执行过程
* 先去判断while小括号中的条件是否成立
* 若成立 则执行循环的代码块
* 再去判断while小括号中的条件是否成立
* 若成立 则执行循环的代码块
* .....
* 若不成立
* 代码逐语句往后执行....
* */
//
//循环的三要素:1. 循环变量的初始化 2.基于循环变量的条件 3.循环变量的改变
// int count = 0;//1
// while (count < 3){//2
// System.out.println("行动是成功的阶梯!我们都是最棒的!!");
// count++;
// }
// System.out.println("写一个很重要程序....");
// 练习 使用while循环 让业寿同学 跑10圈
int count = 1;//初始值
while (count <= 10){//循环条件
System.out.println("业寿同学跑第"+count+"圈了!" );
count++;//循环变量自增
}
System.out.println("跑完了 歇歇.....");
}
}
package day06;
/**
* do ..while 循环的使用演示类:
* */
public class DoWhileDemo {
public static void main(String[] args) {
/** do .. while 循环结构:
* //循环的三要素:1. 循环变量的初始化 2.基于循环变量的条件 3.循环变量的改变
* 1.循环变量的初始化
* do{
* //循环的代码块
* //3.
* }while(2.条件);
* 执行过程:
* 先去做do 的循环代码块 然后再判断while小括号中的条件
* 若条件成立 则再次执行 do的循环代码块 再判断while小括号中的条件
* 若条件成立 则再次执行 do的循环代码块 再判断while小括号中的条件
* ....
* 若条件不成立 则代码代码继续往后执行...
* */
// 今天大家非常棒!我们继续保持 !奥里给! X3
int count = 0;//1
do {
System.out.println("今天大家非常棒!我们继续保持 !奥里给!");
count++; //3.
}while (count < 3); //2.
}
}
随机数:
Math.random(); 返回给一个double类型 从 0 ~ 1 区间的小数 可取到0 取不到1
//---------随机数的使用
double r = Math.random();//返回给一个double类型 从 0 ~ 1 区间的小数 可取到0 取不到1
//System.out.println(r);
double r1 = Math.random() * 100;//返回给一个double类型 从 0 ~ 100 区间的小数 可取到0 取不到100
//System.out.println(r1);
int r2 = (int) (Math.random() * 100);//返回的随机数强制int 类型
// System.out.println(r2);
// (int)(随机数 * (最大值 - 最小值 ) +最小值); //500 ~ 1500 的随机数
int r3 = (int)(Math.random() * (1500 - 500 ) +500);
System.out.println(r3);
/** 猜大小的案例:
* 运行产生一个随机数 ,提示用户在控制台中输入数据 来进行猜大小
* */
int random = (int) (Math.random() * 100);//随机生成0 ~ 100 的整数
Scanner s = new Scanner(System.in);//创建一个扫描器
int user;
do {
System.out.println("开始猜大小 区间0~99");//需要
user = s.nextInt();//接收用户输入的猜的数据 需要
if (user > random) { //如果用户输入的数据 大于 系统产生的数据
System.out.println("猜大了");
} else if (user < random) {//如果用户输入的数据 小于 系统产生的数据
System.out.println("猜小了");
}
} while (user != random);//用户猜的数据 不等于 系统产生的数据
//如果 代码能执行到这里 那么一定是猜对了!
System.out.println("猜对了");
}
相同点:当不明确循环次数的时候,但是已知循环条件,可以进行选择。
不同点:while 循环先去判断 循环条件 ,再决定是否循环!有可能一次循环都不做。
do..while循环 先去做一次循环,再决定是否循环! 至少做一次循环体
for(常用的循环)当已知循环次数 就使用 for
continue:作用:跳过当次循环 直接到达第 ③步 (直接进入下次的循环执行过程)
break:作用在循环体中,代表退出当前循环(即使循环没做完也不做了...)
package day06;
/**
* for循环使用演示类:
*/
public class ForDemo {
public static void main(String[] args) {
/** for 循环的语法结构 当已知循环次数 就使用 for
* 循环的三要素:①循环变量的初始化 ② 基于循环变量的条件 ③ 循环变量的改变
* ① ② ③
* for(int count = 0; count < 3; count++){
* //循环的代码块 ④
* }
*
* 执行过程:
* 先去执行 ① ② ④
* 然后执行 ③ ② ④ ------- ③ ② ④------- ③ ② ④
* 直到 ② 不满足 循环则结束 代码继续往后执行了....
* */
//同学们继续加油!! x3
for (int count = 0; count < 3; count++) {
//System.out.println("同学们加油666!");
}
// continue 循环中可使用的关键字 作用:跳过当次循环 直接到达第 ③步 (直接进入下次的循环执行过程)
for (int i = 1; i <= 10; i++) {
if (i % 2 == 0) { //如果成立 则为偶数
System.out.println(i + ":是偶数");
continue;//跳过当次循环,直接执行下次循环过程
}
System.out.println(i + ":是奇数");
}
//---------break 关键字 作用在循环体中,代表退出当前循环(即使循环没做完也不做了...)
for (int i = 1; i <= 10; i++) {
if(i > 5){
System.out.println("马润同学肚子疼...剩下圈数不能跑了..");
break;//退出循环
}
System.out.println("马润同学跑第" + i + "圈了");
}
System.out.println("不跑了 让孩子歇歇吧...");
}
}
for循环小括号中声明的变量,作用域只在当前for可用,其它地方视为看不见!
for(int i = 0; i < 3; i++){
}
for(int i = 0; i < 3; i++){ //并不冲突
}
for练习题:
当运行程序以后,随机出10道100以内的加法运算,然后让用户输入答案 ,然后再判断是否答对。若答对+10分。
(1) 55 + 65 = ?
请回答!
120
答对了!
(2) 67 + 99 = ?
请回答!
166
答对了
(3) 16 + 89 = ?
请回答!
110
答错了!
(4).....
(10) 1 + 1 = ?
请回答!
2
答对了!
输出信息:共答对了多少道题、成绩判定为 ??
//随机出 1道 加法题。
// 出题 答题 判题
int score = 0;//分数
Scanner s = new Scanner(System.in);//创建一个扫描器
for (int i = 1; i <= 10; i++) { //10次循环
int n1 = (int) (Math.random() * 101);//随机0~100的整数
int n2 = (int) (Math.random() * 101);//随机0~100
System.out.println("(" + i + ")" + n1 + " + " + n2 + " =?");
System.out.println("请答题:");
int user = s.nextInt();//接收控制台输入的数据 接收给用户
if (user == (n1 + n2)) { //如果用户输入的数据等于 系统出题的和
System.out.println("答对了");
score += 10; //自增
} else {
System.out.println("答错了");
}
}
双重for循环
// 双重for 九九乘法表
for (int num = 1; num <= 9; num++) { //外层走一轮
for (int i = 1; i <= num; i++) {//内层走多次 (具体看循环条件)
// 删除ln 代表不换行 //tab键
System.out.print(i + " * " + num + " = " + (i * num)+"\t");
}
System.out.println();//换行
}
数组
定义:代表一组相同数据元素的集合,数组是一种数据类型 (引用数据类型)
3.数组名.length 代表数组的总长度 ,通常用于for遍历数组时的条件.
数组可以理解为家里的衣柜,橱柜,鞋柜。
-----------------------------
int a;//声明一个int类型的变量名为a ---------------里面只能放整数
int[] b;//声明一个int类型的数组 数组名为b ---------------里面可以放多个整数
double[] c;//声明一个double类型的数组 数组名为c -----------里面可以放多个小数
int score1 = 100;
int score2 = 99;
int score3 = 60;
....
int score174 = 88;
//1.数组的定义
int[] arr;//定义: 声明一个int类型的数组 数组名为arr
//初始化方式动态初始化写法:
//未知数组中每个格子存的数据是多少,但是已知存储的数据个数
int[] arr1 = new int[3];//声明一个int类型的数组 名字为arr1 在内存中开了3个格子 分别存了默认0
//初始化方式静态初始化写法:
//已知数组中每个格子的具体数据,已知存储的个数
int[] arr2 = {100,60,20};//声明一个int类型的数组 名字为arr2 在内存中开了3个格子,分别存100,60,20
//int[] arr3 = new int[]{100,60,20}; 写法2
//2.数组的使用
int[] arr4 = new int[3];//声明一个int类型的数组 名字为arr4 在内存中开了3个格子 分别存了默认0
arr4[1] = 666;//访问数组下标为1的这块地 存了666
System.out.println(arr4[0]);//访问arr4数组第一块内容
System.out.println(arr4[1]);//访问arr4数组第二块内容
System.out.println(arr4[2]);//访问arr4数组第三块内容
//System.out.println(arr4[3]);//ArrayIndexOutOfBoundsException:访问数组下标越界的异常
//-----------------------------------------
int[] scores = new int[7];//创建数组 开了 5块地 数组名scores
scores[0] = 100;//访问下标为0 的这块地 存了100
scores[1] = 99;//访问下标为1 的这块地 存了99
scores[2] = 88;
scores[3] = 77;
scores[4] = 66;
scores[5] = 66;
scores[6] = 0;
// System.out.println(scores[0]);
// System.out.println(scores[1]);
// System.out.println(scores[2]);
// System.out.println(scores[3]);
// System.out.println(scores[4]);
//循环5次 //长度
// 依次遍历数组每个元素 并打印出来
for (int i = 0; i < scores.length; i++) {
System.out.println(scores[i]);
}
/** 需求: 通过在控制台的方式 录入10学生成绩 存入数组中. int[] scores = new int[10];
* 例如: 请输入第 1 名同学的成绩?
* 100
* 请输入第2 名同学的成绩?
* 19
* 请输入第3 名同学的成绩?
* ....
* 请输入第10名同学的成绩?
* 100
* 录入完毕!
* 输出10名同学的平均成绩 : 总成绩 / 总人数
* */
方法
适用性:当有一些重复的性的逻辑代码时,我们可以用方法来进行复用,减少代码冗余.
方法语法: [访问修饰符] 返回值类型 方法的名称 ([参数列表]) { 方法的代码块 }
2.返回值类型:没有返回值类型用 void 来表示没有返回值,如果有返回值的话 定义的时候要写上返回值类型 , 其次在方法体的最后一行代码 要写上return 关键字 代表返回 然后再后面写上要返回的数据,返回的数据要与返回值类型匹配.
4.参数列表:又叫做形式参数,定义方法时占一个坑,供外部使用时传入具体的数据内容.
方法分类:1.无参无返回值 2.有参无返回值 3.无参有返回值 4.有参有返回值
1.无参无返回值:仅仅只是想优化重复性的代码 没有其他的逻辑.
package day07;
/**
* 方法的使用演示:
* 无参无返回值的写法
*/
public class MethodDemo {
public static void main(String[] args) {
//多次定义 多次使用
//一旦发生修改 都要变!
// System.out.println("直拳");
// System.out.println("摆拳");
// System.out.println("上勾拳");
// System.out.println("侧踹");
//
// System.out.println("直拳");
// System.out.println("摆拳");
// System.out.println("上勾拳");
// System.out.println("侧踹");
//
// System.out.println("直拳");
// System.out.println("摆拳");
// System.out.println("上勾拳");
// System.out.println("侧踹");
attack(); //调用方法 从哪里来回哪去
System.out.println("程序继续往后执行");
}
//方法的语法: 没有返回值 方法的名称 () { 方法的代码块 }
//定义了一个无参数无返回 名为attack 的方法
//加static 是为了能在main中调用 否则main不可访问attack方法
static void attack() { //定义方法
System.out.println("直拳");
System.out.println("摆拳");
System.out.println("上勾拳");
System.out.println("侧踹");
System.out.println("下勾拳");
}
}
package day07;
/**
* 有参数无返回值的方法使用演示:
* */
public class MethodDemo02 {
public static void main(String[] args) {
//传递实际参数:具体的数据 注意,具体的数据要与该功能需要的类型匹配
//注意:name: 是属于编译器的提示 不需要写!!
sayHi("乾隆");//调用功能
}
//方法表示的是功能
//做一个问好的功能
//返回值类型 方法的名称 () { 方法的代码块 }
//定义功能
//目标是希望谁调用这个问好功能,可以自己选择到底问谁好!
//可以给方法加 形式参数:定义功能时占一个坑 但是具体坑里面是什么,要看使用者传递什么数据
//程序设计中的一个要求: 对修改关闭 就是设计好一个功能后尽量不要修改它了
static void sayHi(String name){
System.out.println(name + "同学你好!");
}
}
package day07;
import java.util.Scanner;
/**
* 无参数有返回值的使用演示:
*/
public class MethodDemo03 {
public static void main(String[] args) {
//方法有返回值 才可以接收!!
double pi = getPI();//调用方法
System.out.println(pi);// 3.1415926
//只有方法有返回值类型 才可以放到打印语句中! 也就是下面这种写法
System.out.println(getPI());//调用getPI方法并将返回值打印出来
// //用随机数的功能
// double r = Math.random();//返回一个0~1的double类型的小数!
// System.out.println(r);
}
//做一个功能 别人调用时 返回 PI
static double getPI() {
//3.1415926 ----PI
//返回 数据的类型要与方法的返回值类型 匹配!
return 3.1415926;
}
}
//有参有返回值:就是当一个功能 需要外部传递数据 进行计算 然后需要计算结果返回给使用者的时候
// 两个int的数据求和运算的功能
// 形式参数:占两个坑 供调用者传递具体运算的两个数据
static int sum(int a, int b) {
int r = a + b;//求 a + b的和
return r;
}
好处:当方法参数个数和方法参数类型发生变化时,我们在定义对应的功能时,可以不需要起不同的方法名称,可以共用一个方法名. 对于调用者而言,非常友好,只需要传递要计算的内容即可,不需要区分使用的是哪一个功能,系统会根据调用者传入的数据来自动匹配对应的功能实现.
重载的条件:当方法参数个数或者参数类型不一样时,则可构建成重载!我们可以共用一个方法名.
不能重载的条件:参数个数和参数类型都一样 则不可构建成重载.
package day07;
/**
* 有参数有返回值的使用演示:
*/
public class MethodDemo04 {
public static void main(String[] args) {
/** 需求是多变的!
* 小峰做程序:
* 做一个 10 + 20 的 运算
* 做一个 100 + 1000 的运算
* 做一个程序 5000000+ 50000 的运算
* 做一个 50 + 50 +100 的运算
* 做一个 1.5 + 4.5 的运算
* 使用方法来实现:
* */ //从需求的角度:数据是多变的! 数据的个数 数据的类型
// int a = 10;
// int b = 20;
// int r = a + b;
// System.out.println(r);//30
// int c = 100;
// int d = 1000;
// System.out.println(c+d);//1100
//.....
int result = sum(10, 20);//调用功能
//----------重载的使用
System.out.println( sum(100,1000));
System.out.println( sum(10,10,10));
System.out.println(sum(1.5,4.5));
//println 打印功能实现了重载
System.out.println(10);
System.out.println(1.1);
System.out.println("hello");
System.out.println(true);
System.out.println('a');
}
//有参有返回值:就是当一个功能 需要外部传递数据 进行计算 然后需要计算结果返回给使用者的时候
// 两个int的数据求和运算的功能
// 形式参数:占两个坑 供调用者传递具体运算的两个数据
static int sum(int a, int b) {
int r = a + b;//求 a + b的和
return r;
}
static int sum(int a, int b, int c) {
return a + b + c;
}
static double sum(double a, double b) {
return a + b;
}
}
package day07;
/**
* 数组 与 方法 结合的案例:
*/
public class MethodDemo05 {
public static void main(String[] args) {
/**
* 需求: 写一个程序,求出数组中最大值!
// 运行打印出数组的最大值.
* */
int[] array = {100, 60, 30, 20, 1000};
int max = getArrayMax(array);//调用功能 传入要求最大值的数组
System.out.println(max);
int[] arr1 = {10000,500,30000,40000};
int max1 = getArrayMax(arr1);//调用功能 传入要求最大值的数组
System.out.println(max1);
// //思路:假设数组第一个元素是最大值,然后存给max变量
// // 在遍历的过程中 需要用max 与 后面的内容 依次作比较 (始终保持max是数组中最大值)
// int max = array[0];//把第一个元素 存给了max
// for (int i = 0; i < array.length; i++) {
// if(max < array[i]){//判断max 是否小于当前数组的元素
// max = array[i];//则将当前数组元素的数据 给max
// }
// }
// System.out.println("数组中最大值为:"+max );
}
//写一个功能 , 通过外部传入一个数组 求出数组中最大值 并返回出去!
// 占一个坑 具体什么数组由调用者传入
static int getArrayMax(int[] array) {
int max = array[0];//把第一个元素 存给了max
for (int i = 0; i < array.length; i++) {
if (max < array[i]) {//判断max 是否小于当前数组的元素
max = array[i];//则将当前数组元素的数据 给max
}
}
return max;
}
}
FUNDAMENTAL02
面向对象
学完面向对象
大概听懂了,但是让你设计一个内容设计不出来---------优秀!
面向对象的三大特征:
1.对象和类
类:抽象不具体的事物,通常表示一类事物的类别, 在程序中类表示模板.
一个战舰,一堆深水炸弹对象,一堆侦察潜艇,一堆水雷潜艇,一堆鱼雷潜艇,一堆水雷,一堆鱼雷
os1 --------x , y , width , height ,spped
os2 ---------x , y , width , height ,spped
os3 ----------x , y , width , height ,spped
os4 ----------x , y , width , height ,spped
.....
os100 -------x , y , width , height ,spped
使用类来去解决:
类 对象
模具 月饼
类中放所有对象共有数据/属性 -----------------变量
类中放所有对象共有的行为/方法 --------------方法
class Person{//人类的模板
//所有对象共有数据/属性
int age;//年龄
String name;//姓名
char sex;//性别
//所有对象共有的行为
void eat(){ //吃的行为
}
void sleep(){//睡觉
}
void play(){//玩耍
}
}
-----------------------------------------------
class car{//车类的模板
//共有数据
String color;//颜色
String type;//型号
double price;//价格
//共有的行为
void run(){//前进的行为
}
void back(){//后退的行为
}
void stop(){ //刹车的行为
}
}
-------------------------------------------------
class ObserverSubmarine{//侦察潜艇类
//所有侦察潜艇对象共有的数据
int x;
int y;
int width;//宽
int height;//高
int speed;//速度
//共有的行为
void step(){ //移动的行为
}
}
1.成员变量:声明在类的里面,方法的外的变量,叫做成员变量,作用域在整个类中!
2.局部变量:声明在方法里面的变量,都叫做局部变量,作用域在当前方法种!
package oo.day01;
/**
* 学生类模板: 代表所有学生对象
* 类中放所有学生对象共有的属性(变量)和行为(方法)
* 模板中不要放main 功能
*/
public class Student {
//共有的属性 成员变量作用域在整个类体中
String name;//姓名
int age;//年龄
int stuID;//学号
//共有的行为
void study() {//学习的行为
//int a = 0;方法声明的变量为局部变量 作用域只在当前方法中可见
System.out.println(name + "在刻苦学习...");
}
void sayHi() {
System.out.println("大家好,我叫" + name + ",今年" + age + "岁了,我的学号是:" + stuID);
}
}
数据类型 变量名 创建一个学生对象
Student stu = new Student(); //创建了一个学生对象 将对象放到stu这个变量里面
Student zs = new Student(); //创建了一个学生对象 将对象放到zs这个变量里面
Student ls = new Student(); //创建了一个学生对象 将对象放到ls这个变量里面
2.哪个对象 打点 调用 属性 并赋值 那么就是给那个对象的数据进行赋值。
3.哪个对象 打点 调用 方法 那么调用的方法里面用到数据都是那个对象的!
package oo.day01;
/**
* 学生模板的测试类:
* 用于创建学生类对象
* */
public class StudentDemo {
public static void main(String[] args) {
//创建对象的语法
Student zs = new Student();//创建一个学生对象 存到了 zs变量里面. 拿zs就相当于用这个具体对象!
//zs 这个变量 能打点调用 出什么 取决去当前 对象的模板!
//哪个对象 打点 调用 属性 并赋值 那么就是给那个对象存到数据
//访问对象的语法
zs.name = "张三"; //为zs这个对象的姓名赋值 为"张三"
zs.age = 30; //为zs这个对象的年龄赋值 为30
zs.stuID = 1001;//为zs这个对象的学号赋值 为1001
//哪个对象 打点 调用 方法 那么调用的方法里面用到数据都是那个对象的!
zs.study();//调用zs这个对象 的学习行为
zs.sayHi();//调用zs这个对象 的问好行为
//------------------------------------------
Student ls = new Student();//创建一个学生对象 赋值给 ls这个变量
ls.name = "李四";
ls.age = 40;
ls.stuID = 1002;
ls.study();
ls.sayHi();
}
}
2.项目需求:
深海杀手项目分析:
1.识别对象:
战舰,深水炸弹,侦察潜艇(银色),鱼雷潜艇(金黄),水雷潜艇(军绿),水雷(圆形),鱼雷(长条形)
2.分配职责:
战舰(玩家):发射深水炸弹。
深水炸弹: 可以攻击(三种潜艇),若打到时:
1.深水炸弹和被打倒的潜艇要消失。
2.打到侦察潜艇加10分,打到鱼雷潜艇加40分。
3.打到水雷潜艇加1条命。
鱼雷潜艇: 可以发射鱼雷攻击战舰,若打到时:
1.鱼雷要消失
2.战舰要减1条命(命数为0,则游戏结束)
水雷潜艇:可以发射水雷攻击战舰,若打到时:
1.水雷要消失
2.战舰要减1条命(命数为0,则游戏结束)
3.建立交互......
2.1在src这个包下,创建一个包 cn.tedu.submarine
战舰类: -------------------------Battleship
属性: x ,y , width ,height,speed
行为: 移动的行为 void step (){ }
深水炸弹类: -------------------------Bomb
属性: x ,y , width ,height,speed
行为: 移动的行为 void step (){ }
侦察潜艇类: -------------------------ObserverSubmarine
属性: x ,y , width ,height,speed
行为: 移动的行为 void step (){ }
鱼雷潜艇类: -------------------------TorpedoSubmarine
属性: x ,y , width ,height,speed
行为: 移动的行为 void step (){ }
水雷潜艇类: -------------------------MineSubmarine
属性: x ,y , width ,height,speed
行为: 移动的行为 void step (){ }
水雷类: -------------------------Mine
属性: x ,y , width ,height,speed
行为: 移动的行为 void step (){ }
鱼雷类: -------------------------Torpedo
属性: x ,y , width ,height,speed
行为: 移动的行为 void step (){ }
package cn.tedu.submarine;
/**
* 游戏窗口类:负责运行游戏
* 存放项目运行相关的代码逻辑
* */
public class GameWorld {
Battleship ship;//声明一个战舰类型的变量
Bomb bomb;//声明一个深水炸弹类型的变量
ObserverSubmarine os;//声明一个侦察潜艇类型的变量
TorpedoSubmarine ts;//声明一个鱼雷潜艇类型的变量
MineSubmarine ms;//声明一个水雷潜艇类型的变量
Torpedo t;//声明一个鱼雷类型的变量
Mine m;//声明一个水雷潜艇类型的变量
//用来测试的方法
void action() {
ship = new Battleship();//创建一个战舰类对象 存到ship这个变量里面
ship.x = 120;
ship.y = 250;
ship.width = 100;
ship.height = 200;
ship.speed = 50;
bomb = new Bomb();//创建一个深水炸弹对象 存到 bomb里面
bomb.x = 104;
bomb.y = 31;
bomb.width = 100;
bomb.height = 200;
bomb.speed = 50;
os = new ObserverSubmarine();//创建一个战舰类对象 存到ship这个变量里面
os.x = 120;
os.y = 250;
os.width = 100;
os.height = 200;
os.speed = 50;
ts = new TorpedoSubmarine();//创建一个深水炸弹对象 存到 bomb里面
ts.x = 104;
ts.y = 31;
ts.width = 100;
ts.height = 200;
ts.speed = 50;
ms = new MineSubmarine();//创建一个深水炸弹对象 存到 bomb里面
ms.x = 104;
ms.y = 31;
ms.width = 100;
ms.height = 200;
ms.speed = 50;
}
public static void main(String[] args) {
GameWorld gw = new GameWorld();
gw.action();
}
/**
* 1.为什么要将各类的变量声明在main的外面呢?
* 答:在main方法中声明的变量作用域只在main中,后期当前类其它的地方也需要用到这些变量,
* 所以应设计为成员变量。
* 2.为什么要单独写一个action方法做测试?
* 答:因为main是static修饰的方法,比较特殊,普通成员变量无法方法main中使用,所以需要单独
* 定义普通的action方法,来进行测试。
* 3.为什么要创建GameWorld类对象再调用action方法?
* 答:因为main是static修饰的方法,比较特殊,普通方法是无法放到main中使用,所以我们可以通过
* 创建GameWorld类对象 因为对象可以调用action方法。
**/
}
问:再action方法中,创建对象并为对象赋值数据的操作特别麻烦,每个对象照目前来讲要赋值5次数据,多个对象就要操作赋值多次,代码也非常冗余。
解决:可以通过构造方法来来解决创建对象赋值的繁琐过程!
构造方法:
1.适用性:再创建对象时,可以快速实现为当前对象的属性赋值(初始化赋值)。
2.构造方法的语法: 类名(){ } (注意:没有返回值那一说,加了就报错)
Java规定:如果不给类模板写构造方法,系统会自动送一个无参数的构造方法。
特性:当前类模板,若被创建对象时,系统则会自动调用对应的构造方法。
4.构造方法也可以重载:只要满足构造方法名相同,参数个数或参数类型不同即可。
5.如果在一个类中写了构造方法,系统则不会在赠送默认的构造方法(无参数的构造方法)
package oo.day01;
/**
* 学生类模板: 代表所有学生对象
* 类中放所有学生对象共有的属性(变量)和行为(方法)
* 模板中不要放main 功能
*/
public class Student {
//共有的属性 成员变量作用域在整个类体中
String name;//姓名
int age;//年龄
int stuID;//学号
//有参数的构造方法
Student(String name1,int age1,int stuID1) { //构造方法再类被创建对象时执行
name = name1;
age = age1;
stuID = stuID1;
}
//构造方法语法: 类名(){}
//无参数的构造方法
Student(){
}
//共有的行为
void study() {//学习的行为
int a = 0;//方法声明的变量为局部变量 作用域只在当前方法中可见
System.out.println(name + "在刻苦学习..."); //隐式写法:this.name 代表当前对象的name
}
void sayHi() {
System.out.println("大家好,我叫" + name + ",今年" + age + "岁了,我的学号是:" + stuID);
}
}
this关键字:
this指代的是当前对象,使用this关键可以区分成员变量 和 局部变量。
Java规定 局部变量可以与成员变量名字一样。在使用变量时,遵循就近原则。
解决:可以用this关键字 明确表示 成员变量 避免与局部变量冲突的问题。
main{
Student zs = new Student();
Student ls = new Student();
Student ww = new Student();
}
zs.sayHi();-----------------this 指代的就 zs 这个对象
ls.sayHi();-----------------this 指代的就 ls 这个对象
ww.sayHi();-----------------this 指代的就 ww 这个对象
默认值:引用类型的默认值都是null (String,数组,自定义类)
值类型中:整数默认值为0 , 小数默认为 0.0 boolean类型默认为 false
如果要拿一个null(空的)引用类型变量 打点访问内容,会报异常!NullPointerException空指针异常。
内存是JVM来进行分配划分: 栈区 、 堆区 、 方法区(后续)
基本数据类型的变量直接存数据,引用数据类型变量存的是对象地址。
1.栈区:用来存放局部变量的区域,局部变量指的就是方法中声明的变量。特性:方法执行完以后,方法中所声明的变量 则会被销毁。
2.堆区:用来存放对象的区域,对象指的是通过new关键字的语法创建的对象!特性:堆区的对象地址 若没有被引用的话,则会变为内存垃圾。
int[] array = new int[3];
Student[] stus = new Student[3];
问题:当前项目7个类,若被创建对象时,需要依次打点调用对象的数据内容进行初始化赋值,非常麻烦
战舰类构造方法:
Battleship(){//当前这个类模板的无参数构造方法
x = 270;
y = 124;
width = 66; //图片的宽
height = 26;//图片的高
speed = 20;//每次移动多少
life = 5;//游戏开始5条命
}
深水炸弹类构造方法:
Bomb(int x, int y) {//构造方法
this.x = x;
this.y = y;
width = 9;
height = 12;
speed = 3;//移动速度
}
鱼雷类的构造方法:
Torpedo(int x, int y){
this.x = x;
this.y = y;
width = 5;
height = 18;
speed = 2;
}
水雷类的构造方法:
Mine(int x, int y){
this.x = x;
this.y = y;
width = 11; //宽
height = 11;//高
speed = 2;//速度
}
侦查潜艇类的构造方法:
ObserverSubmarine(){
width = 63;
height = 19;
x = -width;//x坐标负一个图片的宽度 表示在屏幕左侧外面.
y = (int) (Math.random() * (479 - height - 150) + 150); //随机y坐标
speed = (int) (Math.random() * (3 - 1) + 1);//随机速度1~3
}
水雷潜艇类的构造方法:
MineSubmarine() {
width = 63;
height = 19;
x = -width;//x坐标负一个图片的宽度 表示在屏幕左侧外面.
y = (int) (Math.random() * (479 - height - 150) + 150); //随机y坐标
speed = (int) (Math.random() * (3 - 1) + 1);//随机速度1~3
}
鱼雷潜艇类的构造方法:
TorpedoSubmarine(){
width = 64;
height = 20;
x = -width;//x坐标负一个图片的宽度 表示在屏幕左侧外面.
y = (int) (Math.random() * (479 - height - 150) + 150); //随机y坐标
speed = (int) (Math.random() * (3 - 1) + 1);//随机速度1~3
}
问题:除了战舰只有一个,其他类型都会存在多个对象,如果存储同一类型的多个对象.
Battleship ship;//声明一个战舰类型的变量
Bomb[] bombs;//声明一个深水炸弹类型数组的变量
ObserverSubmarine[] os;//声明一个侦察潜艇类型数组的变量
TorpedoSubmarine[] ts;//声明一个鱼雷潜艇类型数组的变量
MineSubmarine[] ms;//声明一个水雷潜艇类型数组的变量
Torpedo[] t;//声明一个鱼雷类型数组的变量
Mine[] m;//声明一个水雷潜艇类型数组的变量
继承:
继承的适用性:当多个类之间存在一些共性属性和行为时,且它们在概念(is a)上达到一致,才可以使用继承来优化冗余的代码.
3.子类对象不仅可以访问自己的 内容,还可以访问父类中的内容(继承过来就是自己有)。
class Person{ //人类 --------代表 父类 / 超类
String name; //姓名
int age;//年龄
char sex;//性别
//----------------------------
void eat(){ } //吃
void sleep(){ }//睡
}
class Student extends Person { //学生 子类 / 派生类
//学生对象共有的数据
int stuID;//学号
//共有的行为
void study(){ }//学习
}
class Teacher extends Person{ //老师类 子类 / 派生类
double salary;//工资
//行为
void teach(){ }
}
class Doctor extends Person{//医生 子类 / 派生类
int level;//职称
//
void cut(){ } //手术
}
class 爷爷类{
传家宝();
}
class 爸爸类 extends 爷爷类{
传家宝();
}
class 儿子类 extends 爸爸类{
传家宝();
}
解决:将7个类模板抽取共性的属性和行为到SeaObject类中,然后7个类通过继承来复用父类中提供的属性和行为.
7个类删掉x y width height speed 行为 step ,继承SeaObject类即可
package cn.tedu.submarine;
/**
* 海洋对象类:
* 所有海洋对象的父类
* 存放所有子类共有的属性和行为
* */
public class SeaObject {
//共有的属性
int x ;
int y ;
int width;
int height;
int speed;
//共有的行为
void step(){//移动的行为
System.out.println("海洋对象在移动......");
}
}
Java规定:
实现子类之前,父类默认有自己的无参构造方法,构造方法不可以继承,各的各的.
1.如果实现了继承,在创建子类对象时,子类构造方法会先去执行父类的构造方法,然后再执行子类的构造方法里的内容. 若没有明确为父类或者子类提供构造方法,系统则会调用默认提供的构造方法. 子类的构造方法中有隐式写法-----> super();
2.若父类写了有参数的构造方法,而没写无参的构造方法,那么子类若实现继承,子类则报错!需要手动明确的调用父类有参的构造方法.
注意事项:若明确写了子类的构造方法,调用父类的构造方法时需要放到第一行.
现象:再创建子类对象时,一定会执行父类的构造方法 ,然后在执行子类的构造方法内容.
package oo.day02;
/**
* 父与子构造方法的测试类
*/
public class SuperDemo {
public static void main(String[] args) {
Boo b = new Boo();//创建Boo这个对象
}
}
class Aoo { //父类
// Aoo() {
// System.out.println("父类Aoo的无参构造方法");
// }
Aoo(int a) {
System.out.println("a的值为:" + a);
}
}
class Boo extends Aoo {//子类
Boo() {//无参构造方法
super(5);
System.out.println("Boo的构造方法中的内容");
}
}
super.成员变量 ------------------------------访问父类的成员变量(应用率极低)
super.方法 -----------------------------------访问父类的方法(后续会讲)
super() ------------------------------------访问父类无参构造方法,如果括号写了参数,明确访问 父类有参数的构造方法.
问题:当前侦查潜艇/水雷潜艇/鱼雷潜艇 的构造方法重复 冗余.
解决:在创建子类对象时 会先执行父类的构造方法,我们可以将重复多份代码提取到父类构造方法中,达到代码的复用.
/**
* 此构造方法是专门为三种潜艇提供构造的方法
* 因为潜艇宽高不同,所以宽高不能写死,做成形式参数,由具体的子类传递它的宽高数据
* x y speed 初始化的数据都是一样的 则写死即可.
* */
SeaObject(int width,int height){//构造方法
this.width = width;
this.height = height;
x = -width;//x坐标负一个图片的宽度 表示在屏幕左侧外面.
y = (int) (Math.random() * (479 - height - 150) + 150); //随机y坐标
speed = (int) (Math.random() * (3 - 1) + 1);//随机速度1~3
}
水雷潜艇:
MineSubmarine() {
super(63,19);//调用父类的构造方法 传递当前潜艇的宽高即可
}
侦查潜艇:
OberverSubmarine() {
super(63,19);//调用父类的构造方法 传递当前潜艇的宽高即可
}
鱼雷潜艇:
TorpedoSubmarine() {
super(64,20);//调用父类的构造方法 传递当前潜艇的宽高即可
}
剩下的4个类,战舰/深水炸弹/水雷/鱼雷 的构造方法 赋值的过程是重复的.所以 可以在父类中提供一个为当前这4个类初始化的构造方法.
/** 因为赋值的过程是重复的.
* 此构造方法是为战舰.深水炸弹.鱼雷和水雷提供的
* 因为这4个类的具体数据都不同,所以做成形式参数,具体由当前类使用时提供
* */
SeaObject(int x,int y ,int width,int height,int speed){
this.x = x;
this.y = y;
this.width = width; //图片的宽
this.height = height;//图片的高
this.speed = speed;//每次移动多少
}
战舰类:
Battleship(){//当前这个类模板的无参数构造方法
super(270,124,66,26,20);
life = 5;//游戏开始5条命
}
深水炸弹类
Bomb(int x, int y) {//构造方法
super(x,y,9,12,3);
}
鱼雷类:
Torpedo(int x, int y){
super(x,y,5,18,2);
}
水雷类:
Mine(int x, int y){
super(x,y,11,11,2);
}
学生类:
package oo.day02;
/**
* 学生类模板
* 存放自己特有的属性和行为
* */
public class Student extends Person { //学生类继承了 人类
//学生对象共有的数据
int stuID;//学号
Student(String name,int age,char sex,int stuID){
super(name, age, sex);
this.stuID = stuID;
}
//共有的行为
void study(){//学习
}
}
//------------------------------------------------
package oo.day02;
/**
* 老师类
* 存放自己特有的属性和行为
* */
public class Teacher extends Person {//老师类继承人类
double salary;//工资
Teacher(String name,int age,char sex,int salary){
super(name, age, sex);
this.salary = salary;
}
//行为
void teach(){ }
}
-------------------------------------------------
package oo.day02;
/**
* 医生类
* */
public class Doctor extends Person { //医生类继承 人类
int level;//职称
Doctor(String name,int age,char sex,int level){
super(name, age, sex);
this.level = level;
}
//
void cut(){ } //手术
}
-------------------Person类---------------------------
package oo.day02;
/**
* 人类 ------代表学生类、老师类、医生类的 父类
* 存放子类共有的属性和行为。
*/
public class Person {
String name; //姓名
int age;//年龄
char sex;//性别
Person(String name, int age, char sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
//----------------------------
void eat() {
} //吃
void sleep() {
}//睡
void sayHi(){
System.out.println("大家好,我叫"+name+"今年"+age+"性别是:"+sex);
}
}
问题:在测试代码中,因为有三个数组,学生数组,老师数组,医生数组,那么再去遍历当前这数组中的信息时,需要写3个for循环.
解决:把3个数组变成1个数组.
向上造型:
class Animal{ //动物类
}
class Tiger extends Animal { //老虎类
}
main{
类型 对象
Animal a = new Tiger(); //老虎 是 动物 ---语义通
Animal a1 = new Animal();//动物 是 动物 ---语义通
Tiger t = new Tiger(); //老虎 是 老虎 ---语义通
Triger t1 = new Animal();//动物 是 老虎 ---语义不通 代码会报错.
}
// 父 Person 子 学生 老师 医生
//声明父 new 子对象
Person p = new Student("小明",10,'男',1001);
//p能打点调用什么 取决于Person中有什么属性和行为
// p.stuID; p是Person 父类型,只能访问自己内容
Person p1 = new Teacher("小明",22,'男',122222);
Person p2 = new Doctor("小明",44,'男',5);
Person[] pp = new Person[3];//声明父类型数组
//父类型数组 可以存放不同的子类型对象.
pp[0] = new Student("小明",10,'男',1001);
pp[1] = new Teacher("小s",22,'男',122222);
pp[2] = new Doctor("小t",44,'男',5);
for (int i = 0; i < pp.length; i++) {
//调用父方法 执行子对象
// 编译期间 : 调用父类的sayHi功能
// 运行期间 : sayHi方法用的数据 取决于 当前对象是谁!
//pp[i].sayHi();
}
问题:在GameWorld类中,由于三种潜艇用三个数组来表示,二种雷用两个数组来表示,后期在测试的时候,因为存在多个数组,for循环要写多次.
解决: 因为三种潜艇的行为是一样的,所以可以单独用一个父类型数组来表示三种潜艇.
因为二种雷的行为是一样的,所以可以单独用一个父类型数组来表示二种雷.
Battleship ship;//声明一个战舰类型的变量
Bomb[] bombs;//声明一个深水炸弹类型数组的变量
SeaObject[] submarines;//代表三种潜艇
SeaObject[] thunders;//代表两种雷
问题: 运行测试后,当前打印输出的信息不明确!例如项目中测试 都是海洋对象在移动... 包括继承测试类:打印的信息 不明确,不知道是医生的还是老师还是学生的信息....
重写(override):
1.定义:子类可以通过重写的方式,来解决父类中存在的方法子类不受用的情况!重写是发生在父子关系才可以.
2.若子类重写了父类的方法,在编译期调用的是父类的,运行后则执行的是子类重写后的那个方法.现象:调用父 执行子
两小:子类在重写父类方法时,返回值类型要等于或小于父类中那个方法
子类在重写父类方法时,异常要等于或小于父类中的那个方法 (异常第二阶段)
一大: 子类在重写父类方法时,访问权限要等于或大于父类中那个方法。(后续会讲)
重写的条件:子类如果想要重写父类的不适用方法,首先在子类中要写一个跟父类的那个方法一模一样. 方法体的内容 可以自行根据逻辑去实现即可.
@Override
void sayHi() { //重写语法
System.out.println("我叫"+name+"我是一名学生,我的学号是"+stuID);
}
//-----------
@Override
void sayHi() {
System.out.println("我叫"+name+"我是一名老师,我的工资:"+salary);
}
------------
@Override
void sayHi() {
System.out.println("我叫"+name+"我是一名医生,我的职称:"+level);
}
@Override
void step() {
System.out.println("战舰通过键盘左右运动");
}
-------------------------------------------------
@Override
void step() {
System.out.println("深水炸弹Y向下运动");
}
--------------------------------------------------
@Override
void step() {
System.out.println("水雷y向上运动..");
}
-------------------------------------------------
@Override
void step() {
System.out.println("鱼雷y向上运动...");
}
----------------------------------------------------
@Override
void step() {
System.out.println("水雷潜艇x向右运动");
}
--------------------------------------------------
@Override
void step() {
System.out.println("侦察潜艇x向右运动");
}
-----------------------------------------------------
@Override
void step() {
System.out.println("鱼雷潜艇x向右运动");
}
Person[] pp = new Person[3];//声明父类型数组
//父类型数组 可以存放不同的子类型对象.
pp[0] = new Student("小明",10,'男',1001);
pp[1] = new Teacher("小s",22,'男',122222);
pp[2] = new Doctor("小t",44,'男',5);
重写的情况分类:
情况一、Boo子类只想吃西餐 ------------不需要重写
情况二、Boo子类只想喝果汁 ------------需要重写
情况三、Boo子类要吃西餐也要喝果汁 --------需要重写
class Aoo{//父类
void eat(){
System.out.println("吃西餐");
}
}
class Boo extends Aoo{ //子类
void eat(){
super.eat();//调用父类的eat方法 情况三
System.out.println("喝果汁");
}
}
重写和重载区别 --------------------面试题
重写(override/overrideing) :发生在父子关系中,方法名和参数列表要相同。
重载(overload/overloading) : 发生在同一个类中(继承过来也算自己的),方法名相同,参数列表要不同(个数或类型)。
class Aoo{ 父
void show(){
}
}
class Boo extends Aoo{ 子
void show(){ ----------发生了重写
}
}
-----------------------------------------
class Coo{
void show(){
}
}
class Doo extends Coo{
void show(int a){ -------------发生了重载
}
}
画窗口
界面绘制相关的功能,swing相关的内容....不需要大家掌握,后期工作不用。
import javax.swing.JFrame;// 导入 画框功能
import javax.swing.JPanel;// 导入 底板功能
3.在GameWorld类写一个普通方法,实现绘制窗口的逻辑功能,
//绘制界面的方法
void paintWorld(){
//1.创建一个画框
JFrame frame = new JFrame();
this.setFocusable(true);
frame.add(this);//将当前类 添加到画框里
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//关闭窗口时释放程序
frame.setSize(641 + 16,479 + 39);//画框的大小
frame.setLocationRelativeTo(null);
frame.setVisible(true);//设置窗口可见
}
public static void main(String[] args) {
GameWorld gw = new GameWorld();
gw.paintWorld();//调用绘制窗口的功能
gw.action();//调用了测试的方法
}
package(包)
1000个类 想1000个类名,所以Java设计package
import(导入)
1.当前要使用的类,在同一个包下,可以直接访问,不需要导入。
2.不同包的类,在使用的时候,需要通过import 关键字来进行导入(类的全包名),才可以用
如果用到的是Java所提供的功能,若不导入功能的全包名 则不能使用。
package oo.day02;
import oo.day01.Student;
import oo.day01.Test;//类的全包名
public class Demo {
public static void main(String[] args) {
//如果当前类要用到不同包下同名的文件时,需要明确写上类的全包名(这种情况少见)
oo.day01.Student s = new oo.day01.Student();
oo.day02.Student s2 = new oo.day02.Student("",1,'1',1);
}
}
访问修饰符:
属性封装:数据私有化,方法公开化。为了数据安全,方法内可以去加一些限制条件,避免不合法的情况发生。
1.private :私有的,访问权限最小,只能当前类内部可见。
4.protected:保护的,访问权限在本类,同包子类,同包类,不同包的子类 ,不同的类不可以!
一般都在父子关系下,在父类中写的数据和行为会加上 protected权限
class Wife{ //老婆类
private String name = "王小花"; //属性私有化
private int age = 22; //属性私有化
private char sex = '女';
public void setAge(int age){ //通过方法公开化 可以让外部设置数据时 可以加一些要求
if(age < 25 && age >18){//
this.age = age;
}
}
public String getName(){ //方法公开化 让外部获取本类私有的name的方法
return name;
}
}
package oo.demo01;
/**
* private:私有的,范围权限是在当前类中
* public:公开的,任意地方都可以访问
* 默认的: 本类或同包其它类 可以访问 -----行业不用 用的很少
* protected:保护 本类 、 同包类/子类可以用 不同包子类可用 ---- 不同包类不可用
* */
public class Aoo {
private int a ;//私有的属性
public int b;//公开的属性
int c; //默认级别的属性
protected int d;//保护级别的属性
void fun3(){ //默认级别的方法
}
public void fun2(){ //公开的方法
c = 10;//本类可用
fun3();//本类可用
}
private void fun(){ //私有方法
a = 10;//本类都可以用a
}
}
package oo.demo01;
public class Boo extends Aoo{
void test(){
d = 10; //同包子类可以用
}
}
package oo.demo01;
public class TestDemo {
public static void main(String[] args) {
Aoo tt = new Aoo();
// tt.a = 0; 私有的属性 其它不可访问
// tt.fun(); 私有的方法 其它不可访问
tt.b = 10; //公开的同包的其它类可以访问
tt.fun2();//公开的同包的其它类可以访问
tt.c = 50; //默认的 同包的其它类可以访问
tt.fun3(); //默认的 同包的其它类可以访问
tt.d = 90;//保护级别 同包的其它类可以访问
}
}
package oo.demo02;
import oo.demo01.Aoo;
public class Coo extends Aoo {
void test2(){
d = 50;//不同包的子类可以访问保护级别的内容
}
}
package oo.demo02;
import oo.demo01.Aoo;
public class Demo {
public static void main(String[] args) {
Aoo pp = new Aoo();
pp.b = 50;//公开的内容 不同包其他类 也可以访问
pp.fun2();//公开的内容 不同包其他类 也可以访问
// pp.c = 0; 默认级别 不同包其他类不可访问
// pp.fun3(); 默认级别 不同包其他类不可访问
// pp.d = 0; 保护级别 不同的其它类不可访问
}
}
访问修饰符 类内部 同一包个中 非同包子类 非同包类
public √ √ √ √
protected √ √ √
默认的 √ √
private √
关键字
final:代表最终的意思。
1.修饰变量:变量不能够被二次修改!声明final的变量时,需要初始化。
2.修饰方法:用fianl修饰的方法,该方法是不允许被子类重写的!
package oo.day03;
/**
* 测试final 的类
*/
public class FinalDemo {
private int a;//声明int 的 成员变量 名为 a
// private final int c; 声明final的成员变量 需要声明时初始化
private final int b = 100;//变量 b 里面赋值的是 100
public void fun() {
System.out.println(b);
// b = 200; 编译错误:final修饰的变量不允许二次修改
final int d;//声明final的局部变量 声明时可以不做初始化
d = 150;//第一次赋值
// d = 350;编译错误:final修饰的变量不允许二次修改
}
}
//final class Aoo{ //父类 final 修饰的类不能被子类继承
// final void action(){ //final修饰的方法 不能被子类重写
// }
//}
//class Boo extends Aoo{ //子类
//
//}
1.实例变量 : 属于对象的(有多个对象就有多少份),在堆中存储,通过对象名打点访问。
2.静态变量:属于类的(且只有一个),在方法区中存储,所有对象共享静态变量。
修饰变量:用static修饰的变量称之为静态变量,通过类名打点去访问。
JVM:分配 栈区 堆区 方法区
1.栈区:存放局部变量
2.堆区:存放对象的区域
3.方法区:用来加载.class字节码文件(类中方法和静态方法/静态变量)
package oo.day03;
/**
* 静态的使用演示类
*/
public class StaticDemo {
public static void main(String[] args) {
Coo c1 = new Coo();//创建Coo对象 存给了 c1
c1.show();//显示ab值的功能
Coo c2 = new Coo();//创建Coo对象 存给了 c2
c2.show();//显示ab值的功能
Coo c3 = new Coo();//创建Coo对象 存给了 c3
c3.show();//显示ab值的功能
//静态修饰变量 不需要创建对象进行访问 可以通过类名点的形式访问!
System.out.println(Coo.b);
Coo.b = 30;
}
}
class Coo {
int a;//实例变量 属于对象的
static int b = 10; //静态变量 属于类的
Coo() {
a++;
b++;
}
void show() { // 对象的a 类的 b
System.out.println("a的值为:" + this.a + ",b的值为:" + Coo.b);
}
}
例如:当侦察潜艇类别下的所有侦察潜艇对象都需要进行图片显示,那么图片的话,我们可以做一份静态的图片类型.让所有侦察潜艇对象所共享这一张图片,节省内存。
1.用static 修饰的方法叫做静态方法,属于类,通过类名来打点使用
3.静态的方法中,没有this隐式的写法,所有也就是说为什么静态的方法无法使用实例的成员(实例的属性或普通方法)。
静态方法以后在写工具功能的时候,才会用到。主要目的就是方便使用者可以直接通过类名点的形式就可以访问。
class Coo {
int a;//实例变量 属于对象的
static int b = 10; //静态变量 属于类的
static void method(){ //静态方法
System.out.println(b);//隐式的写法 Coo.b
// System.out.println(this.a); 静态的方法是没有this传递的,所以无法访问this相关成员
// this.show(); 静态的方法是没有this传递的,所以无法访问this相关成员
}
Coo() {
a++;
b++;
//this.show();可以访问 有隐式的this 传递
}
void show() { // 对象的a 类的 b
System.out.println("a的值为:" + this.a + ",b的值为:" + Coo.b);
}
}
2.属于类,当类被加载时(当第一次用到某个类时,类且只加载一次),静态代码块则会被只执行一次。
3.如果类被创建对象时,那么优先调用静态代码块,再调用该类的构造方法
package oo.day03;
/**
* 静态代码块的使用演示
*/
public class StaticDemo02 {
public static void main(String[] args) {
//类只会被加载一次
// Doo d1 = new Doo();//此时Doo类加载到方法区中
System.out.println(Doo.a);//这种情况也是可以使用类的加载
}
}
class Doo {
public static int a = 10; //静态变量 属于类的
Doo() {
System.out.println("Doo的无参构造方法执行了");
}
static { //静态代码块 当本类被加载到方法区时 执行 且只会执行一次
System.out.println("Doo的静态代码块执行了.....");
}
}
项目中很多类别都需要用图片,例如:当侦察潜艇类别下的所有侦察潜艇对象都需要进行图片显示,那么图片的话,我们可以做一份静态的图片类型.让所有侦察潜艇对象所共享这一张图片,节省内存。
设计:项目中所需要用的图片,都需要进行加载和初始化,可以单独创建一个类 处理图片资源加载相关的一些内容.类的单一职责.
package cn.tedu.submarine;
import javax.swing.*;
/**
* 图片资源加载类:
* 负责加载和初始化项目中所需要的图片
* */
public class ImageResources {
//ImageIcon 用来存图片资源的类型
public static ImageIcon battleShip;//用来存战舰图片的变量
public static ImageIcon bomb;//用来存深水炸弹图片的变量
public static ImageIcon gameover;//用来存储游戏结束图片的变量
public static ImageIcon mine;//用来存储水雷图片的变量
public static ImageIcon minesubm;//用来存储水雷潜艇图片的变量
public static ImageIcon obsersubm;//用来存储侦查潜艇图片的变量
public static ImageIcon sea;//用来存海洋背景图片的变量
public static ImageIcon start;//用来存游戏开始图片的变量
public static ImageIcon torpedo;//用来存鱼雷图片的变量
public static ImageIcon torpedosubm;//用来存储鱼雷潜艇的变量
static{//静态代码块 当类被加载时 执行 代码块中的内容。
battleShip = new ImageIcon("img/battleship.png");
bomb = new ImageIcon("img/bomb.png");
gameover = new ImageIcon("img/gameover.png");
mine = new ImageIcon("img/mine.png");
minesubm = new ImageIcon("img/minesubm.png");
obsersubm = new ImageIcon("img/obsersubm.png");
sea = new ImageIcon("img/sea.png");
start = new ImageIcon("img/start.png");
torpedo = new ImageIcon("img/torpedo.png");
torpedosubm = new ImageIcon("img/torpesubm.png");
}
}
并在类中构建测试图片加载是否成功的代码 放到main中运行为8则代表加载成功
public static void main(String[] args) {
System.out.println(battleShip.getImageLoadStatus());
System.out.println(bomb.getImageLoadStatus());
System.out.println(gameover.getImageLoadStatus());
System.out.println(mine.getImageLoadStatus());
System.out.println(minesubm.getImageLoadStatus());
System.out.println(obsersubm.getImageLoadStatus());
System.out.println(sea.getImageLoadStatus());
System.out.println(start.getImageLoadStatus());
System.out.println(torpedo.getImageLoadStatus());
System.out.println(torpedosubm.getImageLoadStatus());
}
常量
用static final 修饰的变量称之为常量,应用率较高。
常量特点:结合static 和final的特性,通过类名访问,不能二次修改
package oo.day03;
/**
* 常量的使用演示类:
*/
public class StaticFinalDemo {
public static void main(String[] args) {
//编译期间自动会将常量中的内容 转换为具体的值 效率更高
// System.out.println(900);
System.out.println(Eoo.C);//通过类名点的形式访问常量
// Eoo.C = 1000; 不能二次修改
//1.假设第一次用Eoo,先将Eoo加载到方法区
//2.获取方法区中存的a这个静态的数据
System.out.println(Eoo.a);//通过类名点的形式访问静态变量
}
}
class Eoo{
static int a = 100;//静态变量 通过类名访问
final int b = 100;//fianl修饰的变量 不能二次修改 且需要声明时初始化
//常量
static final int C = 700;
}
常量的适用性:因为我们的游戏窗口大小是固定的,而且后续其它地方都会用到这个窗口大小,如果一旦窗口大小发生修改 ,那么很多地方都需要改,所以我们可以将窗口大小设计为常量,方便使用和修改。
public static final int WIDTH = 641;//宽
public static final int HEIGHT = 479;//高
在GameWorld类的paintWorld方法里 将使用到的宽高具体 数据修改为定义好的常量
在SeaObject类中为潜艇提供的构造方法 将使用的高度 也改成定义好的常量
1.将所有(概念统一的)类的共性属性和行为 提取到父类中 (泛化)
2.在父类中 若所有子类的行为具体实现逻辑都一样, 设计为普通方法。
抽象方法:
抽象类:
2.抽象类中只是在原有的普通类基础上可以多放些抽象方法。(既可以放抽象也可以放普通方法)
5.抽象类不能被创建对象的,但是可以创建抽象类型数组的对象。
可以包含抽象方法,由具体子类决定当前抽象方法的具体实现逻辑。
子类的行为逻辑虽然不同,但是我们在父类中写的那个抽象方法,在使用向上造型时 提供统一的入口。
// SeaObject s = new SeaObject();//抽象类不能被创建对
SeaObject[] ss = new SeaObject[5];//创建抽象类型的数组对象可以
1).抽象方法还有意义吗?为什么不直接用普通方法呢?
答:遵循面向对象设计规则,其次做成抽象方法的原因在于子类的行为逻辑都不一样,父类中做这个抽象方法就是为了约束子类必须要进行重写实现。
2).子类既然都重写了,那么父类的抽象方法为什么不能删?
答:父类中存在抽象方法 就是为了实现向上造型后,父类可以提供一个入口,统一管理子类的行为,具体执行时 执行的则是各个不同子类的实现逻辑。
画对象的图片只需要三个步骤:
贴切到项目中画对象
每个对象都需要绘制图片,意味着每个类都要写一个获取图片的方法。如果每个类都写获取图片的方法的话,则每个类都有共性的行为,此时可以提取到父类中,但是因为每个子类获取图片的具体行为逻辑不一样,所以提取到父类的获取图片的方法应该设计为抽象方法!
public abstract ImageIcon getImage();
获取图片的具体逻辑,每个对象都是有状态的,例如:活着的状态和死亡的状态,当判断当前对象活着的情况下再去返回图片。所有对象都有活着和死亡的状态,且这个状态是固定的。那么可以写到父类做两个常量表示死亡和活着的状态。还应该做一个表示当前对象状态的变量。
public static final int LIVE = 0;//活着的状态
public static final int DEAD = 1;//死亡的状态
//当前状态
public int currentState = LIVE;//默认初始化对象都是活着的状态
在SeaObject类中添加两个普通方法,用来判断是否是活着的或者是是否是死亡的方法。
/** 判断当前调用方法这个对象 是否是 死亡状态 */
public boolean isDead(){
return currentState == DEAD;
}
/** 判断当前调用方法这个对象 是否是 活着状态 */
public boolean isLive(){
return currentState == LIVE;
}
战舰类:
//该方法被对象调用
@Override//战舰比较特殊 并不是一打就死亡 所以在这里直接返回图片的就可以了
public ImageIcon getImage() {
return ImageResources.battleShip;//返回战舰图片
}
-----------------------------------------------------------
深水炸弹类:
@Override
public ImageIcon getImage() {
if (this.isLive()) {//如果当前这个对象是活着的
return ImageResources.bomb;//返回深水炸弹图片
}
return null;//如果能执行这一行 说明当前对象是死亡状态 返回null
}
-------------------------------------------------------------
水雷类:
@Override
public ImageIcon getImage() {
if(this.isLive()){//如果当前对象是活着的话
return ImageResources.mine;//返回水雷图片
}
return null;//执行到这里 则表示对象不是活着的 则返回null
}
-----------------------------------------------------------------
水雷潜艇:
@Override
public ImageIcon getImage() {
if(this.isLive()){//如果当前对象是活着的话
return ImageResources.minesubm;//返回水雷潜艇图片
}
return null;//执行到这里 则表示对象不是活着的 则返回null
}
--------------------------------------------------------------------
侦察潜艇:
@Override
public ImageIcon getImage() {
if(this.isLive()){//如果当前对象是活着的话
return ImageResources.obsersubm;//返回侦察潜艇图片
}
return null;//执行到这里 则表示对象不是活着的 则返回null
}
----------------------------------------------------------------------
鱼雷
@Override
public ImageIcon getImage() {
if(this.isLive()){//如果当前对象是活着的话
return ImageResources.torpedo;//返回鱼雷图片
}
return null;//执行到这里 则表示对象不是活着的 则返回null
}
------------------------------------------------------------------
鱼雷潜艇
@Override
public ImageIcon getImage() {
if(this.isLive()){//如果当前对象是活着的话
return ImageResources.torpedosubm;//返回鱼雷潜艇图片
}
return null;//执行到这里 则表示对象不是活着的 则返回null
}
获取对象图片的功能有了,但是每个对象都需要绘制啊,绘制的过程又重复了,将绘制图片的行为提取到父类中复用。
/**
* 因为每个子类都需要进行绘制,那么就将绘制的行为提取到父类中
* 因为每个子类绘制的逻辑都是一样的 所以做一个普通方法
* 参数需要一个画笔 通过外部调用方法时传递一个画笔
*/
public void paintImage(Graphics g) {
//如何表示调用该方法的对象
if (this.getImage() != null) { //如果当前对象获取图片不为null
ImageIcon icon = this.getImage();//获取当前对象图片
//1.填null 2. 填 g 3.x坐标 4.y坐标
icon.paintIcon(null, g, this.x+100, this.y);//绘制当前图片
//+100是为了能看到潜艇 后续还是要删的
}
}
在GameWorld类中paint方法中测试绘制图片的功能:
@Override //g 可以理解为画笔
public void paint(Graphics g) { //JPanle 提供了绘制图片的方法
ship.paintImage(g);
for (int i = 0; i < submarines.length; i++) {
submarines[i].paintImage(g);
}
}
问题:运行游戏以后,潜艇能够绘制出来,但是不能移动. 潜艇的移动是自动发生的.
成员内部类(应用率不高)
class Aoo{ //外部类
class Boo{ //内部类
}
}
-----------------
package oo.day04;
/**
* 测试成员内部类
* */
public class TestDemo {
public static void main(String[] args) {
Mama a = new Mama();
// Baby b = new Baby(); 内部类对外不具备可见性
}
}
class Mama{//外部类 --
private int a;
void action(){
Baby b = new Baby();//外部类可以创建内部类对象
}
class Baby{//内部类
int c;
void test(){
this.c = 100;//隐式this的传递 代表自己的东西
Mama.this.a = 10;//内部类可以共享外部类的属性和行为 隐式写法类名.this.xx
Mama.this.action();
}
}
}
匿名内部类(应用率高)
适用性:如果一个子类,仅仅只是想重写实现父类中某个方法时,其他地方也根本用不到这个子类,那么我们可以直接使用匿名内部类的方式,快速实现重写的逻辑!
1.匿名内部类只会存在于子类要重写父类的(抽象/普通)方法时,才会用到.
2.匿名内部类 无法修改外部类的值!因为规定在匿名内部类的类体中使用外部类的属性时,会自动修饰为final
package oo.day04;
/**
* 匿名内部类的测试类
*/
public class NsInnerClassDemo {
public static void main(String[] args) {
int b = 10;
//使用匿名内部类的方式:
//1.创建Aoo的子类 只不过没有名字
//2.将当前这个没有子类对象 地址 赋值给了 a
//3.花括号就是子类的类体!
Aoo a = new Aoo() { //创建的匿名内部类的外部类是NsInnerClassDemo这个类
//创建的匿名内部类的父类是 Aoo这个类
@Override
public void show() {
// b = 100; 匿名内部类无法修改外部类的属性的
// System.out.println(b); 匿名内部类使用外部类属性会默认为 final
System.out.println("匿名内部类重写Aoo的show方法");
}
};
a.show();
//常规的重写
// Boo b = new Boo();
// b.show();
}
}
class Aoo { //-----作为父类
public void show() {
System.out.println("Aoo这个父类的show方法");
}
}
//常规的重写
class Boo extends Aoo { //构建父子关系
@Override
public void show() {
System.out.println("Boo子类重写父类的show方法");
}
}
外部类的类名 字节码文件 成员内部类 内部类的字节码文件
Mama Mama.class Mama.Baby Mama$Baby.class
外部类的类名 字节码文件 匿名内部类的字节码文件
NsInnerClassDemo NsInnerClassDemo.class NsInnerClassDemo$1.class
动态入场
1.给闹钟定一个任务 2.开始延时多久的时间执行这个任务 3.闹钟任务执行一次后距离下 次执行的间隔时间.
1.具体执行的任务 2.延时多久开始第一次执行 3.执行第一次以后下次执行的间隔时间
Java提供了任务类模板 TimeTask / 定时器模板 Timer
import java.util.TimerTask; //任务模板
import java.util.Timer; //定时器模板
Timer timer = new Timer();//创建具体 定时器对象
TimerTask task = new TimerTask(){
@Override
public void run() { //自定义的任务逻辑方法
System.out.println("叮叮叮~~~~~~~~~");
}
};
//1.具体执行的任务 2.延时多久开始第一次执行(毫秒) 3.执行第一次以后下次执行的间隔时间(毫秒)
timer.schedule(task,5000,1000);
潜艇(入场)生成是不确定数量的,没有办法去在运行之前定义死到底生成多少个潜艇,而是应该在游戏运行后,不断的产生。
数组扩容:数组扩容本质上是新创建了数组对象,并不是在原来的数组对象中修改,数组一旦开始确定了容量,大小不可变。
package oo.day04;
import java.lang.reflect.Array;
import java.util.Arrays;
/**
* 数组拷贝的方法:
* 1.Arrays.copyOf --------------------更多是基于源数组扩容或者缩容的情况下使用的。
* 2.System.arraycopy -------------------更多是存在两个数组的情况下 例如将A数组的内容拷贝到B数组时。
*/
public class ArrayCopyDemo {
public static void main(String[] args) {
// //1.Arrays.copyOf
// int[] array = {};
// System.out.println("扩容前的长度" + array.length);//0
// int a = 10;
//
// /**
// * copyOf:
// * 1. 填要扩容的数组
// * 2. 扩容的长度
// * */
// //扩容以后会产生新的扩容后的数组对象 此时需要影响源数组.所以要赋值
// array = Arrays.copyOf(array, array.length + 1);
// array[0] = a;
// System.out.println("扩容后的长度" + array.length);
// System.out.println(array[0]);
//-----------------------------
// int[] arr2 = {5,4,70,50,3};
// System.out.println("扩容前"+arr2.length);
// for (int i = 0; i < arr2.length; i++) {
// System.out.print(arr2[i]+"\t");
// }
// int b = 200;
// arr2 = Arrays.copyOf(arr2,arr2.length+1);
// //arr2.length-1 代表数组最后一个元素
// arr2[arr2.length-1] = b;
// System.out.println("扩容后");
//
// for (int i = 0; i < arr2.length; i++) {
// System.out.print(arr2[i]+"\t");
// }
//2.System.arraycopy
int[] arr1 = {50,80,60,40,10};
int[] arr2 = {1 ,1 ,1 ,1 ,1};
/*
* arraycopy
* 1.拷贝的源数组
* 2.从要拷贝的原数组哪个索引开始拷贝
* 3.要拷贝到的目标数组
* 4.从目标数组的哪个地方开始装
* 5.拷贝过来的数量是多少 长度 (数组长度 - 从源数组拷贝的索引位置)
* * */
System.arraycopy(arr1,0,arr2,0,arr1.length);
System.out.println("arr2完成拷贝");
for (int i = 0; i < arr2.length; i++) {
System.out.println(arr2[i]);
}
}
}
1.间隔1秒每次执行时生成潜艇对象,要在三种潜艇中,随机产生一个对象!
/**
* 生成潜艇的方法 返回值可以写具体潜艇类型吗?不可以!如果写了具体子类型
* 方法则不通用
* 返回值写父类型 SeaObject 实现父类型可以代表不同子类
* */
public SeaObject createSubmarine(){
//产生 0~20的随机数
int type = (int)(Math.random()*20);
if(type <10){//如果生成的随机数在 0 ~ 9
return new ObserverSubmarine();//返回侦察潜艇
} else if(type <15){//如果生成的随机数在 10 ~ 14
return new TorpedoSubmarine();//返回鱼雷潜艇
}
else {
return new MineSubmarine();//返回水雷潜艇
}
}
2.随机产生的这个对象,要先将submarine数组扩容然后存储进去,放到run中调用
/**
* 潜艇入场的方法: 将随机产生的潜艇对象 装到 潜艇数组中
*/
public void submarineEnterAction() {
//1. 调用生成潜艇的方法 返回一个随机潜艇对象 接收
SeaObject obj = createSubmarine();
//2. 为潜艇数组在原有基础上扩1个容量
submarines = Arrays.copyOf(submarines, submarines.length + 1);
//3. 将对象 赋值给数组中下标最后一个空间
submarines[submarines.length-1] = obj;
}
3.paint方法在进行绘制时,submarine数组新增对象了,那么就会多绘制一个对象。
//用来测试的方法
void action() {
Timer timer = new Timer();//创建具体 定时器对象
TimerTask task = new TimerTask() {
@Override
public void run() { //自定义的任务逻辑方法
submarineEnterAction();//调用潜艇入场的方法
repaint();//刷新绘制 每0.01s 执行一次
}
};
//1.具体执行的任务 2.延时多久开始第一次执行(毫秒) 3.执行第一次以后下次执行的间隔时间(毫秒)
timer.schedule(task, 5000, 10);// 0.01s
}
实现雷入场的功能
水雷和鱼雷分别由水雷潜艇和鱼雷潜艇,因为发射的逻辑基本一样.所以可以放到父类中写一个shootThunder方法,供潜艇来进行调用。
/**
* 生成雷的方法 返回值可以写具体雷类型吗?不可以!如果写了具体子类型
* 方法则不通用
* 返回值写父类型 SeaObject 实现父类型可以代表不同子类
* 后续会被水雷潜艇或者鱼雷潜艇调用 侦察潜艇也可能会调用
*/
public SeaObject shootThunder() {
//创建雷对象之前 要先将雷对象的x 和y 坐标 算出来
int x = this.x + this.width;//雷的x坐标
int y = this.y - 5;//雷的y坐标
//instanceof 关键字作用:判断 是否是某个类型的语法
if(this instanceof MineSubmarine){//判断this这个对象 是不是 水雷潜艇
return new Mine(x,y);//返回水雷
}else if(this instanceof TorpedoSubmarine){//判断this这个对象 是不是 鱼雷潜艇
return new Torpedo(x,y);//返回鱼雷
}else {
return null; //如果能走到这一行 说明侦察潜艇调用了发射雷的方法 那么返回null
}
}
水雷和鱼雷也是自动入场的,所以在GameWorld类中写一个雷入场的方法,然后放到run中调用测试。
private int thunderEnterIndex = 0;//控制雷产生的速度
/**
* 控制雷入场的方法 ---放到run中测试
*/
public void thunderEnterAction() {//每0.01s执行
thunderEnterIndex++;
if (thunderEnterIndex % 100 == 0) {//控制每1000毫秒(1s)执行
// 1.for循环 遍历潜艇数组
for (int i = 0; i < submarines.length; i++) {
//2.在for循环体里面 访问潜艇数组的每个对象并调用shootThunder方法 声明一个SeaObject类型接收
SeaObject obj = submarines[i].shootThunder();
if (obj != null) {
// 3.为当前雷数组扩容
thunders = Arrays.copyOf(thunders, thunders.length + 1);
// 4.将接收的雷对象 装到雷数组中的最后一个元素中
thunders[thunders.length - 1] = obj;
}
}
}
}
所有的潜艇都是向右运动 ---------- x += speed;
所有的雷都是想上运动的 ---------- y -= speed;
深水炸弹是向下运动的 --------- y += speed;
当前所有的潜艇、雷、深水炸弹移动行为都是自动,在GameWorld类中写一个移动的方法,stepAction方法 里面遍历当前所有潜艇、雷、深水炸弹数组调用数组的每个对象的step方法. 在run中调用stepAction();
/**
* 所有的潜艇、雷、深水炸弹的移动功能
*/
public void stepAction() {
for (int i = 0; i < submarines.length; i++) {
submarines[i].step();//调用潜艇数组的每个对象的移动方法
}
for (int i = 0; i < thunders.length; i++) {
thunders[i].step();//调用雷数组的每个对象的移动方法
}
for (int i = 0; i < bombs.length; i++) {
bombs[i].step();//调用深水炸弹数组的每个对象的移动方法
}
}
事件:发生了一件事情。 (在餐厅点了一份餐)
事件处理:发生事件以后要做的事情。 (点完餐以后让服务员通知你取餐)
侦听:用来检测要做事件处理的条件有没有达成。(服务员在点餐实时侦听有没有出餐)
import java.awt.event.KeyAdapter; //键盘侦听器
import java.awt.event.KeyEvent; //键盘事件
//实现键盘侦听事件相关逻辑代码
KeyAdapter adapter = new KeyAdapter() { //创建具体侦听对象
@Override
public void keyPressed(KeyEvent e) { //重写实现按下键盘键的事件
//可以通过KeyEvent类直接访问键盘的按键
//可以通过参数e里面提供一个方法 getKeyCode() 来获取用户当下按下的按键是什么
if(e.getKeyCode() == KeyEvent.VK_SPACE){//判断用户按下的按键是不是空格键
//发射深水炸弹
}
}
};
this.addKeyListener(adapter);//将键盘侦听器添加到检测当中
/**
* 当外部的战舰对象调用发射深水炸弹这个方法时,返回一个深水炸弹对象
* 返回值 写深水炸弹类型
* */
public Bomb shootBomb(){
return new Bomb(this.x,this.y);//返回深水炸弹对象 坐标位置是战舰的坐标
}
在GameWord类中做一个深水炸弹入场的方法,并在判断是否按下是空格键的地方调用
/**
* 深水炸弹入场的方法 放在当按下空格键的if内调用
*/
public void bombEnterAction() {
//1.通过战舰对象调用 shootBobm方法 并用深水炸弹类型来接收
Bomb obj = ship.shootBomb();
//2.为bombs数组扩1个容
bombs = Arrays.copyOf(bombs,bombs.length+1);
//3.将接收的对象赋值给bombs数组最后一个元素
bombs[bombs.length-1] = obj;
}
分析:所有的潜艇/雷/深水炸弹 当移出到屏幕外的时候,虽然看不见了,但是实际还存在内存中。
400毫秒 1个潜艇
1秒 2个潜艇 + 2个雷 共4个对象
1分钟 ------240对象
10分钟 ------2400个对象
-----------------------------------------
每10毫秒 移动的方法里要遍历2400个对象 paint方法里 也需要遍历2400个对象 ----------遍历4800个对象
1秒 ----处理......4800000个对象
内存溢出:指的就是内存没有可以在使用的空间了!(报异常错误)
在父类中写一个判断是否越界的方法 后续会被需要被判断是否越界的对象所调用的,也就意味着判断每个对象是否越界 此时方法返回值应该写boolean类型。
/**
* 是否越界的方法:
* 为什么不做抽象方法而做普通方法:
* 因为三种潜艇的判断是否越界的标准是一样的,可以复用
* 其它三个类,深水炸弹,鱼雷,水雷类 不一样,则自行重写实现即可
* */
public boolean isoutBounds(){
return this.x >= GameWorld.WIDTH;//判断潜艇是否超出屏幕的宽
}
深水炸弹:
@Override
public boolean isoutBounds() {
return this.y >= GameWorld.HEIGHT;//如果当前深水炸弹对象的y大于等于窗口的高
}
水雷类:
@Override
public boolean isoutBounds() {
return this.y <= 150 - this.height;//如果水雷的高小于等于水平面-水雷图的高
}
鱼雷类:
@Override
public boolean isoutBounds() {
return this.y <= -this.height;//如果当前鱼雷对象的y 小于等于 负自身图片的高度
}
删除越界对象行为的方法,因为是自动发生的,所有在GameWorld类中写一个outOfBounds方法, 在run。
/**
* 此方法是用来自动判断并删除越界对象的方法 放到run中调用
*/
public void outOfBounds() {
//1.遍历潜艇数组,并在循环里
for (int i = 0; i < submarines.length; i++) {
if (submarines[i].isoutBounds()) {//判断当前潜艇数组的每个对象是否越界
//2.若成立则将当前对象剔除出数组中。
submarines[i] = submarines[submarines.length - 1];//将数组最后的元素赋值给当前越界的元素空间 (替换)
submarines = Arrays.copyOf(submarines, submarines.length - 1);//删除最后一个空间 (缩容)
}
}
//遍历雷数组
for (int i = 0; i < thunders.length; i++) {
if(thunders[i].isoutBounds()){
thunders[i] = thunders[thunders.length-1];
thunders = Arrays.copyOf(thunders,thunders.length-1);
}
}
//遍历深水炸弹数组
for (int i = 0; i < bombs.length; i++) {
if(bombs[i].isoutBounds()){
bombs[i] = bombs[bombs.length-1];
bombs = Arrays.copyOf(bombs,bombs.length-1);
}
}
}
实现战舰的移动
//左移
public void leftMove(){
this.x -= speed;
}
//右移
public void rightMove(){
this.x += speed;
}
//实现键盘侦听事件相关逻辑代码
KeyAdapter adapter = new KeyAdapter() { //创建具体侦听对象
@Override
public void keyPressed(KeyEvent e) { //重写实现按下键盘键的事件
//可以通过KeyEvent类直接访问键盘的按键
//可以通过参数e里面提供一个方法 getKeyCode() 来获取用户当下按下的按键是什么
if (e.getKeyCode() == KeyEvent.VK_SPACE) {//判断用户按下的按键是不是空格键
bombEnterAction(); //发射深水炸弹
}
if(e.getKeyCode() == KeyEvent.VK_LEFT){//判断用户按下的按键是不是左移键
ship.leftMove();//调用战舰对象左移动
}
if(e.getKeyCode() == KeyEvent.VK_RIGHT){判断用户按下的按键是不是右移键
ship.rightMove();//调用战舰对象右移动
}
}
};
this.addKeyListener(adapter);//将键盘侦听器添加到检测当中
接口:
接口是一组行为的规范,接口不关心多个具体实现类之间是否是一种的关系,关注的它们的行为是否达到统一。
一旦实现(子)类 实现了 接口,那么必须要实现该接口中所有的行为。所以往往在设计时候,接口一般只放一个功能。(定义小而精的接口)
3.接口中只能放常量 和抽象方法。接口中不管是常量还是抽象方法 默认访问权限都是公开的!
5.接口是需要被实现类/子类 实现的。一旦实现(子)类 实现了 接口,那么必须要实现该接口中所有的行为。
6.一个类是可以实现多个接口的!实现时需要通过 implements 来实现,在后面写上接口名即可。
若一个子类又继承了一个父类 又想实现接口,子类 extends 父类名 implements 接口名
7.接口与接口之间 是可以继承的!例如:B接口 继承了 A接口,但是 C类 若实现了B接口,那么C同时还要实现A接口中的抽象内容。
class Aoo{ //定义一个 Aoo这个类
}
interface Boo{ //定一个 Boo 接口
}
package oo.day05;
/**
* 接口的测试类:
*/
public class InterfaceDemo {
public static void main(String[] args) {
// Inter1 i1 = new Inter1(); 接口不能被创建对象
}
}
interface Inter1{ //接口 中默认是常量
//接口中不管是常量还是抽象方法 默认访问权限都是公开的!
// int NUM = 10; 默认是常量
static final int NUM = 1;//标准常量写法
// void test(); 默认是抽象方法
abstract void test();
}
class Aoo{ //父类
}
class Boo extends Aoo implements Inter1{// 先继承父类 再实现接口
@Override
public void test() {
}
}
接口与抽象类的区别:
抽象类可以放普通方法也放抽象方法;JDK1.8以前接口中只能放抽象方法。现在其实可以加静态的方法。
抽象类中的成员(变量或方法) 可以加访问修饰符; 接口中修饰符默认都是public。
在项目包创建两个接口:EnemyScore (加分接口) EnemeyLife(加命接口)
package cn.tedu.submarine;
/**
* 加分的接口 :主要提供加分的行为 供实现类去实现加分细节!
* */
public interface EnemyScore {
/**
* 返回值类型 为 int 代表就是若实现类被打倒可以提供对应的分数
* */
int getScore();
}
-------------------------两个实现类:
public class ObserverSubmarine extends SeaObject implements EnemyScore {
@Override
public int getScore() {
return 10;
}
}
----------------
public class TorpedoSubmarine extends SeaObject implements EnemyScore {
@Override
public int getScore() {
return 40;
}
}
加命接口:
package cn.tedu.submarine;
/**
* 加命的接口:主要提供加命的行为 供实现类去实现加命细节!
* */
public interface EnemyLife {
int getLife();//返回值为 命数 具体实现类决定返回多少命。
}
------------------
public class MineSubmarine extends SeaObject implements EnemyLife{
@Override
public int getLife() {
return 1;
}
}
1.将所有(概念统一的)类的共性属性和行为 提取到父类中 (泛化)
2.在父类中 若所有子类的行为具体实现逻辑都一样, 设计为普通方法。
3.接口:(不同的类别或同样的类别)部分存在行为相同,我们可以使用接口来进行优化和复用。
1.编译错误:编译期产生的错误----------全部都是语法错误 (欠练!)
2.运行异常:运行期产生的错误----------空指针异常/数组下标越界异常
FUNDAMENTAL03
多态
程序上的多态 -----------------行为的多态,一个类型下的不同实现
人 p = new 理发师();//向上造型 声明父 new 子
人 p1 = new 医生();
人 p2 = new 园丁();
p.cut();//p在调用的时候,用的人类型的cut ,但是具体运行执行p里面存的对象的cut行为
p1.cut();//p1在调用cut行为,是人类的. 但是具体执行是子对象的。
p2.cut();//p2 调用的是人类的行为,运行则是园丁的cut行为
abstract class 人{
abstract void cut();//剪
}
class 理发师 extends 人 {
void cut(){
System.out.println("剪发");
}
}
class 医生 extends 人 {
void cut(){
System.out.println("做手术");
}
}
class 园丁 extends 人{
void cut(){
System.out.println("剪草");
}
}
我 me = new 我();
me.授课();
me.孝顺父母();
me.互相卷();
--------------------------------
讲师 o1 = new 我();//向上造型
o1.授课();
父母的儿子 o2= new 我();
o2.孝顺父母();
其它老师的同事 o3 = new 我();
o3.互相卷();
interface 讲师{
void 授课();
}
interface 父母的儿子{
void 孝顺父母();
}
interface 其它老师的同事{
void 互相卷();
}
class 我 implement 讲师, 父母的儿子,其它老师的同事{
void 授课(){
...
}
void 孝顺父母(){
...
}
void 互相卷(){
...
}
}
3.能向上造型成功的 : 父 new 子 / 接口 new 实现类
向下转型 / 强制类型转换,可以成功的两个条件满足其一即可:
条件二:要强转的引用类型变量中存的引用 实现了要转换的接口类型,或继承了该类。
我们可以通过 instanceof 关键字来判断,判断当前对象是否符合要强转的类型
Aoo o = new Boo();//向上造型
Boo o1 = (Boo)o;//可以完成强制转换 :符合强转成功的条件一
Inter1 i1 = (Inter1)o;//可以完成强制转换: 符合强转成功的条件二
Coo c1 = (Coo)o;//强制转换失败的异常:classcastException
interface Inter1{
}
class Aoo{//父类
}
class Boo extends Aoo implements Inter1{
}
class Coo extends Aoo{
}
package oo.day05;
/**
* 类型转换测试 Demo
* */
public class ClassCastDemo {
public static void main(String[] args) {
Aoo o = new Boo();//声明父new子 o中存的引用是Boo这个类型对象。
Boo o1 = (Boo)o;//符合条件一:要强转的变量引用 就是这个类型
if(o instanceof InterA){ //判断o对象是否可以转换为 InterA 接口类型
System.out.println("o可以强转InterA接口类型");
InterA i1 = (InterA)o;//符合条件二:要强转的变量引用 实现了该接口类型 或继承了该类
}
if(o instanceof Coo){//判断o对象是否可以转换为 Coo类型
System.out.println("o是Coo这个类型");
Coo c1 = (Coo)o;
}
Coo c1 = (Coo)o;//ClassCastException类型转换异常
// Aoo o = new Boo();
// Coo c1 = (Coo)o;
}
}
interface InterA{
}
class Aoo{
}
class Boo extends Aoo implements InterA{
}
class Coo extends Aoo{
}
因为子类都碰撞检测的行为,提到父类中,且碰撞检测的逻辑都一样,在父类中做一个普通方法。
/*
* 检测碰撞的方法,这个方法由子类对象来进行调用。
*/
public boolean isHit(SeaObject other) {
//this
//other
/**
x1: this的x - other的宽
x2: this的x + this的宽
y1: this的y - other的高
y2: this的y + this的高
*/
int x1 = this.x - other.width;
int x2 = this.x + this.width;
int y1 = this.y - other.height;
int y2 = this.y + this.height;
int x = other.x;
int y = other.y;
//如果传入对象的x 在 x1和x2之间 y在y1和y2之间 则返回为true 代表撞上了。
return (x >= x1 && x <= x2) && (y >= y1 && y <= y2);
}
-----------------------------------------
1)深水炸弹.isHit(潜艇对象); -------------------this指代的是深水炸弹 other指代的是潜艇对象
2)潜艇对象.isHit(深水炸弹); -------------------this指代的是潜艇对象 other指代的是深水炸弹
3)雷.isHit(战舰); -------------------this指代的是雷对象 ohter指代的是战舰对象
4)战舰.isHit(雷); -------------------this指代的是战舰对象 ohter指代的是雷对象
深水炸弹与潜艇的碰撞检测是自动发生的,需要在GameWorld类中写一个方法
bombBangAction,在该方法内部实现逻辑,遍历深水炸弹数组和潜艇数组,让两个数组中的每个对象进行检测。
/**
* 深水炸弹与潜艇碰撞检测的具体使用。 放run中调用。
*/
public void bombBangAction() {
for (int i = 0; i < bombs.length; i++) {//控制轮数
Bomb b = bombs[i];//获取当前深水炸弹对象
for (int j = 0; j < submarines.length; j++) {//次数
SeaObject s = submarines[j];//获取当前潜艇数组中的对象
if(b.isHit(s)){//当前深水炸弹对象调用检测的方法 将 潜艇对象传递进去
System.out.println("碰到了!!!");
}
}
}
}
深水炸弹打到潜艇,深水炸弹消失,潜艇消失。我们可以在SeaObject类中写一个godead方法。哪个对象打点调用godead方法,那个对象则设置为死亡状态。
/**
* 哪个对象打点调用狗带方法,那么就将当前对象设置为si亡状态。
*
* */
public void goDead(){
this.currentState = DEAD;//设置状态为si亡状态。
}
然后再删除对象的代码中再加一个判断条件,当前对象若死亡,也需要删除。
/**
* 此方法是用来自动判断并删除越界对象的方法 放到run中调用
*/
public void outOfBounds() {
//1.遍历潜艇数组,并在循环里
for (int i = 0; i < submarines.length; i++) {
if (submarines[i].isoutBounds()||submarines[i].isDead() ) {//判断当前潜艇数组的每个对象是否越界或者是否死亡
//2.若成立则将当前对象剔除出数组中。
submarines[i] = submarines[submarines.length - 1];//将数组最后的元素赋值给当前越界的元素空间 (替换)
submarines = Arrays.copyOf(submarines, submarines.length - 1);//删除最后一个空间 (缩容)
}
}
//遍历雷数组
for (int i = 0; i < thunders.length; i++) {
if (thunders[i].isoutBounds()||thunders[i].isDead()) {
thunders[i] = thunders[thunders.length - 1];
thunders = Arrays.copyOf(thunders, thunders.length - 1);
}
}
//遍历深水炸弹数组
for (int i = 0; i < bombs.length; i++) {
if (bombs[i].isoutBounds() || bombs[i].isDead()) {
bombs[i] = bombs[bombs.length - 1];
bombs = Arrays.copyOf(bombs, bombs.length - 1);
}
}
}
在GameWorld类中定义一个方法,方法名thunderBangAction 。放到run中调用。
/**
* 雷与战舰检测的逻辑 放 run中调用。
* */
public void thunderBangAction(){
for (int i = 0; i < thunders.length; i++) {
SeaObject t = thunders[i];//当前雷对象
if(t.isLive() && t.isHit(ship)){//如果雷对象是活着的状态并且 雷对象与战舰碰到的话
t.goDead();//当前雷对象消失
//战舰减命
ship.subtractLife();
}
}
}
int score = 0;//默认游戏分数为 0
/**
* 深水炸弹与潜艇碰撞检测的具体使用。 放run中调用。
*/
public void bombBangAction() {
for (int i = 0; i < bombs.length; i++) {//控制轮数
Bomb b = bombs[i];//获取当前深水炸弹对象
for (int j = 0; j < submarines.length; j++) {//次数
SeaObject s = submarines[j];//获取当前潜艇数组中的对象
//如果当前深水炸弹对象是活的 并且 当前潜艇对象也是活的 最后再去调用互相检测
if (b.isLive() && s.isLive() && b.isHit(s)) {//当前深水炸弹对象调用检测的方法 将 潜艇对象传递进去
b.goDead();
s.goDead();
//加分 加命的方法
//判断s是不是 侦察潜艇类型
if(s instanceof ObserverSubmarine){//如果s对象是侦察潜艇的话
//则强转侦察潜艇类型 因为 s是SeaObject类型 不强转则无法调用getScore方法
ObserverSubmarine os =(ObserverSubmarine) s;
score += os.getScore();
} else if(s instanceof TorpedoSubmarine){//如果s对象是鱼雷潜艇的话
TorpedoSubmarine ts =(TorpedoSubmarine) s;
score += ts.getScore();
}
}
}
}
}
//绘制命数
g.drawString("Life:" + ship.getLife(), 400, 50);
//绘制分数
g.drawString("Score:"+score,200,50);
在深水炸弹与潜艇碰撞具体逻辑里,不能判断具体的类型来实现加分或加命的行为,因为后续可能会新增新的潜艇,代码不易维护。
以下代码的问题:
复用性差 扩展性差 维护性差 (代码要遵循开闭原则 对扩展开放,对修改关闭)
加分 加命的方法
判断s是不是 侦察潜艇类型
if (s instanceof ObserverSubmarine) {//如果s对象是侦察潜艇的话
//则强转侦察潜艇类型 因为 s是SeaObject类型 不强转则无法调用getScore方法
ObserverSubmarine os = (ObserverSubmarine) s;
score += os.getScore();
} else if (s instanceof TorpedoSubmarine) {//如果s对象是鱼雷潜艇的话
TorpedoSubmarine ts = (TorpedoSubmarine) s;
score += ts.getScore();
} else if (s instanceof MineSubmarine) {
MineSubmarine ms = (MineSubmarine) s;
ship.setLife(ms.getLife());
}else if(s instanceof 以后的潜艇类型?){
???
}
应修改为判断当前潜艇对象是否实现了加分或加命的接口,然后再去转换实现。
if(s instanceof EnemyScore){//判断当前s这个潜艇对象 有没有实现EnemyScore接口
EnemyScore addScore= (EnemyScore)s;//直接把s强转成EnemyScore接口类型
//编译期间调父 运行时执行子。
score += addScore.getScore();//具体运行时使用的是侦察潜艇还是鱼雷潜艇 要看 地址指向的是哪个对象
} else if(s instanceof EnemyLife ){//判断当前s这个潜艇对象 有没有实现EnemyLife接口
EnemyLife addLife = (EnemyLife)s;
ship.setLife(addLife.getLife() );
}
游戏状态
在GameWorld类中需要定义三个状态,做三个常量 还有一变量表示当前游戏
public static final int START = 0;//开始状态
public static final int RUNNING = 1;//运行状态
public static final int GAME_OVER=2;//结束状态
private int currentState = START;//默认是开始状态
1.开始状态(Start):右键运行后呈现的界面,绘制一张开始界面的图片。当按下空格键则切换为运行状态。
if (e.getKeyCode() == KeyEvent.VK_SPACE) {//判断用户按下的按键是不是空格键
if (currentState == START) { //当前状态是不是开始状态
currentState = RUNNING;//切换当前状态为运行状态
} else {//否则
bombEnterAction(); //发射深水炸弹
}
}
if (currentState == RUNNING) { //如果当前状态是运行状态
submarineEnterAction();//调用潜艇入场的方法
thunderEnterAction();//调用雷入场的方法
stepAction();//调用移动的方法
outOfBounds();//调用删除优化对象的方法
bombBangAction();//调用深水炸弹与潜艇检测碰撞的方法
thunderBangAction();//调用雷与战舰的检测碰撞的方法
// System.out.println(submarines.length + "当前潜艇在内存中的数量" + ",雷在内存中的数量" + thunders.length);
repaint();//刷新绘制 每0.01s 执行一次
}
3.结束状态(GameOver):当战舰的命数为0时,切换为结束状态。
/**
* 检测游戏是否结束的方法 ----实时检测 放 run中调用
*/
public void checkGameOverAction() {
if (ship.getLife() <= 0) {//如果战舰命数小于等于 0
currentState = GAME_OVER;//切换状态为 游戏结束状态
}
}
@Override //g 可以理解为画笔
public void paint(Graphics g) { //JPanle 提供了绘制图片的方法
// 1.填null 2.填g 3.0 4.0
if (currentState == START) { //当前状态如果是开始状态
ImageResources.start.paintIcon(null, g, 0, 0);
} else if (currentState == RUNNING) {//当前状态如果是运行状态
ImageResources.sea.paintIcon(null, g, 0, 0);
ship.paintImage(g);//绘制战舰
for (int i = 0; i < submarines.length; i++) {
submarines[i].paintImage(g); //绘制潜艇数组中所有对象
}
for (int i = 0; i < thunders.length; i++) {
thunders[i].paintImage(g);//绘制雷数组中 所有的对象
}
for (int i = 0; i < bombs.length; i++) {
bombs[i].paintImage(g);//绘制深水炸弹数组中的所有对象
}
//绘制命数
g.drawString("Life:" + ship.getLife(), 400, 50);
//绘制分数
g.drawString("Score:" + score, 200, 50);
} else if (currentState == GAME_OVER) {
ImageResources.gameover.paintIcon(null, g, 0, 0);
}
}
2.上一次游戏结束时,所有用的数组中存在对象吗??若存在如何保证重新开始时 所有用到的数组重置 ?
基础知识的掌握:
2.数据类型 整数 : int long 小数:double 字符类型:char 布尔: boolean
多路分支: if(){} else if() {}else if() {}......
面向对象:
有三大特征
封装:
所有的学生对象都有 属性:姓名 年龄 学号 行为: 学习的行为
方法的封装:封装某个功能逻辑,达到复用(一次定义,多次使用)
属性的封装:属性私有化,方法公开化 (目的:保护程序(属性)防止任意地方修 改!)
继承:好处:实现代码的复用,
坏处:耦合性高!(父类只放子类共有的属性和行为)
接口:如果多个类别之间 仅仅只是行为一致!我们推荐使用接口来去当代表。
多态:在一个类别下 的不同实现
API (Application Programing Interface)应用程序接口(功能),我们讲解Java中最常用的工具(功能)。
什么是API:API表示的就是功能,我们可以通过学习API 快速的进行我们编程思路的开发。
API的实现初衷:设计思路就是将复杂的业务逻辑 封装成功能,供调用者更好的使用。对于使用者而言,不需要关注功能内部的实现细节,只需要知道如何使用即可。
1.java.lang包:java语言的核心语法,内部封装了java最核心的API.所以一般在使用lang包下的内容的时候,不需要导包。
例如:String 、 Object 、System 、Integer..................
String (字符串)
1.java.lang.String这个类用final来修饰的,不能被继承。
2.String类中维护了一个不可变(用final修饰)的char数组!
3.字符串一旦创建,对象的内容是不可变,若重新为字符串的变量赋值,则表示一个新对象。
String name = "陈晓东";//声明一个String类型的变量 name里面存 了"陈晓东"字符串对象 //推荐的形式
String name1 = new String("陈晓东");//但是这种方式Java不推荐
char[] chs ={'陈','晓','东'};
上述代码 name 和 chs 在内存引用的内容是同一个。
String一旦被创建则不可变,原因在于String非常的常用,这么设计就是为了优化内存,在堆中的区域存在一个常量池,池子里存放的是已被创建过的字符串地址。
String s1 = "ABC";
String s2 = "ABC";
System.out.println(s1 == s2);
String可以复用的条件,String创建对象的形式分为两种:
1.以字符串字面量的形式创建:例如 "ABC" 、 "hello world".....
public static final String S3 = "ABC"; //字符串常量 可以进行常量池复用
public static void main(String[] args) {
// String s1 = "ABC";//使用字面量 创建了一个String 对象
// String s2 = s1;
// s1 = s1 + "DEF";
// System.out.println(s1);
// System.out.println(s2);
//可以复用的情况测试:
String s1 = "ABC";
String s2 = "ABC";
String s4 = "A" + "BC"; //字符串字面量拼接 可以复用
System.out.println(s1 == s2);
System.out.println(S3 == s1);
System.out.println(s1 == s4);
1.使用new关键字创建的字符串 : String s = new String("ABC");
//不可复用的情况测试:
String s5 =new String("ABC"); //不推荐
String s6 = new String(new char[]{'A','B','C'}); //不推荐
System.out.println(s5 == s1);
String s7 = "A";
String s8 = s7 + "BC";
System.out.println(s8 == s1);
}
String str = new String("ABC");
如果问的是创建多少个对象,答:两个String对象 一个char数组对象。
StringAPI
String类中,提供了一些方法(API),可以获取字符串的长度,获取字符串中某个下标的字符,转换大写,转换小写....
//1.获取字符串的长度 和通过下标获取字符串中对应的字符
String str = "Thinking in java是一本好书";
int length = str.length();// 获取字符串长度
System.out.println(length);
char c = str.charAt(20);// 通过下标获取字符串中对应的字符
System.out.println(c);
/**
* 面试题:请写一段程序,测试出 "Thinking in java是一本好书" 里面 字符i存在的次数
* */
int count = 0;
for (int i = 0; i < str.length(); i++) {
if(str.charAt(i) == 'i'){//判断当前字符是否 等于 字符 i
count++;//计数
}
}
System.out.println("i存在的次数:"+count);
2. indexof() 从头向后 检索字符或字符串 在当前这个字符串中的位置
lastIndexof() 从后向前 检索字符或字符串 在当前这个字符串中的位置
//2. indexof 从头向后开始检索
String str = "Thinking in java是一本好书";
int index = str.indexOf('i');//通过传入的字符 返回字符在字符串中存在的下标
System.out.println(index);
int index1 = str.indexOf('x');//如果找不到 则返回-1
System.out.println(index1);
int index2 = str.indexOf('i', 3);//可以指定起始检索位置向后检索字符
System.out.println(index2);//5
int index3 = str.indexOf("java");
System.out.println(index3);
//lastIndexOf 从后往前开始检索 无非检索的方向发生的变化 其它的功能使用与index是一致的。
int index4 = str.lastIndexOf('i');
System.out.println(index4);
//3.转换大小写
String str = "Thinking in java是一本好书";
str = str.toUpperCase();//字符串字母内容转大写
System.out.println(str);
str = str.toLowerCase();//字符串字母内容转小写
System.out.println(str);
//4.去除两端空白
String name = " Tom";
System.out.println(name);
name = name.trim();//去除字符串两端空白
System.out.println(name);
//5.是否是以什么开头 或 什么结尾的方法
boolean r = name.startsWith("T");//判断是否是以xx开头的字符串内容
System.out.println(r);
boolean r1 = name.endsWith("m");//判断是否是以xx结尾的字符串内容
System.out.println(r1);
String str = "bjzhangpeng@tedu.cn";
// String user = str.substring(7);//截取字符串,填入截取起始下标位置,从此位置向后全部截取
int endIndex = str.indexOf('@');
String user = str.substring(0,endIndex);//截取字符串 不包括结束索引的位置(包头不包尾)
System.out.println(user);
//截取字符串的用法:
//1.substring(开始索引,结束索引); 会截取出区域的字符串内容.
//2.subString(开始索引,开始索引+要截取的长度);
//为什么不包含结束位置,其实就是为了方便以后按照长度进行截取.
String test = str.substring(0,0+5);
System.out.println(test);
//5.valueof();这是String类提供的静态方法,主要的作用就是可以将数值类型的变量转换为字符串
int a = 100;
String b = String.valueOf(a);
System.out.println(b);//"100"
System.out.println(100);//打印语句中放的任意的值类型数据 其实都是做了String.valueof的处理来显示的.
//6.contains 判断是否包含XX内容的方法使用
String c = "AOP";
boolean r = c.contains("A");//判断传入的字符串内容是否存在当前这个变量c中
System.out.println(r);
String 不适合频繁修改,一旦发生修改,就是新创建对象,若频繁的使用String修改,对内存的消耗非常大!容易出现异常,java.lang.OutOfMemoryError 内存溢出。
StringBuilder
解决当频繁的对字符串进行修改时,可以使用Java提供的StringBuilder
因为StringBuilder内部维护了可变的char数组,修改性能小,提供很多字符串修改相关的操作。
//增
String str = "好好学习Java!";
StringBuilder builder = new StringBuilder(str);//创建对象并传入要处理的字符串对象
//append 字符串追加
builder.append("为了找份好工作");
System.out.println(builder);
//改
//好好学习Java!为了找份好工作 修改为好好学习Java!为了改变世界
//replace 替换 1. 替换开始索引 2.结束的索引(包头不包尾) 3.替换的内容
builder.replace(9, 16, "为了改变世界");
System.out.println(builder);
//删
//delete 1.从哪里开始索引删除 2.到哪里结束索引(包头不包尾)
builder.delete(0,9);
System.out.println(builder);
//插
//insert 1.从哪里开始插入(索引) 2.插入的内容
builder.insert(0,"活着");
System.out.println(builder);
//字符串内容反转
//reverse反转
builder.reverse();
System.out.println(builder);
str = builder.toString();//将处理好的字符串对象 再赋值给 str
System.out.println(str);
正则表达式
正则表达式可以用来检测格式是否正确的功能,例如:邮箱的格式,手机号,身份证号格式...都可以用正则来进行约束用户输入的数据。
[a,b,c] 表示可以写任意一个 a 或 b 或 c 其中一个字符。
[^abc] 表示的是除了a 或 b 或 c 之外的任意一个字符串。
[a-z] 表示的意思是可以任意写一个 26个小写字母其中一个字符
[a-zA-Z0-9] 表示的是可以写 任意一个26个小写字母或26个大写字母或0-9的数字。
[a-z&&[^bc]] 表示的是 26个小写字母中,除了bc 不可以写,其它都可以写
\w 表示单个词的字符 -----> [a-zA-Z0-9]
? 代表可以写 0 - 1次
例如:[abc]? 表示可以写一个 a 或 b 或 c,不能写其它的字符内容。
+ 代表至少出现1次以上 没有上限
例如:[abc]+ 表示至少写一个a 或 b 或 c内容,可以出现多次,但不能写其它的字符内容。
* 代表任意次,一次不写也可以
---------------------------
限制次数
[abc]{3} 代表写3个字符
[abc]{3,5} 代表至少写3个 最多写5个
[abc]{3,} 代表至少写3次 没有上限
(abc){3} 表示:可以视为abc为一组 至少出现3组的意思
(abc|def){3} 表示:可以视为abc 或 def 为一组 至少出现3组的意思
// String email = "bjzhangpeng@tedu.com.cn";
// String regex = "[a-zA-Z0-9_]+@[a-zA-Z0-9]+(\\.[a-zA-Z]+)+";
// boolean match = email.matches(regex);//判断传入的正则表达式的格式 是否与 当前字符串对象中的邮箱格式匹配
// if(match){
// System.out.println("是邮箱");
// }else {
// System.out.println("不是邮箱!");
// }
String str = "abc123def456ghi";
//0-9的数字出现一个或多个的地方来进行切割
String[] data = str.split("[0-9]+");//传入正则表达式 根据正则的格式来进行切割
for (int i = 0; i < data.length; i++) {
// System.out.println(date[i]);
}
String str1 = "a,b,c,d,e,f";
String[] data1 = str1.split(",");//也可以传入字符串的内容 根据具体的内容来进行切割
for (int i = 0; i < data1.length; i++) {
System.out.println(data1[i]);
}
String regex = "(sb|tmd|nc|wr|qnmd|dsb)";
String message= "qnmd!你个dsb,你怎么这么nc!";
//1.传入需要匹配的正则格式
// 2.若匹配时所替换的内容
message = message.replaceAll(regex,"小可爱");
System.out.println(message);
// message = message.replaceAll(regex, "小可爱");
// System.out.println(message);
String str2 = "abc12de3f456";
str2 = str2.replaceAll("[0-9]+", "NUMBER");
// System.out.println(str2);
//replaceAll 支持正则的替换功能 , 也支持第一个参数填入具体的内容,匹配则全部替换。
//replace 不支持正则的替换功能, 只要被替换的内容存在,则全部替换。
String str3 ="abc123def123";
//replace 方法 1.需要被替换的内容
//2. 实际替换的内容
str3 = str3.replace("123","456");
System.out.println(str3);
Object
为什么需要Object类,因为Object类承载了所有类的共性内容,为Java提供一系列的基础的结构支撑,是所有的类的(父类/基类)。