Java学习笔记
Day01
javac 生成的字节码文件是类名;
java + 类名 (不加.java);
/*HelloWorld程序*/ /** @author SunWenhao @version v1.0 第一个java程序 */ public class HelloJava { public static void main(String[ ] args){ System.out.println("Hello,world!"); } }注释 1.增强可读性,对文档中的程序进行说明;2.调试代码
单行注释://
多行注释:/* */ 不能嵌套使用
文档注释(Java特有):/** */
格式:javadoc -d myHello -author -version HelloJava.java
总结:java程序编写-编译-运行;
编写:将编写的java代码保存在以“.java”结尾的源文件中;
在一个源文件中可以声明多个class,但是只能最多有一个类声明为public;
public只能加到和文件名一样的类上(public类名和文件名一样);
输出语句:
System.out.println(“HelloWorld!”): //换行
-->HelloWorld!
HelloWorld!
System.out.print(“HelloWorld!”): //不换行
-->HelloWorld!HelloWorld!
程序的入口是main方法,格式固定(args可以变);
每个执行语句末尾都有“;”(程序末尾或是{ },或是;)
public static void main(String[] args) public static void main(String[] a[])
编译:使用javac.exe编译java源文件,javac 源文件名.java;
编译后,会生成一个或多个字节码文件,字节码文件名与类名一致(有几个 类就有几个字节码文件);
运行:使用java.exe解释运行字节码文件,java 类名;
运行只能运行包含main方法的类。
Day03
运算符:
算术运算符: + - * / % ++ -- + (连接)
class AriTest{
public static void main(String[] args) {
int num1 = 12;
int num2 = 5;
int result1 = num1 / num2;
System.out.println(result1);//2 小数点后省略掉
int result2 =num1 / num2 * num2;
System.out.println(result2);//10 按照顺数算
double result3 = num1 / num2;//将2赋值给double
System.out.println(result3);//2.0
double result4 = num1 / num2 + 0.0;//2.0
System.out.println(result4);
double result5 = num1 / (num2 + 0.0);//2.4
System.out.println(result5);
double result6 = (double)num1 /num2;
System.out.println(result6);
// % 取模运算 得余数
//最终结果的符号和被模数是一样的
//使用%判断能否被除尽
int m1 = 12;
int n1 = 5;
System.out.println("m1 % n1 = " + m1 % n1);
int m2 = -12;
int n2 = 5;
System.out.println("m2 % n2 = " + m2 % n2);
int m3 = 12;
int n3 = -5;
System.out.println("m3 % n3 = " + m3 % n3);
int m4 = -12;
int n4 = -5;
System.out.println("m4 % n4 = " + m4 % n4);
//(前)++ 先赋值再运算
//(后)++ 运行后再赋值
int a1 = 10;
int b1 = ++a1;
System.out.println("a1 = " + a1 +",b1 = "+ b1);
int a2 = 10;
int b2 = a2++;
System.out.println("a2 = " + a2 +",b2 = "+ b2);
//注意:
short s1 = 10;
//s1 = s1 + 1; 编译失败
//s1 = (short)(s1 + 1); 可以
s1++;//自加不会改变变量的数据类型
System.out.println("s1 = " + s1);
byte bb1 =127;
bb1++;
System.out.println("bb1 = " + bb1);
//(前)--
//(后)--
int a4 = 10;
int b4 = --a4;
System.out.println("a4 = " + a4 +",b4 = "+ b4);
int a5 = 10;
int b5 = a5--;
System.out.println("a5 = " + a5 +",b5 = "+ b5);
//输出153各位数字
int num = 153;
int bai = num /100;
int shi = num % 100 / 10; // int shi = num / 10 % 10;
int ge = num % 10;
System.out.println(num + "百位是" + bai);
System.out.println(num + "十位是" + shi);
System.out.println(num + "个位是" + ge);
}
} 赋值运算符:= += -= *= /= %=
开发中,如果实现+2的操作 int num = 10;
方式一:num = num + 2;
方式二:num+ = 2;(推荐:不会改变数据类型)
开发中,如果实现+1的操作 int num = 10;
方式一:num = num + 1;
方式二:num+ = 1;
方式三:num++;(推荐)
class SetValueTest{
public static void main(String[] args) {
int i1 = 10;
int j1 =10;
int i2,j2;//连续赋值
i2 = j2 =10;
int i3 =10,j3 =10;
int num1 = 10;
num1 += 2;// num1 = num1 + 2
System.out.println(num1); // 12
int num2 = 12;
num2 %= 5;//num2= num2 % 5;
System.out.println(num2);
short s1 = 10;
s1 + = 2;//不会改变s1的数据类型
System.out.println(s1);
}
} 比较运算符: == != < > <= >= instanceof
比较运算符的结果都是布尔型
class ComepareTest{
public static void main(String[] args) {
boolean b1 = true;
boolean b2 = false;
System.out.println(b2 == b1);
System.out.println(b2 = b1);
}
} 逻辑运算符:
逻辑运算符操作的是布尔型变量
&—逻辑与 |—逻辑或 !—逻辑非
&&—短路与 ||—短路或 ^—逻辑异或
优先用&&和||
//区分&和&&
//不管是&和&&,运算结果相同,当符号左边是true时,二者都会执行符号右边运算
//当符号左边是false时,&继续执行右边运算,&&则不执行右边运算,同理|和||
class LogicTest{
public static void main(String[] args) {
//区分& 与 &&
boolean b1 = false;
int num1 = 10;
if (b1 & (num1++ > 0)) {
System.out.println("我现在在北京");
} else {
System.out.println("我现在在南京");
}
System.out.println("num1 = " + num1);//11
boolean b2= false;
int num2 = 10;
if (b2 && (num2++ > 0)) {
System.out.println("我现在在北京");
} else {
System.out.println("我现在在南京");
}
System.out.println("num2 = " + num2);//10
boolean b3= true;
int num3 = 10;
if (b3 | (num3++ > 0)) {
System.out.println("我现在在北京");
} else {
System.out.println("我现在在南京");
}
System.out.println("num3 = " + num3);//11
boolean b4= true;
int num4 = 10;
if (b4 || (num4++ > 0)) {
System.out.println("我现在在北京");
} else {
System.out.println("我现在在南京");
}
System.out.println("num4 = " + num4);//10
}
} 位运算符:
<<左移 >>右移 >>>无符号右移 &与运算 |或运算 ^异或运算 ~取反运算
位操作符都是整型的数据
<< :在一定范围内,每向左移一位,都相当于*2
>> :在一定范围内,每向右移一位,都相当于/2
>>> : 用0补二进制最高位
class BitTest{
public static void main(String[] args) {
int i = 21;
System.out.println("i << 2:" + (i << 2));//每左移一位都乘2的几次幂(左移过多可能成负数)
System.out.println("i << 3:" + (i << 3));
int m =12;
int n = 5;
System.out.println("m & n :" + (m & n));
System.out.println("m | n :" + (m | n));
System.out.println("m ^ n :" + (m ^ n));
//交换两个变量数值
int num1 = 10;
int num2 = 20;
//方式一:
int temp = 0;
temp = num2;
num2 = num1;
num1 = temp;
System.out.println("num1 : " + num1);
System.out.println("num2 : " + num2);
num1 = 10;
num2 = 20;
//方式二:
num1 = num1 + num2;
num2 = num1 - num2;
num1 = num1 - num2;
System.out.println("num1 : " + num1);
System.out.println("num2 : " + num2);
num1 = 10;
num2 = 20;
//方式三:
num1 = num1 ^ num2;
num2 = num1 ^ num2;
num1 = num1 ^ num2;
System.out.println("num1 : " + num1);
System.out.println("num2 : " + num2);
}
} 三元运算符:
凡是可以使用三元运算符的地方,都可以使用if-else语句,反之不一定
尽量使用三元运算符
(条件表达式)?表达式1:表达式2;
//(条件表达式)?表达式1:表达式2;
//条件表达式结果为布尔型,如果条件表达式为true执行表达式1,如果条件表达式为false则执行表达式2
class SanYuanTest{
public static void main(String[] args) {
//获取两个整数较大值
int m = 12;
int n = 5;
int max = (m > n) ? m : n;
System.out.println(max);
double max_2 = (m > n) ? 2 : 1.0;//可以统一成一个类型
System.out.println(max_2);
String max_3 = (m > n) ? "m大" : "n大";
System.out.println(max_3);
String maxStr = (m > n) ? "m大" : ((m == n) ? "m和n相等" : "n大" );//三元运算符可以嵌套使用
//获取三个数的最大值
int n1 = 12;
int n2 = 30;
int n3 = -43;
int max1 = (n1 > n2)? n1 : n2;
int max2 = (max1 > n3)? max1 : n3;
System.out.println("三个数中最大值为: " + max2);
}
} 运算符优先级:套括号
Day04
流程控制:
三种基本流程结构:
顺序结构
分支结构
循环结构
分支结构:
- if-else
- switch-case
if-else结构:
- if
- if ... else (二选一)
- if ... else if ...else (多选一)
/*
第一种:
if (条件表达式){
执行表达式
}
第二种:
if (条件表达式){
执行表达式1
}else{
执行表达式2
}
第三种:
if (条件表达式1){
执行表达式1
}else if (条件表达式2){
执行表达式2
}else if (条件表达式3){
执行表达式3
}
...
else
*/
class IfTest{
public static void main(String[] args) {
//举例一
int heartBeats = 179;
if (heartBeats < 60 || heartBeats > 100) {
System.out.println("需要进一步检查");
}
System.out.println("检查结束");
//举例二
int age = 23;
if (age < 18) {
System.out.println("可以看动画片");
} else {
System.out.println("可以看电影");
}
//举例三
if (age < 0) {
System.out.println("输入无效");
}else if (age < 18){
System.out.println("青少年时期");
}else if (age < 35){
System.out.println("青壮年时期");
}else if (age < 60){
System.out.println("中年时期");
}else if (age < 120){
System.out.println("老年时期");
}else{
System.out.println("成仙");
}
}
} 接受从键盘输入
/*
从键盘获取不同类型的变量:使用Scanner类
1.导包:import java.util.Scanner;
2.Scanner实例化
3.调用Scanner类的相关方法,获取指定类型的变量
注意:需要根据相关方法输入指定数据类型,如果输入的数据类型不匹配,会出现异常
*/
import java.util.Scanner;
class ScannerTest{
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);//实例化
int num = scan.nextInt();
System.out.println("获取的数字是" + num);
System.out.println("输入你的名字:");
String name = scan.next();
System.out.println("你好," + name);
System.out.println("输入你的体重:");
double weight = scan.nextDouble();
System.out.println("您的体重是:" + weight);
System.out.println("今天是否学习Java?");
boolean study = scan.nextBoolean();
System.out.println(study);
//Scanner没有提供char型的获取
System.out.println("输入性别:");
String gender = scan.next();
char genderChar = gender.charAt(0);
System.out.println(genderChar);
}
} 练习1:
/*
1.else if中,else结构是可选的;
2.针对多个条件表达式:
如果多个条件表达式之间是互斥关系(或没有交集的关系),哪个判断或者执行语句顺序无所谓;
如果多个条件表达式之间有交集的关系,需要根据实际情况考虑清楚将哪个结构声明在上面;
如果多个条件表达式之间有包含的关系,将小的范围写在上面
*/
import java.util.Scanner;
class ifExcer{
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.println("输入小明的成绩:");
int score = scan.nextInt();
if (score == 100){
System.out.println("奖励BMW");
}else if(score > 80 && score <= 99){
System.out.println("奖励iPhone");
}else if(score > 60 && score <= 80){
System.out.println("奖励iPad");
}else{
System.out.println("没有奖励");
}
}
} 练习2:
随机数:Math.random()
/*
1.if-else可以嵌套使用(超过三层时就要注意);
2.如果if-else中执行语句只有一行,大括号可以省略,但是不建议使用
*/
import java.util.Scanner;
class ifExcer2{
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.println("请输入第一个整数:");
int num1 = scan.nextInt();
System.out.println("请输入第二个整数:");
int num2 = scan.nextInt();
System.out.println("请输入第三个整数:");
int num3 = scan.nextInt();
if (num1 > num2){
if (num3 >= num1)
System.out.println(num2 + "," + num1 + "," + num3);
else if (num3 <= num2)
System.out.println(num3 + "," + num2 + "," + num1);
else
System.out.println(num2 + "," + num3 + "," + num1);
}else {
if (num3 >= num2)
System.out.println(num1 + "," + num2 + "," + num3);
else if (num3 <= num1)
System.out.println(num3 + "," + num1 + "," + num2);
else
System.out.println(num1 + "," + num3 + "," + num2);
}
//测算狗的年龄
//int dogAge = 6;
System.out.println("输入狗的年龄");
int dogAge = scan.nextInt();
if (dogAge >= 0 && dogAge <= 2) {
System.out.println("相当于人的年龄:" + dogAge * 10.5);
}else if (dogAge > 2) {
System.out.println("相当于人的年龄:" + ( 2 * 10.5 + (dogAge - 2) * 4));
}else{
System.err.println("输入错误!");
//随机数产生:10-99
double value = (int)(Math.random() * 90 + 10);//[0.0 , 90.0)-->[10.0 , 100.0)-->[10 , 99]
//公式:[a , b] : (int)(Math.random() * (b - a + 1) + a)
System.out.println(value);
}
}
} switch-case分支结构:
根据switch表达式中的值,依次匹配各个case中的常量,一旦匹配成功,就进入相应的case中执行,当调用完执行语句后,仍然向下执行其他case结构中的语句,直到遇到break关键字,或末尾为止;
switch结构中的表达式只能是如下6种数据类型之一:(不能有浮点型和布尔型)
byte、short、char、int、枚举类型(JDK 5.0新增)、String(JDK 7.0新增);
case只能声明常量,不能声明范围;
default相当于if-else中的else,都不满足时才执行,default结构也是可选的,位置是灵活的(不一定写在末尾)
/*
switch-case分支结构:
switch(表达式){
case 常量1:
语句1;
//break;
case 常量2:
语句2;
//break;
... ...
default:(类似于else)
语句;
//break;
}
*/
class SwitchCaseTest{
public static void main(String[] args) {
int number = 2;
switch (number) {
case 0:
System.out.println("zero");
break;
case 1:
System.out.println("one");
break;
case 2:
System.out.println("two");
break;
case 3:
System.out.println("three");
break;
default:
System.out.println("没有匹配");
break;
}
}
} 练习:
/*
如果执行语句中重复,可以合并(实际上是没有break继续执行)
*/
class SwitchExer{
public static void main(String[] args) {
int score = 78;
switch (score / 10) { //除以60更好
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
System.out.println("不及格");
break;
case 6:
case 7:
case 8:
case 9:
case 10:
System.out.println("及格");
break;
}
}
} 循环结构:
- for循环;
- while循环;
- do-while循环
循环四个部分:
- 初始化部分;
- 循环条件部分;
- 循环体;
- 迭代条件
for (初始化;循环条件;迭代条件) {
循环体
}
/*
for循环:
- 初始化部分;
- 循环条件;(布尔型)
- 循环体;
- 迭代条件
for(初始化;循环条件;迭代条件){
循环体
}
*/
//输出500个HelloWorld!
class ForExer{
public static void main(String[] args) {
for(int i = 1;i <= 500;i++){//这个i只作用于这个for循环作用域内,下一个循环中仍然可以定义i
System.out.println("HelloWorld");
}
//遍历100以内的偶数,输出所有偶数的和,记录偶数个数
int count = 0;
int sum = 0;
for(int i = 1;i <= 100;i++){
if (i % 2 == 0){
System.out.println(i);
sum += i;
count ++;
}
}
System.out.println("总和为:" + sum);
System.out.println("偶数个数为:" + count);
}
} 练习:
不在循环条件限制次数的结构:for( ; ; )、while(true);
结束无限循环的方式:
- 循环条件部分返回false
- 循环体中,执行break
/*
从键盘读入个数不确定的整数,并判断读入的正数和负数的个数,输入为0时结束程序
*/
import java.util.Scanner;
class ForWhileTest{
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int positiveNumber = 0;
int negativeNumber = 0;
while (true) {
int number = scan.nextInt();
if (number > 0) {
positiveNumber++;
}else if (number < 0) {
negativeNumber++;
}else{
break;
}
}
System.out.println("正数个数为:" + positiveNumber);
System.out.println("负数个数为:" + negativeNumber);
}
} 嵌套循环:
将一个循环结构A声明在另一个循环结构B中,就构成了嵌套循环
外层循环:循环结构B
内层循环:循环结构A
练习:
/*
输出:
******
******
******
******
说明:
内层循环遍历一遍,相当于外层循环遍历一次(外层控制行数,内层控制列数)
假设外层循环执行m次,内层循环需要执行n次,此时内层循环的循环体一共执行m*n次
*/
for (int j = 1; j <= 4; j++) {
for (int i = 1;i <= 6;i++){
System.out.print('*');
}
System.out.println();
} 练习:
/*
输出:
*
**
***
****
*****
*/
for (int i = 1; i <= 5; i++) {
for (int j = 1; j <= i; j++) {
System.out.print('*');
}
System.out.println();
} 练习:
/*
输出:
*****
****
***
**
*
*/
for (int i = 1; i <= 5; i++) {
for (int j = 1; j <= 5 - i; j++) {
System.out.print('*');
}
System.out.println();
} 练习(99乘法表):
/*
1*1=1
2*1=2 2*2=4
... ...
9*1=9 ... ... 9*9=81
*/
class NineNineTable{
public static void main(String[] args) {
for (int i = 1; i < 9; i++) {
for (int j = 1; j <= i; j++) {
System.out.print(i + "*" + j + "=" + i*j +" ");
}
System.out.println();
}
}
} 练习(100以内所有质数):
Day05
数组:
- 数组是多个相同的数据类型,按照一定顺序排列的集合,并使用一个名字命名,通过编号统一管理
数组相关概念:
- 数组名;
- 元素;
- 角标(索引);
- 数组的长度(存储的元素个数)
数组特点:
- 有序排列;
- 数组本身属于引用数据类型,但是数组的元素可以是基本数据类型也可以是引用数据类型;
- 在内存中连续空间(链表是不连续的);
- 数组长度一旦确定,就不可以修改;
数组分类:
- 一维数组、二维数组、多维数组... ...
- 基本数据类型元素数组、引用数据类型数组... ...
一维数组的使用:
一维数组的声明和初始化;
如何调用数组的指定位置元素(角标);
如何获取数组的长度;
如何遍历数组元素;
数组元素的默认初始化值;
数组的内存解析
静态初始化:数组的初始化和数组元素的赋值同时进行;
动态初始化:数组的初始化和数组元素的赋值分别进行;
不管采用何种初始化方法,数组长度是确定的
public class ArrayTest {
public static void main(String[] args) {
int num;//声明
num = 10;//初始化
int id = 1001;//声明+初始化
//一维数组的声明和初始化
int[] ids;//声明(数组元素类型为int)
//静态初始化:数组的初始化和数组元素的赋值同时进行
ids = new int[]{1001,1002,1003,1004};
//动态初始化:数组的初始化和数组元素的赋值分别进行
String[] names = new String[5];
//如何调用数组的指定位置元素
//数组角标从0开始,到数组长度-1结束
names[0] = "Wang";
names[1] = "Liu";
names[2] = "Zhang";
names[3] = "Zhao";
names[4] = "Xu";
//如何获取数组的长度
//属性:length
System.out.println(ids.length);
System.out.println(names.length);
//如何遍历数组元素
System.out.println(ids.length);
for(int i = 0;i <= names.length - 1;i++){
System.out.println(names[i]);
}
/*
数组元素的默认初始化值
数组元素是整型,数组初始化值是0
数组元素是浮点型,默认初始化值是0.0
数组元素是char型,默认初始化值是'0'对应的ASCII码(空格效果)
数组元素是布尔型,默认初始化值是false
数组元素是引用数据类型时,默认初始化值是null(空值)
*/
int[] arr = new int[4];
for(int i = 0;i <= arr.length - 1;i++){
System.out.println(arr[i]);//没有赋值,默认为0
}
char[] arr2 = new char[4];
for(int i = 0;i <= arr2.length - 1;i++){
System.out.println(arr2[i]);//没有赋值,默认为0
}
String[] arr3 = new String[4];
for(int i = 0;i <= arr3.length - 1;i++){
System.out.println(arr3[i]);//没有赋值,默认为0
}
//数组的内存解析
}
}
练习:
/*
从键盘读入学生成绩,找出最高分,并输出学生成绩等级
成绩 >= 最高分 - 10 等级为A
成绩 >= 最高分 - 20 等级为B
成绩 >= 最高分 - 30 等级为C
其余 等级为D
*/
import java.util.Scanner;
public class ArrayExer {
public static void main(String[] args) {
//使用Scanner 读取学生个数
Scanner scanner = new Scanner(System.in);
System.out.println("输入学生人数:");
int number = scanner.nextInt();
//创建数组,存储学生成绩,并且动态初始化
int[] scores = new int[number];
//给数组中元素赋值
System.out.println("输入学生成绩:");
for(int i =0;i < scores.length;i++){
scores[i] = scanner.nextInt();
}
//获取数组中元素最大值(最高分)
int maxScore = 0;
for(int i = 0;i < scores.length;i++){
maxScore = scores[i];
}
//根据每个学生的成绩判断等级,并输出等级和成绩
char level;
for(int i = 0;i < scores.length;i++){
if (maxScore - scores[i] <= 10) {
level = 'A';
}else if (maxScore - scores[i] <= 20) {
level = 'B';
}else if (maxScore - scores[i] <= 30) {
level = 'C';
}else{
level = 'D';
}
System.out.println("Student " + i + " score is " + scores[i] + ",grade is " + level);
}
}
} 二维数组:
对于二维数组,可以看作是一维数组array1又作为另一个一维数组array2的元素而存在
二维数组的使用:
二维数组的声明和初始化;
如何调用数组的指定位置元素(角标);
如何获取数组的长度;
如何遍历数组元素;
数组元素的默认初始化值;
数组的内存解析
/*
*/
import java.util.Scanner;
public class ArrayTest2 {
public static void main(String[] args) {
//二维数组的声明和初始化
//静态初始化
int[][] arr1 =new int[][]{{1,2,3},{4,5,6},{7,8,9},{10}};
//动态初始化1
String[][] arr2 = new String[3][2];
//动态初始化2
String[][] arr3 = new String[3][];
//如何调用数组的指定位置元素
System.out.println(arr1[0][1]);//2
System.out.println(arr2[1][1]);//null
//如何获取数组的长度
System.out.println(arr1.length);//4
System.out.println(arr1[0].length);//3
//如何遍历二维数组
for (int i = 0; i < arr1.length; i++) {
for (int j = 0; j < arr1[i].length; j++) {
System.out.print(arr1[i][j]);
}
System.out.println();
}
//数组元素的默认初始化值
//二维数组分为外层数组元素和内层数组元素
//int arr[][] = new int[][]{{2,3},{4,5}};
//外层元素:arr[0] arr[1]
//内层元素:arr[0][0]... ...arr[1][1]... ...
/*
针对初始化方式一:int arr[][] = new int[4][3];
外层初始化元素为地址值;
内层初始化元素与一维数组初始化类型相同
针对初始化方式二:int arr[][] = new int[4][];
外层初始化元素为null;
内层初始化元素:不能调用,否则报错
*/
int arr[][] = new int[][]{{2,3},{4,5}};
System.out.println(arr[0]);//地址值
System.out.println(arr[0][0]);
}Day06
Java面向对象学习的三条主线:
1.Java;类以及类的成员:属性、方法、构造器;代码块、内部类
2.面向对象的三大特点:封装性、继承性、多态性、(抽象性)
3.其他关键字:this、super、static、final、abstract、interface、package、import
类:对一类事物的描述,是抽象的、概念上的定义
对象:是实际存在的该类事物的每个个体,因而也称为实例(instance)
面向对象程序设计的重点是类的设计
设计类,就是设计类的成员。
属性:对应类中的成员变量 field 域
方法:对应类中的成员方法 函数 method
创建类的对象:实例化
面向对象思想落地的实现:
1.创建类、设计类的成员;
2.创建类的对象;
3.通过 对象.属性 或 对象.方法 调用对象的结构
如果创建了一个类的多个对象,则每个对象都独立拥有一套类的属性(非static)
意味着,如果修改一个对象属性A则不影响另外一个对象属性A的值
package day06;
/**
* @author SunWenhao
* @create 2021/8/13
*/
public class OOPTest {
public static void main(String[] args) {
//创建Person类的对象
Person p1 = new Person();
//调用对象的结构:属性、方法
//调用属性:对象.属性
p1.name = "Sun";
p1.isMale = true;
System.out.println(p1.name);
//调用方法:对象.方法
p1.eat();
p1.sleep();
p1.talk("Chinese");
Person p2 = new Person();
System.out.println(p2.name);//null 没有赋值
System.out.println(p2.isMale);
Person p3 = p1;
System.out.println(p3.name);//Sun
p3.age = 10;
System.out.println(p1.age);//和数组一样,只是把p1变量的地址值赋给p3,p1和p3指向堆空间的同一个对象实体
}
}
class Person{
//属性
String name;
int age = 1;
boolean isMale;
//方法
public void eat(){
System.out.println("人可以吃饭");
}
public void sleep(){
System.out.println("人可以睡觉");
}
public void talk(String language){
System.out.println("人使用的语言是:" + language);
}
}
类中属性(成员变量)vs 局部变量的使用:
1.相同点:
定义变量的格式:数据类型 变量名 = 变量值
先声明、后使用
变量都有其对用的作用域
2.不同点:
在类中声明的位置不同
属性:直接定义在类的一堆{}中;
局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量
权限修饰符的不同
属性:可以在声明属性时,指明其权限,使用权限修饰符
常用权限修饰符:private public 缺省 protected
目前声明属性时,使用缺省即可
局部变量不可以使用权限修饰符
默认初始化值
属性:类的属性根据其类型有默认初始化值
属性:类的属性,根据其类型,都默认初始化值。
整型(byte、short、int、long:0)
浮点型(float、double:0.0)
字符型(char:0 (或'\u0000'))
布尔型(boolean:false)
引用数据类型(null)
局部变量没有初始化值,调用局部变量要赋值
特别地,形参在调用时赋值即可
在内存中加载的位置不同(非static)
属性:加载到堆空间中
局部变量:加载到栈空间
package day06;
/**
* @author SunWenhao
* @create 2021/8/13
*/
public class UserTest {
public static void main(String[] args) {
User u1 = new User();
System.out.println();
}
}
class User {
//属性(成员变量)
String name;
public int age;
boolean isMale;
public void talk(String language) {//language:形参 局部变量
System.out.println("我们使用" + language);
}
public void eat() {
String food = "馅饼";//局部变量
System.out.println("我们吃" + food);
}
} 类中方法的声明和使用
方法:描述类应该具有的功能
比如Math:sqrt() random()
Scanner: nextxxx()
Array: sort() toString() equals()
public void eat() void:没有返回值
public void sleep(int hour)
public String getName() String:返回String类型数据
public String getNation(String nation)
方法的声明:
权限修饰符 返回值类型 方法名(形参列表-可选){
方法体
}
权限修饰符:private、public、缺省、protected
返回值类型:有返回值 、 没有返回值
如果方法有返回值,则必须在方法声明时,指定返回值类型,同时方法中使用return关键字返回指定类型数据
如果方法没有返回值,则方法声明时,用void表示,通常没有返回值的方法中不用return,只能用return;表示结束方法的意思
定义方法该不该有返回值:
题目要求:明确要求返回什么值
题目没有要求:凭借经验
方法名:属于标识符,遵循标识符见名知意
形参列表:方法可以声明0个、1个,或多个形参
格式:数据类型1 形参1,数据类型2 形参2,... ...
定义方法时要不要形参:1.题目要求 2.凭借经验
方法体:方法功能的体现
return关键字:
1.使用范围:使用在方法体中;
2.作用:1.结束方法; 2.针对有返回值类型的方法,使用 return 数据 返回指定的数据
3.return关键字后面不可以声明执行语句
方法的使用:
方法内可以调用当前类中的属性和方法;
特殊的:方法A中又调用方法A:递归方法
方法中不可以定义别的方法
package day06;
/**
* @author SunWenhao
* @create 2021/8/13
*/
public class CustomerTest {
public static void main(String[] args) {
}
}
class Customer{
String name;
int age;
boolean isMale;
public void eat(){
System.out.println("吃饭");
}
public void sleep(int hour){
System.out.println("休息了" + hour + "个小时");
}
public String getName(){
return name;
}
public String getNation(String nation){
String info = "国籍是:" + nation;
return info;
}
}
练习1:
package day06;
/**
* @author SunWenhao
* @create 2021/8/13
*/
public class Person {
String name;
int age;
int sex;
//sex = 1 男, sex = 0 女
public void study(){
System.out.println("studying");
}
public void showAge(){
System.out.println(age);
}
public int addAge(int i){
age = age + i;
return age;
}
}
package day06;
/**
* @author SunWenhao
* @create 2021/8/13
*/
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person();
p1.name = "Sun";
p1.age = 23;
p1.sex = 1;
System.out.println(p1.age);
System.out.println(p1.name);
p1.study();
p1.showAge();
int newAge = p1.addAge(2);
System.out.println(newAge);
}
}
练习2:
package day06;
/**
* @author SunWenhao
* @create 2021/8/13
*/
public class CircleTest {
public static void main(String[] args) {
Circle circle = new Circle();
circle.radius = 2.0;
double circleArea = circle.Area();
System.out.println(circleArea);
}
}
class Circle{
double radius;
public double Area(){
double area = 3.14 * radius * radius;
return area;
}
} 练习3:
package day06;
/**
* @author SunWenhao
* @create 2021/8/13
*/
public class MatrixExer {
public static void main(String[] args) {
Matrix_1 matrix_1 = new Matrix_1();
matrix_1.Width = 8;
matrix_1.Length = 10;
matrix_1.Matrix_1_Print();
int S = matrix_1.Matrix_1_Area(20,8);
System.out.println(S);
}
}
class Matrix_1{
int Length;
int Width;
int Area;
public void Matrix_1_Print(){
for (int i = 0; i < Length ; i++) {
for (int j = 0; j < Width; j++) {
System.out.print("* ");
}
System.out.println();
}
}
public int Matrix_1_Area(int Length,int Width){
int Area = Length * Width;
return Area;
}
} Day07
匿名对象:
没有显时名称
匿名对象只能调用一次
匿名对象的使用:
package day07;
/**
* @author SunWenhao
* @create 2021/8/14
*/
public class InstanceTest {
public static void main(String[] args) {
Phone p = new Phone();
p.playGame();
p.sendEmail();
//匿名对象
new Phone().sendEmail();
new Phone().playGame();
new Phone().price = 1999;
new Phone().showPrice();//和上方的对象不一样
PhoneMall mall = new PhoneMall();
//mall.show(p);
mall.show(new Phone());
}
}
class Phone{
double price;
public void sendEmail(){
System.out.println("看电影");
}
public void playGame(){
System.out.println("玩游戏");
}
public void showPrice(){
System.out.println("手机价格为:" + price);
}
}
class PhoneMall{
public void show(Phone phone){
phone.sendEmail();
phone.playGame();
}
}
自定义数组工具类:
方法:
方法的重载
可变形参的方法
方法参数的值传递机制*
递归方法
重载:
定义:在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。
两同一不同:同一个类、同一个方法名、参数列表不同,参数个数不同
判断是否是重载:
跟方法的权限修饰符、返回值类型、形参变量名、方法体都没有关系
总结:"两同一不同":同一个类、相同方法名
参数列表不同:参数个数不同,参数类型不同
在通过对象调用方法时,如何确定指定方法:
方法名(可能有重载)---参数列表
package day07;
/**
* @author SunWenhao
* @create 2021/8/15
*/
public class OverLoadExer {
public static void main(String[] args) {
OverLoadExer test = new OverLoadExer();
test.mOL(2);
test.mOL(2,3);
test.mOL("Hello");
System.out.println(test.max(3.14,8.56,6.98));
}
public void mOL(int i){
System.out.println(i * i);
}
public void mOL(int i,int j){
System.out.println(i * j);
}
public void mOL(String s){
System.out.println(s);
}
public int max(int i,int j){
return (i > j)? i : j;
}
public double max(double d1,double d2){
return (d1 > d2)? d1 : d2;
}
public double max(double d1,double d2,double d3){
double max = (d1 > d2)? d1 : d2;
return (max > d3)? max : d3;
}
} 可变个数形参:(JDK5.0新增)
格式:数据类型 ... 变量名
调用可变个数形参方法时,传入参数个数可以是0、1、2 ... ... 多个;
可变个数形参方法与本类中同一个类、同一个方法名、参数列表不同,参数个数不同方法之间构成重载;
可变个数形参方法必须声明在末尾
package day07;
/**
* @author SunWenhao
* @create 2021/8/15
*/
public class MethodArgsTest {
public static void main(String[] args) {
MethodArgsTest test = new MethodArgsTest();
test.show(12);
test.show("Hello");
test.show("Hello1","Hello2");
}
public void show(int i){
}
public void show(String s){
}
public void show(String ... strs){
for (int i = 0; i < strs.length; i++) {
System.out.println(strs[i]);
}
}
//不可以共存
// public void show(String[] strs){
//
// }
}
方法参数的值传递机制:
如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值
如果参数时基本数据类型,此时实参赋给形参的时实参真实存储的数据值
方法形参传递机制:值传递
形参:方法定义时小括号内的参数
实参:给形参赋的值
值传递机制?:
package day07;
/**
* @author SunWenhao
* @create 2021/8/15
*/
//public class ValueTransferTest {
// public static void main(String[] args) {
// int m = 10;
// int n = m;
// System.out.println("m:"+ m + "," + "n:" + n);
// n = 20;
// System.out.println("m:"+ m + "," + "n:" + n);
//
//
// System.out.println();
// Order o1 = new Order();
// o1.orderId = 1001;
// Order o2 = o1;
// System.out.println(o1.orderId + " " + o2.orderId);
// o2.orderId = 1002;
// System.out.println(o1.orderId + " " + o2.orderId);
//方法形参传递机制:值传递
//形参:方法定义时小括号内的参数
//实参:给形参赋的值
public class ValueTransferTest {
public static void main(String[] args) {
ValueTransferTest test = new ValueTransferTest();
int m = 10;
int n = 20;
System.out.println("m:"+ m + "," + "n:" + n);
int temp = m;
m = n;
n = temp;
System.out.println("m:"+ m + "," + "n:" + n);
test.swap(m,n);
System.out.println("m:"+ m + "," + "n:" + n);//这里的m、n仍然是main方法中的,所以不会变
}
public void swap(int m,int n){
int temp = m;
m = n;
n = temp;
System.out.println("m:"+ m + "," + "n:" + n);
}
}
递归(了解):
在一个方法体中自己调自己
方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制。
递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环。
package day07;
/**
* @author SunWenhao
* @create 2021/8/15
*/
//计算1-100所有自然数的和
public class Recursion {
public static void main(String[] args) {
int sum = 0;
Recursion test = new Recursion();
// for (int i = 0; i < 100; i++) {
// sum = sum + i;
// }
sum = test.getSum(20);
System.out.println(sum);
}
public int getSum(int n){
if (n == 1){
return 1;
}else {
return n + getSum(n - 1);
}
}
}
斐波那契数列:
package day07;
/**
* @author SunWenhao
* @create 2021/8/15
*/
public class Fibonacci {
public static void main(String[] args) {
Fibonacci test = new Fibonacci();
System.out.println(test.Calculate(10));
}
public int Calculate(int n){
if (n == 1){
return 1;
}else if (n == 2){
return 1;
}else{
int fibonacci;
fibonacci = Calculate(n - 1) + Calculate(n -2);
return fibonacci;
}
}
}
Day08
封装与隐藏
我们程序设计追求“高内聚,低耦合”。
高内聚 :类的内部数据操作细节自己完成,不允许外部干涉;
低耦合 :仅对外暴露少量的方法用于使用。
隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。
package day08;
/**
* @author SunWenhao
* @create 2021/8/16
*/
/*
当我们创建一个类的对象以后,我们可以通过"对象.属性"的方式,对对象的属性进行赋值。这里,赋值操作要受到属性的数据类型和存储范围的制约。除此之外,没其他制约条件。但是,在实际问题中,我们往往需要给属性赋值加入额外的限制条件。这个条件就不能在属性声明时体现,我们只能通过方法进行限制条件的添加。(比如:setLegs()同时,我们需要避免用户再使用"对象.属性"的方式对属性进行赋值。则需要将属性声明为私有的(private),此时,针对于属性就体现了封装性。
*/
public class AnimalTest {
public static void main(String[] args) {
Animal a = new Animal();
a.name = "大黄";
a.setLegs(2);
a.show();
}
}
class Animal{
String name;
int age;
private int legs;//在AnimalTest中不可直接用,但是可以通过方法调用
public void setLegs(int l){
if (l >= 0 && l % 2 == 0){
legs = l;
}else{
legs = 0;
}
}
public void eat(){
System.out.println("进食");
}
public void show(){
System.out.println("name:" + name + " age:" + age + " legs:" + legs);
}
}
封装性思想具体的代码体现:
体现一:将类的属性xxx私化(private),同时,提供公共的(public)方法来获取(getXxx)和设置(setXxx)此属性的值;
体现二:不对外暴露的私有的方法;
体现三:单例模式(将构造器私有化);
体现四:如果不希望类在包外被调用,可以将类设置为缺省的。
Java规定的四种权限修饰符
权限从小到大顺序为:private < 缺省 < protected < public
具体的修饰范围:
4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类
修饰类的话,只能使用:缺省、public
总结封装性:
Java提供四种权限修饰符来修饰类以及类的内部结构,体现类以及类的内部结构在被调用时的可见性的大小
构造器作用:
创建对象,给对象进行初始化
如果没有显式定义类的构造器,则系统默认提供一个空参的构造器
如果没显式的定义类的构造器的话,则系统默认提供一个空参的构造器
- 定义构造器的格式:权限修饰符 类名(形参列表){}
- 一个类中定义的多个构造器,彼此构成重载
- 一旦我们显式的定义了类的构造器之后,系统就不再提供默认的空参构造器
- 一个类中,至少会有一个构造器。
package day08;
/**
* @author SunWenhao
* @create 2021/8/16
*/
public class PersonTest {
public static void main(String[] args) {
//创建类的对象: new + 构造器
Person p = new Person();
p.eat();
Person p1 = new Person("Tom");
System.out.println(p1.getAge());
}
}
class Person{
//属性
private int age;
private String name;
//构造器
public Person(){
System.out.println("Person");
age = 18;
}
public Person(String n){
name = n;
age = 18;
}
public Person(String n,int a){
name = n;
age = 18;
}
public void eat(){
System.out.println("人吃饭");
}
public void study(){
System.out.println("人学习");
}
public int getAge(){
return age;
}
} 练习:
package day08;
/**
* @author SunWenhao
* @create 2021/8/16
*/
public class TriAngleTest {
public static void main(String[] args) {
TriAngle t1 = new TriAngle();
t1.setBase(7.0);
t1.setHeight(5.0);
System.out.println(t1.getBase() + " " + t1.getHeight());
TriAngle t2 = new TriAngle(8.0,3.0);
System.out.println(t2.getBase() + " " + t2.getHeight());
System.out.println(t2.TriAngleArea());
}
}
package day08;
/**
* @author SunWenhao
* @create 2021/8/16
*/
public class TriAngle {
private double base;
private double height;
public TriAngle(){
}
public TriAngle(double b,double h){
base = b;
height= h;
}
public double TriAngleArea(){
double area = 0.5 * base * height;
return area;
}
public void setBase(double b){
if(b <= 0){
System.out.println("error");
}else {
base = b;
}
}
public double getBase(){
return base;
}
public void setHeight(double h){
if(h <= 0){
System.out.println("error");
}else {
height = h;
}
}
public double getHeight(){
return height;
}
} 总结:属性赋值的先后顺序:
① 默认初始化
② 显式初始化
③ 构造器中初始化
④ 通过"对象.方法" 或 "对象.属性"的方式,赋值
以上操作的先后顺序:① - ② - ③ - ④
JavaBean:
所谓JavaBean,是指符合如下标准的Java类:
- 类是公共的
- 一个无参的公共的构造器
- 有属性,且有对应的get、set方法
package day08;
/**
* @author SunWenhao
* @create 2021/8/16
*/
public class Customer {
private int id;
private String name;
public Customer(){
}
public void setId(int i){
id = i;
}
public int getId(){
return id;
}
public void setName(String str){
name = str;
}
public String getName(){
return name;
}
} this关键字:
this修饰属性、方法、构造器,理解为当前对象
在类的方法中,我们可以使用"this.属性"或"this.方法"的方式,调用当前对象属性或方法。但是,通常情况下,我们都择省略"this." 特殊情况下,如果方法的形参和类的属性同名时,我们必须显式使用"this.变量"的方式,表明此变量是属性,而非形参。
在类的构造器中,我们可以使用"this.属性"或"this.方法"的方式,调用当前正在创建的对象属性或方法。但是,通常情况下,我们都择省略"this."。特殊情况下,如果构造器的形参和类的属性同名时,我们必须显式的使用"this.变量"的方式,表明此变量是属性,而非形参。
package day08;
import java.security.PublicKey;
/**
* @author SunWenhao
* @create 2021/8/16
*/
public class ThisTest {
public static void main(String[] args) {
Person_Test person = new Person_Test("Tom");
System.out.println(person.getName());
}
}
class Person_Test{
private String name;
private int age;
public Person_Test(String name){
this.name = name;
}
public void setAge(int age){
this.age = age;
}
public void setName(String name){
this.name = name;
}
public int getAge() {
return age;
}
public String getName(){
return name;
}
}
this调用构造器:
① 我们在类的构造器中,可以显式的使用"this(形参列表)"方式,调用本类中指定的其他构造器;
② 构造器中不能通过"this(形参列表)"方式调用自己;
③ 如果一个类中有n个构造器,则最多有 n - 1构造器中使用了"this(形参列表)";
④ 规定:"this(形参列表)"必须声明在当前构造器的首行;
⑤ 构造器内部,最多只能声明一个"this(形参列表)",用来调用其他的构造器
... ...
public Person_Test(){
}
public Person_Test(String name){
this();
this.name = name;
}
public Person_Test(String name,int age){
this(age);
this.name = name;
} package的使用:
- 为了更好的实现项目中类的管理,提供包的概念;
- 使用package声明类或接口所属的包,声明在源文件的首行;
- 包,属于标识符,遵循标识符的命名规则、规范(xxxyyyzzz)、“见名知意”;
- 每"."一次,就代表一层文件目录。
Day09
继承性:
继承性的好处:
减少代码冗余,提高代码复用性
便于功能的扩展
为之后的多态性的使用提供了前提
继承性的格式:
class A extends B { }
A:子类(派生类);
B:父类(父类、基类)
体现:一旦子类A继承父类B后,子类A就获取了B中声明的所有属性、方法
私有的属性、方法子类也继承到了,只是由于封装性不能直接调用;
子类继承父类后,还可以定义自己特有的属性、方法,实现功能拓展;
子类、父类不同于子集和集合的关系,父类的功能反而单一一些
Java中关于继承性的规定:
- 一个类可以被多个子类继承,一个类只能有一个父类;
- Java不支持多重继承,支持单继承和多重继承;
- 子父类是相对概念;
- 子类直接继承的父类称为直接父类,子类间接继承的父类称为间接父类;
- 子类继承父类之后,就获取了直接父类以及所有间接父类中声明的属性和方法;
- 如果没有显式地声明一个类的父类的话,则此类继承于java.lang.Object类,所有的Java类都直接或间接于java.lang.Object类;
- 所有的Java类都具有java.lang.Object类声明的功能
package day09;
/**
* @author SunWenhao
* @create 2021/8/19
*/
public class Student extends Person{
// String name;
// int age;
String major;
public Student(){
}
public Student(String name,int age){
this.name = name;
this.age = age;
}
// public void eat(){
// System.out.println("吃饭");
// }
//
// public void sleep(){
// System.out.println("睡觉");
// }
public void study(){
System.out.println("学习");
}
} alt + insert :快速构建构造器、setter、getter
练习:
package day09_test;
/**
* @author SunWenhao
* @create 2021/8/19
*/
public class Circle {
private double radius;
public Circle(){
radius = 1.0;
}
public void setRadius(double radius) {
this.radius = radius;
}
public double getRadius() {
return radius;
}
public double findArea(){
double area = radius * radius * Math.PI;
return area;
}
}
package day09_test;
/**
* @author SunWenhao
* @create 2021/8/19
*/
public class Cylinder extends Circle{
private double length;
public Cylinder(){
length = 1.0;
}
public void setLength(double length) {
this.length = length;
}
public double getLength() {
return length;
}
public double findVolume(){
// double volume = Math.PI * getRadius() * getRadius() * getLength();
// return volume;
return findArea() * getLength();
}
}
package day09_test;
/**
* @author SunWenhao
* @create 2021/8/19
*/
public class CylinderTest {
public static void main(String[] args) {
var cylinder = new Cylinder();
cylinder.setRadius(8.0);
cylinder.setLength(5.0);
System.out.println("圆柱的体积为:" + cylinder.findVolume());
}
} 重写规定:
方法的声明:权限修饰符 返回值类型 方法名(形参列表)throws 异常的类型{
//方法体
}
约定俗成:子类中叫重写的方法,父类中叫被重写的方法
子类重写的方法的方法名和形参列表与父类中被重写的方法的方法名和形参列表相同;
子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符;
- 特殊情况:子类不能重写父类为private的方法;
返回值类型:
- 父类被重写的方法返回值类型是void,则子类重写的方法返回值类型只能是void;
- 父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类;
- 父类被重写的方法的返回值类型是基本数据类型,则子类重写的方法的返回值类型必须相同
子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型;
子类和父类中的同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static的(不是重写)。
super关键字:
super理解为:父类的
可以用来调用的结构:
属性、方法、构造器
我们可以在子类的方法或构造器中。通过使用"super.属性"或"super.方法"的方式,显式的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略"super."
特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的使用"super.属性"的方式,表明调用的是父类中声明的属性。
特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的使用"super.方法"的方式,表明调用的是父类中被重写的方法。
super调用构造器:
- 我们可以在子类的构造器中显式的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器
- "super(形参列表)"的使用,必须声明在子类构造器的首行!
- 我们在类的构造器中,针对于"this(形参列表)"或"super(形参列表)"只能二一,不能同时出现
- 在构造器的首行,没显式的声明"this(形参列表)"或"super(形参列表)",则默认调用的是父类中空参的构造器:super()
- 在类的多个构造器中,至少一个类的构造器中使用了"super(形参列表)",调用父类中的构造器
多态性:可以理解为一个事物的多种形态
多态性的使用:虚拟方法调用
有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
总结:编译,看左边;运行,看右边。
多态性的使用前提:
① 类的继承关系 ② 方法的重写
package day09_test2;
/**
* @author SunWenhao
* @create 2021/8/20
*/
public class PersonTest {
public static void main(String[] args) {
//对象的多态性,父类的引用指向子类的对象(或子类的对象赋给父类的引用)
Person p2 = new Man();
Person p3 = new Woman();
//当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法
p2.eat();//调用的是重写的子类方法
//p2.earnMoney(); 没有在Person类中定义,
}
} 举例:
package day09_test2;
/**
* @author SunWenhao
* @create 2021/8/20
*/
public class Animal{
public void eat(){
System.out.println("进食");
}
public void shout(){
System.out.println("叫");
}
}
class dog extends Animal{
public void eat(){
System.out.println("吃狗粮");
}
public void shout(){
System.out.println("汪");
}
}
class cat extends Animal{
public void eat(){
System.out.println("猫吃鱼");
}
public void shout(){
System.out.println("喵");
}
}
//******************************************************
package day09_test2;
/**
* @author SunWenhao
* @create 2021/8/20
*/
public class AnimalTest {
public static void main(String[] args) {
AnimalTest test = new AnimalTest();
test.func(new dog());
}
public void func(Animal animal){//Animal animal = new dog();
animal.eat();
animal.shout();
}
} 对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
Day10
有了对象的多态性以后,内存中实际是加载了子类特有的方法和属性,由于变量声明为父类类型,导致编译时只能调用父类中声明的属性和方法,子类特有的属性和方法不能调用
如何调用子类特有的属性和方法:
- 使用强制类型转换符,向下转型
Man m1 = (Man)p2;
instanceof关键字使用:
a instanceof A :判断对象a是不是类A的实例,如果是返回true,如果不是返回false
为了避免向下转型出现异常,在向下转型之前先进行instanceof,一旦返回ture就进行向下转型,如果是false则不能进行向下转型;
如果 a instanceof A 返回ture,则 a instanceof B 也返回true,类B是类A的父类
Object类:
1.Object类是所Java类的根父类;
2.如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类 ;
3.Object类中的功能(属性、方法)就具通用性:
属性:无
方法:equals() / toString() / getClass() /hashCode() / clone() / finalize()、wait() 、 notify()、notifyAll()
4.Object类只声明了一个空参的构造器
equals():
equals()是一个方法,而非运算符只能适用于引用数据类型;
Object类中equals()的定义:
public boolean equals(Object obj) {
return (this == obj);
}
说明:Object类中定义的equals()和==的作用是相同的:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体;
像String、Date、File、包装类等都重写了Object类中的equals()方法,重写以后,比较的不是
两个引用的地址是否相同,而是比较两个对象的"实体内容"是否相同;
通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的"实体内容"是否相同。那么,我们就需要对Object类中的equals()进行重写,重写的原则:比较两个对象的实体内容是否相同.
== :
如果比较的是基本数据类型变量,比较两个变量保存的数据是否相等(不一定类型相同)(char 10 == int10);
如果比较的是引用数据类型变量,比较两个对象的地址值是否相同,即两个引用是否指向同一个对象实体
String、Date、File、包装类等都重写了Object类中的equals方法,重写以后比较的不是两个引用地址是否相同,而是比较两个对象的“实体内容”是否相同
package day10;
/**
* @author SunWenhao
* @create 2021/8/22
*/
public class EqualsTest {
public static void main(String[] args) {
String str1 = new String("HelloWorld!");
String str2 = new String("HelloWorld!");
System.out.println(str1 == str2);//false
System.out.println(str1.equals(str2));//true
}
}
通常情况下,自定义类使用equals( )的话,也通常是比较两个对象的“实体内容”是否相同,就需要对Object类中的equals方法进行重写
使用自动生成equals()方法
package day10;ToString
/**
* @author SunWenhao
* @create 2021/8/22
*/
public class EqualsTest {
public static void main(String[] args) {
Person person1 = new Person(23,"Sun");
Person person2 = new Person(23,"Sun");
String str1 = new String("HelloWorld!");
String str2 = new String("HelloWorld!");
System.out.println(str1 == str2);//false
System.out.println(str1.equals(str2));//true
System.out.println(person1.equals(person2));//true
}
}
toString:
当我们输出一个对象的引用时,实际上就是调用当前对象的toString方法;
String、Date、File、包装类等都重写了Object类中的toString方法,使得在调用toString方法时返回“实体内容”信息,自定义类也可以重写toString方法,调用此方法时返回“实体内容”
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
} 单元测试方法:
Java中的JUnit单元测试
步骤:
中当前工程 - 右键择:build path - add libraries - JUnit 4 - 下一步
创建Java类,进行单元测试
此时的Java类要求:① 此类是public的 ②此类提供公共的无参的构造器
此类中声明单元测试方法,
此时的单元测试方法:方法的权限是public,没返回值,没形参
此单元测试方法上需要声明注解:@Test,并在单元测试类中导入:import org.junit.Test;
声明好单元测试方法以后,就可以在方法体内测试相关的代码。
写完代码以后,左键双击单元测试方法名,右键:run as - JUnit Test
说明:
1.如果执行结果没任何异常:绿条
2.如果执行结果出现异常:红条
包装类:
为了使基本数据类型的变量具有类的特征,引入包装类
public static void main(String[] args) {
var d = new Double(5.0);
System.out.println(d.equals(5.0));//true
System.out.println(d.toString());//5.0
}
java提供了8种基本数据类型对应的包装类,使得基本数据类型具有类的特征
掌握基本数据类型、包装类、String类型三者转换
//基本数据类型-->包装类,调用包装类的构造器
int num1 = 10;
Integer in1 = new Integer(num1);
System.out.println(in1.toString());
Integer in2 = new Integer("123");
System.out.println(in2 - 100);
Boolean b1 = new Boolean(true);
Boolean b2 = new Boolean("true");
Boolean b3 = new Boolean("true123");//false
Boolean b4 = new Boolean("TrUe");//true
//包装类-->基本数据类型,调用xxxValue()
Integer in_1 = new Integer(18);
var i1 = in_1.intValue();
System.out.println(i1 + 10); 自动装箱与拆箱 JDK5.0 :
//自动装箱 int num2 = 10; Integer in_2 = num2; //自动拆箱 System.out.println(in_2.toString());
//基本数据类型--> String 调用String重载的valueOf(Xxx xxx)
float f1 = 12.3f;
String str = String.valueOf(f1);
System.out.println(str);
Double d1 = new Double(23.2);
String str2 = String.valueOf(d1);
System.out.println(str2);
//String-->基本数据类型、包装类,调用包装类的parseXxx()
String str3 = "123";
int anInt= Integer.parseInt(str2);
String str4 = "true";
boolean anBoolean= Boolean.parseBoolean(str4);
System.out.println(anBoolean); Day11
static:可以修饰属性、方法、代码块、内部类(不能修饰构造器);
使用static修饰属性:静态变量(类变量),
属性按是否使用static修饰分为静态属性和非静态属性(实例变量);
实例变量:创建了类的多个对象,每个对象都独立拥有一套类中的非静态属性,当修改其中一个对象的非静态属性时,不会导致其他对象中同样的属性值的修改;
静态变量:创建了类的多个对象,每个对象都共享同一个静态变量,当修改其中一个对象的静态属性时,会导致其他对象中同样的静态变量的修改;
static修饰属性说明:
静态变量随着类的加载而加载,通过 类.静态变量 进行加载
静态变量的加载早于对象的创建
由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中
类变量 实例变量
类 yes no
对象 yes yes
static修饰方法:静态方法、类方法
① 随着类的加载而加载,可以通过"类.静态方法"的方式进行调用
② 静态方法 非静态方法
类 yes no
对象 yes yes
③ 静态方法中,只能调用静态的方法或属性
非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性
static的注意点:
在静态的方法内,不能使用this关键字、super关键字(没有对象)
关于静态属性和静态方法的使用,都从生命周期的角度去理解
非静态属性、方法必须new个对象才能调用,而静态方法可以直接通过类调用
package day11;
import javax.crypto.Cipher;
/**
* @author SunWenhao
* @create 2021/8/24
*/
public class StaticTest {
public static void main(String[] args) {
Chinese c1 = new Chinese();
c1.name = "Sun";
c1.age = 23;
Chinese c2 = new Chinese();
c2.name = "Xu";
c2.age = 22;
c1.nation = "China";
c2.nation = "CHN";
System.out.println(c1.nation);
System.out.println(c2.nation);
Chinese.sleep();
}
}
class Chinese{
String name;
int age;
static String nation;
static void sleep(){
System.out.println("睡觉!");
}
public void eat(){
System.out.println("吃饭!");
}
public void print(){
this.eat();
sleep();
System.out.println(nation);
}
}
如何判定属性和方法应该使用static关键字:
关于属性:
属性是可以被多个对象所共享的,不会随着对象的不同而不同的。
类中的常量也常常声明为static
关于方法:
操作静态属性的方法,通常设置为static的
工具类中的方法,习惯上声明为static的,比如:Math、Arrays、Collections
练习:
package day11;
/**
* @author SunWenhao
* @create 2021/8/25
*/
public class CircleTest {
public static void main(String[] args) {
var circle = new Circle();
var circle2 = new Circle();
var circle3 = new Circle(5.2);
System.out.println(circle.getId());
System.out.println(circle2.getId());
System.out.println("创建圆的个数为:" + Circle.getTotal());
System.out.println(circle3.findArea());
}
}
class Circle {
private double radius;
private int id;
public Circle(){
id = init++;
total++;
}
public Circle(double radius){
this();
this.radius = radius;
}
private static int total;
private static int init = 1001;
public double findArea(){
return Math.PI * radius * radius;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public static int getTotal() {
return total;
}
}
设计模式:
设计模式是在大量的实践中总结和理论化之后优的代码结构、编程风格、以及解决问题的思考方式
常用设计模式 --- 23种经典的设计模式 GOF
创建型模式,共5种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共7种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共11种:策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
单例模式
要解决的问题:
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例
main方法
main()方法作为程序的入口
main()方法也是一个普通的静态方法
main()方法可以作为我们与控制台交互的方式。(之前:使用Scanner)
代码块:
作用:用来初始化类、对象
分类:代码块要是使用修饰符,只能使用static
静态代码块:
内部可以输出语句
随着类的加载而执行,而且只执行一次
作用:初始化类的信息
如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行
静态代码块的执行要优先于非静态代码块的执行
静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构
非静态代码块:
内部可以输出语句
随着对象的创建而执行
每创建一个对象,就执行一次非静态代码块
作用:可以在创建对象时,对对象的属性等进行初始化
如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行
非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法
package day11;
/**
* @author SunWenhao
* @create 2021/8/25
*/
public class BlockTest {
public static void main(String[] args) {
String description = Person.description;
var person = new Person();
var person1 = new Person();
}
}
class Person{
//属性
String name;
int age;
static String description = "我是一个人";
//构造器
public Person(){
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
//静态代码块
static {
System.out.println("Hello~");
}
//非静态代码块
{
System.out.println("Hello!");
}
//方法
public void eat(){
System.out.println("吃饭");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
} ①默认初始化
②显式初始化 / ⑤在代码块中赋值
③构造器中初始化
④有了对象以后,可以通过"对象.属性"或"对象.方法"的方式,进行赋值
执行的先后顺序:① - ② / ⑤ - ③ - ④
final关键字:
final可以修饰结构:类、方法、变量
final 用来修饰一个类:此类不能被其他类所继承。
- 比如:String类、System类、StringBuffer类
final 用来修饰方法:表明此方法不可以被重写
- 比如:Object类中getClass();
final 用来修饰变量:此时的"变量"就称为是一个常量
final修饰属性:可以考虑赋值的位置:显式初始化、代码块中初始化、构造器中初始化
final修饰局部变量:
尤其是使用final修饰形参时,表明此形参是一个常量。当我们调用此方法时,给常量形参赋一个实参,一旦赋值以后,就只能在方法体内使用此形参,但不能进行重新赋值。
public void show(){
final int NUMBER = 10;
System.out.println(NUMBER);
}
public void show(final int NUMBER){
System.out.println(NUMBER);
} static final 用来修饰属性:全局常量
Day12
abstract: 抽象的
1.可以用来修饰:类、方法
2.具体的:
abstract修饰类:抽象类
- 此类不能实例化
- 抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)
- 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作 --->抽象的使用前提:继承性
abstract修饰方法:抽象方法
抽象方法只方法的声明,没方法体
包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。
若子类重写了父类中的所有的抽象方法后,此子类方可实例化
若子类没重写父类中的所有的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰
注意点:
abstract不能用来修饰:属性、构造器等结构
abstract不能用来修饰私有方法、静态方法、final的方法、final的类
abstract的应用举例:
abstract class GeometricObject{
public abstract double findArea();
}
class Circle extends GeometricObject{
private double radius;
public double findArea(){
return 3.14 * radius * radius;
};
} package day12;
/**
* @author SunWenhao
* @create 2021/8/26
*/
public class AbstractTest {
public static void main(String[] args) {
// var person = new Person();//不可实例化
// person.eat();
}
}
abstract class Person{
String name;
int age;
public Person(){
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// public void eat(){
// System.out.println("吃饭!");
// }
public abstract void eat();//抽象方法
public void walk(){
System.out.println("走路!");
}
}
abstract class Student extends Person{
public Student(String name,int age){
super(name,age);
}
// public void eat(){
//
// }
} 练习:
package day12;
/**
* @author SunWenhao
* @create 2021/8/26
*/
abstract class Employee {
private String name;
private int id;
private double salary;
public Employee() {
}
public Employee(String name, int id, double salary) {
this.name = name;
this.id = id;
this.salary = salary;
}
public abstract void work();
}
package day12;
/**
* @author SunWenhao
* @create 2021/8/27
*/
public class Manager extends Employee{
private double bonus;
public Manager(double bonus) {
super();
this.bonus = bonus;
}
public Manager(String name, int id, double salary, double bonus) {
super(name, id, salary);
this.bonus = bonus;
}
public void work(){
System.out.println("管理员工");
}
}
package day12;
/**
* @author SunWenhao
* @create 2021/8/26
*/
public class EmployeeTest {
public static void main(String[] args) {
Manager manager = new Manager("Sun",1001,30000.0,20000.0);
manager.work();
}
}
只用一次:
method(new Student());//匿名对象
Worker worker = new Worker();
method1(worker);//非匿名的类非匿名对象
method1(new Worker());//非匿名的类匿名的对象
Person p = new Person(){//匿名子类非匿名的对象
public void work(){
//重写抽象类
}
}
method1(new Person(){//匿名子类非匿名的对象
public void work(){
//重写抽象类
}
}) 接口的使用:
接口使用interface定义
Java中,接口和类是并列的两个结构
如何定义接口,定义接口中的成员:
JDK7:
只能定义全局常量和抽象方法
全局常量:public static final的
抽象方法:public abstract的
接口中不能定义构造器-->接口不可以实例化
Java开发中,接口通过让类实现(implements)去使用,
如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化
如果实现类没有覆盖了接口中的所有抽象方法,则此实现类仍为一个抽象类
(要么全重写抽象方法,要么声明为抽象类)
Java类可以实现多个接口 --->弥补了Java单继承性的局限性
格式:class AA extends BB implements CC,DD,EE
接口与接口之间可以实现多继承
格式:interface AA extends BB,CC
接口的具体使用,体现多态性
接口,实际上可以看做是一种规范
package day12;
/**
* @author SunWenhao
* @create 2021/8/27
*/
public class InterfaceTest {
public static void main(String[] args) {
var plane = new Plane();
plane.fly();
var bullet = new Bullet();
bullet.attack();
}
}
interface Flyable{
//全局常量
public static final int MAX_SPEED = 7900;
int MIN_SPEED = 1;//可省略 public static final,仍是常量
//抽象方法
public abstract void fly();
void stop();//可省略 public abstract
}
interface Attackable{
void attack();
}
class Plane implements Flyable{
@Override
public void fly() {
System.out.println("起飞");
}
@Override
public void stop() {
System.out.println("降落");
}
abstract class Kite implements Flyable{
@Override
public void fly() {
}
}
}
class Bullet extends Object implements Flyable,Attackable{
@Override
public void fly() {
System.out.println("起飞");
}
@Override
public void stop() {
System.out.println("降落");
}
@Override
public void attack() {
System.out.println("攻击");
}
} //创建了接口的非匿名对象实现类的非匿名对象
Flash flash = new Flash();
com.transferData(flash);
//创建了接口的非匿名实现类的匿名对象
com.transferData(new Printer());
////创建了接口的匿名实现类的非匿名对象
USB phone = new USB(){
public void xxx(){
}
}
com.transferData(phone);
////创建了接口的匿名实现类的匿名对象
com.transferData new USB(){
public void xxx(){
}
} JDK8:除了定义全局常量和抽象方法,还可以定义静态方法和默认方法
接口中定义的静态方法只能通过接口调用
通过实现类的对象,可以调用接口中的默认方法
//知识点1:接口中定义的静态方法,只能通过接口来调用。
//知识点2:通过实现类的对象,可以调用接口中的默认方法。
//如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法
//知识点3:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,那么子类在没重写此方法的情况下,默认调用的是父类中的同名同参数的方法。-->类优先原则
//知识点4:如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,
//那么在实现类没重写此方法的情况下,报错。-->接口冲突。
//这就需要我们必须在实现类中重写此方法
//知识点5:如何在子类(或实现类)的方法中调用父类、接口中被重写的方法
package day12;
/**
* @author SunWenhao
* @create 2021/8/27
*/
public class SubClassTest {
public static void main(String[] args) {
var subclass = new SubClass();
//接口中定义的静态方法只能通过接口调用
CompareA.method1();
//通过实现类的对象,可以调用接口中的默认方法
subclass.method2();
}
}
class SubClass implements CompareA{
public void method2(){
System.out.println("重写");
}
} public void myMethod(){
method3();//调用自己定义的重写的方法
super.method3();//调用的是父类中声明的
//调用接口中的默认方法
CompareA.super.method3();
CompareB.super.method3();
} 内部类的使用:
定义:Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类.
内部类的分类:
成员内部类(静态、非静态 ) vs 局部内部类(方法内、代码块内、构造器内)
成员内部类的理解:
一方面,作为外部类的成员:
- 调用外部类的结构
- 可以被static修饰
- 可以被4种不同的权限修饰
另一方面,作为一个类:
- 类内可以定义属性、方法、构造器等
- 可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承
- 可以被abstract修饰
package day12;
/**
* @author SunWenhao
* @create 2021/8/27
*/
public class InnerClassTest {
}
class Person_Test{
//成员内部类
class Dog{
}
static class Bird{
String name;
public Bird(){
}
public void show(){
System.out.println("Bird");
}
}
public void method(){
//局部内部类
class AA{
}
}
} 