Java基础第一阶段
- Java代码规范
- Dos命令
- 基本数据类型转换
- 自动类型转换
- 强制类型转换
- 字符串转换
- 运算符
- 算数运算符
- 关系运算符
- 逻辑运算符
- 赋值运算符
- 三元运算符
- 位运算
- 运算符优先级
- 标识符命名的规则和规范
- 标识符的命名规则
- 标识符命名规范
- 键盘输入
- 原码、反码、补码
- 对于有符号的而言
- 程序控制结构
- 顺序控制
- 分支控制
- 循环控制
- break
- continue
- return
- 数组、排序和查找
- 数组
- 数组赋值的机制、数组拷贝、数组添加
- 二维数组的使用方式
- 面向对象编程(基础部分)
- 类与对象
- 成员方法
- 成员方法传参机制
- 重载
- 可变参数
- 作用域
- 构造器
- this
- Intellij IDEA
- 快捷键(ecplise)
- 模板
- 包
- 访问修饰符
- 基本介绍
- 使用注意事项
- 封装
- 封装的理解和好处
- 封装实现步骤
- 继承(代码复用性)
- 基本语法
- 细节问题
- super关键字
- 便利与细节
- super和this的区别
- 方法重写
- 注意事项和使用细节
- 面向对象编程 - 多态
- 基本介绍
- 多态的具体体现
- 多态注意事项和细节讨论
- Java的动态绑定机制(非常重要)
- 多态的应用
- Object类详解
- equal方法
- hashCode() 方法
- toString方法
- 基本介绍
- finalize()方法
- 断点调试
- 快捷键
- 类变量和类方法
- 定义语法
- 访问方法
- 类变量使用注意事项和细节讨论
- 类方法基本介绍
- 类方法的调用
- 类方法使用的注意事项和细节讨论
- 理解mian方法语法
- 代码块
- 基本语法
- 注意事项和细节讨论
?## Java开发注意事项和细节说明
-
Java源文件以 .java为扩展名。源文件的基本组成部分是类(class),如泵类中的Hello类
-
Java应用程序的执行入孔是main()方法。有固定的书写格式:public static void main(String[] args){......}
-
Java语言严格区分大小写。
-
Java方法有一条条语句构成,每个语句以“;” 结束。
-
大括号都是成对出现的,缺一不可。(习惯先写{},再写代码)
// 这是Java的快速入门,演示java的快速开发 // 说明代码 //1.public class Hello 表示Hello是一个类,并且是public公有类 //2.Hello{}表示一个类的开始和结束 //3.public static void main(String[] args)表示一个主方法,即我们程序的入口 //4.main(){} 表示方法的开始和结束 //5.System.out.println("hello,world~"); 表示输出 hello,world~ 到屏幕 //6.;表示语句的结束 public class Hello{ //编写一个main方法 public static void main(String[] args){ System.out.println("hello,world~"); } } class Dog{ public static void main(String[] args){ System.out.println("汪汪"); } } class Tigger{ public static void main(String[] args){ System.out.println("追鹿"); } }
-
一个源文件中最多包含一个public类。其他类的个数不限
-
如果源文件包含一个public类,则文件名必须按该类名命名
-
一个源文件中最多只能有一个public类。其他类的个数不限,也可以将main方法写在非public类中,然后指定运行非public类,这样入口方法就是非public的main方法
Java代码规范
- 类、方法的注释要用javadoc的方式来写。
- 非Java Doc的注释,往往是给代码的维护者来看的,着重告诉读者为什么这样写,如何修改,注意什么问题。(单行和多行注释)
- 使用tab操作,实现缩进,默认整体向右边移动时,用shift+tab整体向左移动。
- 运算符和 = 两边习惯性的各加一个空格,如:1 + 2 * 4 + 5 - 8
- 源文件使用utf-8编码
- 行宽度不要超过80字符
- 代码编写 次行风格和行尾风格
Dos命令
-
相关知识补充:
相对路径:从当前目录开始定位,形成的一个路径
绝对路径:从顶级目录d,开始定位,形成的路径
-
常用的dos命令
1.查看当前目录有什么内容 dir
dir d:\abc2\test
2.切换到其他盘下:盘符号 cd : change directory
切换到C盘 cd /D c:
3.切换到当前盘的其他目录中(相对路径和绝对路径) ..\ 表示上一级目录
cd d:\abc2\test cd .. \ .. \abc2\test
4.切换到上一级
cd ..
5.切换到根目录
cd \
6.查看指定的目录下所有的子级目录
tree d:
7.清屏 cls
8.推出Dos exit
基本数据类型转换
自动类型转换
public class AutoConvert {
public static void main(String[] args) {
//自动转换
int num = 'a';
double d1 = 80;
System.out.println(num);
System.out.println(d1);
// 1.当多种数据混合计算时,系统自动将所有数据转换成容量最大的数据类型,并计算
int n1 = 10; //ok
//float d1 = n1 + 1.1; //错误,结果类型是double
double d1 = n1 + 1.1; //对,结果类型是double
float d1 = n1 + 1.1F; //对,结果类型是double
//2.当我们把精度大的数据类型赋值给精度小的数据类型时,
//会报错
int n2 = 1.1 //错误
// 3.(byte,short)和char之间不会相互自动转换
//当把书赋给byte时,先判断概述是否在byte范围内,如果是就可以
byte b1 = 10; //对,-127到128
// int n2 = 1; //n2是int
// byte b2 = n2; //错误,原因:如果是变量赋值,判断类型。字节不同
//char c1 = b1; //错误,原因byte不能自动转成char
//4.byte short char 三者只要出现在计算时首先转换为int类型
byte b2 = 1;
short s1 = 1;
//short s2 = b2 + s1; //错,b2 + s1 => int
//int s2 = b2 + s1; //,b2 + s1 => int
//byte b4 = b2 + b3; //错误, =>int
//5.自动提升原则,表达式结果的类型自动提升为操作数中最大类型
byte b4 = 1;
short s3 = 100;
int num200 = 1;
float num300 = b4 + s3 + num200; //错误, ===>double
}
}
强制类型转换
public class ForceConvert {
public static void main(String[] args){
//演示强制类型转换
int n1 = (int)1.9;
System.out.println("n1=" + n1); //1.导致精度损失
int n2 = 2000;
byte b1 = (byte)n2;
System.out.println("b1=" + b1); //2.导致数据溢出
//细节1:强转符号只对最近的操作数有效,通常使用小括号提升优先级
//int x = (int)10 * 3.5 + 6 * 1.5; //编译错误:double -> int
int x = int(10 * 3.5 + 6 * 1.5); //(int)44.0 -> 44
System.out.println(x);
//细节2:char 类型可以保存int的常量值,但不能保存int的变量值,要强转
char c1 = 100;
int m = 100; //ok
//char c2 = m; //错误
char c3 = (char)m; //ok
System.out.println(c3); //100对应字符d
}
}
字符串转换
public class StringToBasic {
public static void main(String[] args){
//基本数据类型->String
int n1 = 100;
float f1 = 1.1F;
double d1 = 4.5;
boolean b1 = true;
String s1 = n1 + "";
String s2 = f1 + "";
String s3 = d1 + "";
String s4 = b1 + "";
System.out.println(s1 + " " + s2 + " " + s3 + " " + s4);
//String->对应的机泵数据类型
String s5 = "123";
//在oop:讲对象和方法的时候会说
int num1 = Integer.parseInt(s5);
double num2 = Double.parseDouble(s5);
float num3 = Float.parseFloat(s5);
Long num4 = Long.parseLong(s5);
byte num5 = Byte.parseByte(s5);
boolean b = Boolean.parseBoolean("true");
short num6 = Short.parseShort(s5);
System.out.println(num1); //123
System.out.println(num2); //123.0
System.out.println(num3); //123.0
System.out.println(num4); //123
System.out.println(num5); //123
System.out.println(num5); //123
//解读s5.charAt(0)得到字符串的第一个字符
System.out.println(s5.charAt(0));
//细节1:将String类型转化成机泵数据类型是要确保String类型
//能够转成有效的数据,如将“123”转成整数,但不能将“hello”转成整数
String str = "hello";
int n1 = Integer.parseInt(str);
System.out.println(n1);
}
}
运算符
算数运算符
/**
* 演示算数运算符的使用
*/
public class ArithmeticOperator {
public static void main(String[] args) {
System.out.println(10 / 4); //从数学角度看是2.5,实际结果2
double d = 10 / 4;
System.out.println(d); //结果是2.0
// % 取模 ,取余
// 在Java中% 的本质,看一个公式: a % b = a - a / b * b
// -10 % 3 => -10 - (-10) / 3 * 3
// 10 % -3 = 10 - 10 / (-3) * (-3) = 10 -9 = 1
// -10 % -3 = (-10) - (-10) / (-3) * (-3) = -10 - 9 = -19
System.out.println(10 % 3); // 1
System.out.println(-10 % 3); // -1
System.out.println(10 % 3); // 1
System.out.println(10 % 3); // 1
//a%b 当a是小数时,公式 = a - (int)a / b * b
System.out.println(-10.5 % 3); // -1.5
//++的使用
//
int i = 10;
i++; //自增 == i = i + 1; ==> 11
++i; //自增 <==> i = i + 1; => 12
System.out.println("i=" + i); // 12
/*
作为表达式使用
前++:先自增后赋值
后++:i++先赋值后自加
*/
int j = 8;
// int k = ++j; //等价j = j + 1; k = j;
int k = j++; //等价j = j; j = j + 1;
System.out.println("k = " + k + "j =" + j);//8 9
//面试题1
int m = 1;
m = m++; //规则使用零食变量:temp = i; i = i + 1; i = tmep;
System.out.println(m); // 1
//面试题2
int i = 1;
i = ++i; //规则使用零时变量:i = i + 1; temp = i; i = temp
System.out.println(i);
}
}
关系运算符
逻辑运算符
ator {
public static void main(Stirng[] args){
//&& 和 &案例演示
int age = 50;
if (age > 20 && age < 90) {
System.out.println("ok100");
}
//& 逻辑与使用
if (age > 20 & age < 90) {
System.out.println("ok200");
}
//区别
int a = 4;
int b = 9;
//对于&&短路与来说,如果第一个条件为flase,后面的条件不再判断
//对于&逻辑与来说,如果第一个条件为flase,后面的条件还是判断
if (a < 1 && ++b < 50) {
System.out.println("ok300");
}
System.out.println("a = " + a + "b = " + b); //4 9
// if (a < 1 & ++b < 50) {
// System.out.println("ok300");
// }
// System.out.println("a = " + a + "b = " + b); //4 10
//&& 和 &案例演示
int age = 50;
if (age > 20 || age < 90) {
System.out.println("ok100");
}
//& 逻辑与使用
if (age > 20 | age < 90) {
System.out.println("ok200");
}
//区别
int a = 4;
int b = 9;
//对于||短路或来说,如果第一个条件为true,后面的条件不再判断
//对于&逻辑或来说,如果第一个条件为true,后面的条件还是判断
if (a < 1 || ++b < 50) {
System.out.println("ok300");
}
System.out.println("a = " + a + "b = " + b); //4 9
// if (a < 1 | ++b < 50) {
// System.out.println("ok300");
// }
// System.out.println("a = " + a + "b = " + b); //4 10
//!和^案例演示
//!操作是取反 T-》F,F->T
System.out.println(60 > 20); //T
System.out.println(!(60 > 20)); //F
//a^b: 叫逻辑异或,当a和b不同时为真,相同时为假
boolean b = (10 > 1) ^ (3 > 5);
System.out.println("b = " + b); //T
}
}
赋值运算符
public class AssignOperator {
public static void main(String[] args){
int n1 = 10;
n1 += 4; //n1 = n1 + n4
System.out.println(n1); //14
n1 /= 3;
// 赋值运算符的特点
// (1)运算顺序从右往左
// (2)赋值运算符的左边只能是变量,右边可以是变量、表达式、常量值
int num = 20;
int num2 = 78 * 34 - 10;
int num3 = a;
// (3)复合赋值运算符等价于
int a += 3; //a = a + 3;
// (4)复合赋值运算符会进行类型转换
byte b = 2;
b += 2; // 等价于 b = (byte)(b + 2);
b++; // b = (byte)(b + 1);
}
}
三元运算符
// 基本语法: 条件表达式?表达式1:表达式2;
// 运算条件:
// (1)如果条件为true,运算后结果为表达式1
// (2)如果条件为Flase,运算后结果为表达式2
public class TernaryOperator {
public static void main(String[] args) {
// int a = 10;
// int b = 99;
// int result = a > b ? a++ : b--;
// System.out.println("b = " + b + " result = " + result + " a =" + a); // 100 99 10
// // 三元运算符细节:
// //细节1:表达式1和表达式2为可以赋给接受变量的类型(为自动转换/或者强制转换)
// int a = 3;
// int b = 8;
// int c = a > b ? (int)1.1 : (int)3.4; //可以的
// double d = a > b ? a : b + 3; //可以的,满足int -> double
// //细节2:三元运算符可以转换为if -- else 语句
// //案例:实现三个数的最大值
// int n1 = 55;
// int n2 = 33;
// int n3 = 123;
// //思路:
// //1.先得到n1 和 n2 中最大数,保存到 max1
// //2.然后再求出max1 和 n3 中最大书,保存到max2
// int max1 = n1 > n2 ? n1 : n2;
// int max2 = max1 > n3 ? max1 : n3;
// System.out.println("最大数= " + max2);
// //使用一条语句
// int max = (n1 > n2 ? n1 : n2) > n3 ?
// (n1 > n2 ? n1 : n2) : n3
// System.out.println("最大数= " + max);
}
}
位运算
// 位运算public class BitOperator { public static void main(String[] args) { // >>,<<和>>>运算规则: //1.算术右移>>:低位溢出,符号位不变,并用符号位补溢出位 //2.算数左移<<:符号位不变,低位补0 //3.>>>逻辑右移,也叫无符号右移,运算规则:低位溢出,高位补0 System.out.println(1 >> 2); //0 System.out.println(1 << 2); //4 System.out.println(4 << 3); // 4* 2 * 2 *2 = 32 System.out.println(15 >> 2); //15 / 2 / 2 = 3 }}
运算符优先级
标识符命名的规则和规范
标识符的命名规则
- 由26个英文字母大小写组成,0-9,_和$组成
- 数字不能开头
- 不能使用关键字和保留字,但能包含关键字和保留字
- 区分大小写,长度无限制
- 标识符中不能含有空格
标识符命名规范
- 包名:多单词组成是所有字母都是小写,如:aaa.bbbb.ccc//com.hsp.crm
- 类名、接口名:多单词组成时,所有单词的首字母大写:XxxYyyZzz[大驼峰] 如:TankShotGame
- 变量名、方法名:多单词组成时,第一个单词首字母小写,,第二个单词开始每个单词首字母大写:xxxYyyZzz【小驼峰】如:thankShotGame
- 常量名:所有字母都是大写。多单词时每个单词用下划线连接:XXX_YYY_ZZZ 如:TAX_RATE
键盘输入
import java.util.Scanner; //表示把java.util下的Scanner类导入public class Input { public static void main(String[] args) { //演示接受用户的输入 //步骤 //1.引入/导入 Scanner类所在的包 //2.创建Scanner对象,new创建一个对象 //myScanner就是Scanner类的对象 Scanner myScanner = new Scanner(System.in); //3.接收用户输入,使用相关的方法 System.out.println("请输入名字"); String name = myScanner.next(); //接收用户输入 System.out.println("请输入年龄"); String age = myScanner.nextInt(); //接收用户输入 System.out.println("请输入收入"); String sal = myScanner.nextdouble(); //接收用户输入 System.out.println("人的信息如下" + "名字" + name + " 年龄:" + age + " 薪水" + sal); }}
原码、反码、补码
对于有符号的而言
- 二进制的最高位是符号位:0表示正,1表示负数
- 正数的原码、反码和补码都是一样
- 负数的反码 = 它的原码符号位不变,其他位取反
- 负数的补码 = 它的反码+1 ,负数的反码 = 负数的补码 - 1
- 0的反码,补码都是0
- Java没有无符号数,换言之,Java中的数都是有符号
- 计算机运算时,都是以补码的方式运算
- 当我们看运算结果时都是看原码
程序控制结构
顺序控制
分支控制
import java.util.Scanner;
public class Lf {
public static void main(String[] args) { // //(1)单分支 // Scanner myScanner = new Scanner(System.in); // System.out.println("请输入年龄:"); // int age = myScanner.nextInt(); // //使用if判断,输出对应信息 // if(age > 18) { // System.out.println("可以送入监狱"); // } // System.out.println("程序继续。。。") //(2)双分支 // Scanner myScanner = new Scanner(System.in); // System.out.println("请输入年龄:"); //int age = myScanner.nextInt(); //使用if判断,输出对应信息 //if(age > 18) { //System.out.println("可以送入监狱"); //}else { //System.out.println("你被放过了"); // } //System.out.println("程序运行");
//单分支和双分支练习题 //编写程序,声明2个double变量并赋值。 //判断第一个数大于10.0,且第二个数小于20.0,打印两数之和 // double d1 = 34.5; // double d2 = 2.6; // if (d1 > 10.0 && d2 < 20.0) { // System.out.println("两个数和= " + (d1 + d2)); // }
//定义两个变量int,判断两者的和,是否能被3又能被5整除,打印提示信息 // int num1 = 10; // int num2 = 20; // int sum = num2 + num1; // if(sum % 3 == 0 && sum % 5 == 0) { // System.out.println("和可以被整除"); // } else { // System.out.println("和不能被整除"); // }
//(3) 多分支 // boolean b = true; // if (b == flase) { //如果改成 b = flase 能通过编译吗,结果是 // System.out.println("a"); // }else { // if (b) { // System.out.println("b"); // }else { // if (!b) { // System.out.println("c"); // }else { // System.out.println("d"); // } // } // }
//(4)嵌套分支(不要超过三层) // Scanner myScanner = new Scanner(System.in); // System.out.println("请输入该歌手的成绩:"); // double score = myScanner.nextDouble(); // if (score > 8.0) { // System.out.println("请输入性别:"); // char gender = myScanner.next().charAt(0); //获取字符串第一个字符 // if (gender == '男') { // System.out.println("你进入男子组"); // } else { // System.out.println("你进入女子组"); // } // } else { // System.out.println("你被淘汰"); // }
//(5) switch 分支结构 // Scanner myScanner = new Scanner(System.in); // System.out.println("请输入一个字符(a-g)"); // char c1 = myScanner.next().charAt(0); // //在Java中,只要有值返回,就是一个表达式 // switch(c1) { // case 'a' : // System.out.println("今天是星期一"); // break; // case 'b' : // System.out.println("今天是星期二"); // break; // default: // System.out.println("字符输入错误"); // } // System.out.println("退出Switch");
// 细节1:表达式数据类型,应和case后的常量类型一样, //或者可以自动转成可以相互比较的类型,如输入的是字符,而常量是int
//细节2:switch(表达式)中,表达式的返回值必须是: //(byte,short,int,char,enum[枚举],String) //double c = 1.1; //switch(c) { //编译报错 // case 'a' : // System.out.println("ok1"); // break; // case 'b' : // System.out.println("ok2"); // break; // default : // System.out.println("ok3"); //}
//细节3:case语句中的值必须是常量(1,'a'),而不能是变量 //char c = 'a'; //char c2 = 'c'; //switch(c) { // case 'a' : // System.out.println("ok1"); // break; // case c2 : //编译报错 // System.out.println("ok2"); // break; // default : // System.out.println("ok3"); //}
//细节4:default子句是可选的,当没有匹配的case时,执行default
//细节5:break语句用来在执行完一个case分支后使程序跳出switch语句 //如果没有写break,程序会顺序执行到switch结尾// char c = 'a';// char c2 = 'c';// switch(c) {// case 'a' :// System.out.println("ok1");// //break;// case 'b' : // System.out.println("ok2"); //break;// default :// System.out.println("ok3");// } //结果为ok1 ok2 ok3}}
循环控制
public class CycleControl { public static void main(Stirng[] args) { //(1)for循环控制 for (int i = 1;i <= 10 ;i++ ) { System.out.println("Hello world" + i ); } //细节1:循环条件是返回一个布尔值的表达式 //细节2:for(;循环判断条件;)中的初始化和变量迭代 //可以写到其它地方,但是两边的分号不能省 //for (;i <= 10 ;) { // System.out.println("Hello world" + i ); // i++; // } //for (;;) { //表示无限循环 // System.out.println("无限循环"); //} //细节3:循环初始值可以有多条初始化语句,但要求类型一样,并且中间用逗号隔开 //循环变量迭代也可以有多条变量迭代语句,中间用逗号隔开 // int count = 3; // for (int i = 0,j = 0; i < count; i++,j +=2 ) { // System.out.println("i=" + i + " j=" + j); // } //(2)while //细节1:循环条件是返回一个布尔值的表达式 //细节2:while循环是先判断再执行语句 //(3)do while }}
break
continue
return
数组、排序和查找
数组
import java.util.Scanner;public class Array { public static void main(String[] args) { // 数组介绍:数组可以存放多个同一类型的数据。数组也是一种数据类型,是引用类型 //快速入门 // double[] hens = {3, 5, 1, 3.4, 2, 50}; // double totalWeight = 0; // System.out.println("数组的长度:" + hens.length); // for (int i = 0; i < hens.length; i++) { // System.out.println("第" + (i + 1) + "个元素的值" + hens[i]); // totalWeight += hens[i]; // } // System.out.println("总体重=" + totalWeight // + "平均体重=" + totalWeight/hens.length); //使用方式1-动态初始化 //数据类型 数组名[] = new 数据类型[大小] //步骤1:创建一个double数组,大小为5 // double scores[] = new double[5]; //步骤2:循环输入 // for (int i = 0; i < scores.length; i++ ) { // System.out.println("请输入第" + (i + 1) + "个元素的值"); // scores[i] = myScanner.nextDouble(); // } //输出,遍历数组 // for (int i = 0; i < scores.length; i++ ) { // System.out.println("第" + (i + 1) + "个元素的值" + scores[i]); // } //使用方法2-动态初始化 //步骤1:创建一个double数组,大小为5 // double scores[]; //double[] scores; 声明数组,这时scores是null // scores = new double[5]; //步骤2:循环输入 // for (int i = 0; i < scores.length; i++ ) { // System.out.println("请输入第" + (i + 1) + "个元素的值"); // scores[i] = myScanner.nextDouble(); // } //输出,遍历数组 // for (int i = 0; i < scores.length; i++ ) { // System.out.println("第" + (i + 1) + "个元素的值" + scores[i]); // } //使用方式3-静态初始化(知道具体数值) // double[] hens = {3, 5, 1, 3.4, 2, 50}; // double totalWeight = 0; // System.out.println("数组的长度:" + hens.length); // for (int i = 0; i < hens.length; i++) { // System.out.println("第" + (i + 1) + "个元素的值" + hens[i]); // totalWeight += hens[i]; // } // System.out.println("总体重=" + totalWeight // + "平均体重=" + totalWeight/hens.length); //细节1:数组是多个相同数据类型的组合 // int[] arr1 = {1, 2, 4, 2.4, "hello"}; //String ->int // double[] arr1 = {1, 2, 4, 2.4}; //int -> double //细节2:数组中元素可以是任何数据类型,包括基本类型和引用类型(String,数组,接口等),但是不能混用 String[] arr3 = {"北京","java"}; //细节3:数组创建后,如果没有赋值,有默认值 int-0,short-0 ,byte-0, float-0.0 //double-0.0, char- \u0000, boolean-false, String-null //细节4:使用数组的步骤 1.声明数组并开辟空间 2.给数组各个元素赋值 3.使用数组 //细节5:数组的下标是从0开始 //细节6:数组下标必须在指定范围内使用,否则报:下标越界异常 //细节7:数组属于引用类型,数组型数据是对象(Object) }}
数组赋值的机制、数组拷贝、数组添加
public class ArrayAssign { public static void main(String[] args) { //基本数据类型赋值,赋值方式为值拷贝 //n2的变化不会影响n1的值 // int n1 = 10; // int n2 = n1; // n2 = 80; // System.out.println("n1 = " + n1); //10 // System.out.println("n2 = " + n2); //80 //数组在默认的情况是引用传递,赋的值是地址,赋值方式为引用赋值,是一个地址 // int[] arr1 = {1, 2, 3}; // int[] arr2 = arr1; // arr2[0] = 10; // //看看arr1的值 // System.out.println("=====arr1的元素值======="); // for (int i = 0; i < arr1.length ; i++ ) { // System.out.println(arr1[i]); // } // //数组拷贝 // int[] arr1 = {10, 20, 30}; // //创建一个新的数组arr2,开辟新的数据空间,大小为arr1.length // int[] arr2 = new int[arr1.length]; // //遍历arr1,把每个元素拷贝到arr2对应元素的位置 // for (int i = 0; i < arr1.length ; i++ ) { // arr2[i] = arr1[i]; // } }}
二维数组的使用方式
public class TwoDimensionalArray { public static void main(String[] args) { //使用方式1:动态初始化 // int arr[][] = new int[2][3]; //使用方式2:动态初始化 // int arr[][]; //先声明二维数组 // arr = new int[2][3]; //再开空间 // //遍历arr数组 // for (int i = 0; i < arr.length ; i++ ) { // for (int j = 0; j < arr[i].length ; j++ ) { // System.out.print(arr[j][i] + " "); // } // System.out.println(); //换行 // } //使用方式3:动态初始化-列数不确定 // int[][] arr = new int[3][]; // // for (int i = 0 ; i < arr.length ; i++ ) { //遍历arr每个一维数组 // //给每个一维数组开空间 // //如果没有给一维数组new ,那么arr[i]就是null // arr[i] = new int[i + 1]; // //遍历一维数组,并给一维数组的每个元素赋值 // for (int j = 0; j < arr[i].length; j++) { // arr[i][j] = i + 1; // } // } //使用方式4:静态初始化 // int[][] arr = {{1,1,1},{8,8,0},{100}}; // //细节1:二维数组的声明方式:int[][] y 或者 int[] y[] 或者 int y[][] //细节2:二维数组实际上由多个一维数组组成,它的各个一维数组的长度可以相同也可以不同 }}
面向对象编程(基础部分)
类与对象
-
类是一种数据类型
-
对象就是一个具体的实例
public class Object { public static void main(String[] args) { //单独变量解决=> 不利于数据的管理(把一只猫的信息拆解) //第一只猫的信息 // String cat1Name = "小白"; // int cat1Age = 3; // String cat1Color = "白色"; // //第二只猫的信息 // String cat2Name = "小花"; // int cat2Age = 100; // String cat2Color = "花色"; //使用OOP面向对象解决 //实例化一只猫 // Cat cat1 = new Cat(); // cat1.Name = "小白"; // cat1.Age = 19; // cat1.Color = "白"; // //怎么访问对象的属性 // System.out.pirntln("第一只猫信息" + cat1.Name + " " // + cat1.Age + " " + cat1.Color) //如何创建对象 //1.先声明再创建 //Cat cat; // cat = new Cat(); //2.直接创建 //类与对象的内存分配机制 // 1.栈:一般存放基本数据类型(局部变量) // 2.堆:存放对象(Cat,cat,数组等) // 3.方法区:常量池(常量,比如字符串),类加载信息 // Cat cat1 = new Cat(); //先加载Cat类信息,(方法和属性信息,只会加载一次) // cat1.Age = 10; // cat1.Name = "傻猫"; //数据存放在方法区的常量池中,地址放在堆中 // Cat cat2 = cat1; //把cat1赋给cat2,让cat2指向cat1 // System.out.pirntln(cat1.Age); }}//使用面向对象的方式来解决养猫问题//定义一个猫类Cat->自定义的数据类型// class Cat {// //属性 成员变量 字段// //属性可以是基本数据类型,也可以是引用类型// //细节1:属性的定义语法同变量,如: 访问修饰符(public,proctected,默认,private) 属性类型 属性名// //细节2:属性的定义类型可以是任意类型,包含基本类型或引用类型// //细节3:属性如果不赋值,有默认值,规则和数组一致,如:[Person类]// String Name; // int Age;// String Color;// double Weight;// //行为// }
成员方法
-
方法的调用机制分析
方法调用小结:
- 当程序执行到方法时,就会开辟一个独立的空间(栈空间)
- 当方法执行完毕后,或者执行到return语句,就会返回
- 返回到调用方法的地方
- 返回后,继续执行方法后面的代码
- 当main方法执行完毕,整个程序退出
成员方法传参机制
public class Method { public static void main(String[] args) { // //方法使用 // Person p1 = new Person(); // p1.speak(); //调用方法 // p1.cal01(); // p1.cal02(); // System.out.println(p1.getSum(10,29)); //编写copyPerson,可以赋值一个person对象,返回复制对象,克隆对象 //注意要求得到新对象和原来的对象是两个独立的对象,只是属性相同 Person p = new Person(); p.name = "milan"; p.age = 100; MyTools tools = new MyTools(); Person p2 = tools.copyPerson(p); //p和p2是person对象,但是是两个独立对象 //可以进行比较 System.out.println(p == p2); }}class Person { String name; int age; //方法(成员方法) public void speak() { System.out.println("我是一个好人"); } public void cal01() { int res = 0; for (int i = 0; i <= 1000; i++) { res += i; } System.out.println("计算结果:" + res); } public void cal02(int n) { int res = 0; for (int i = 0; i <= n; i++) { res += i; } System.out.println("cal02计算结果:" + res); } public int getSum(int num1, int num2) { int res = num1 + num2; return res; } //方法的使用细节 - 返回类型 //细节1:一个方法最多有一个返回值,数组除外 //细节2:返回类型可以为任意类型,包含基本类型或引用类型(数组,对象) //细节3:如果方法要求有返回数据类型,则方法体中最后的执行语句为return 值; //并且要求返回数据类型必须和return 的类型一致或兼容 //细节4:如果方法是void,则方法体中可以没有return 语句,或者只写return; //方法的使用细节 - 形参列表 //细节1:调用带参数的方法,一定对应着参数列表传入相同类型或者兼容类型的参数 //方法使用细节 - 方法体 //细节1: 方法不能嵌套定义}class MyTools { public Person copyPerson(Person p) { Person p2 = new Person(); p2.name = p.name; //把原来对象的名字赋给p2.name p2.age = p.age; return p2; }}
-
方法的递归
public class Recurison { public static void main(String[] args) { T t1 = new T(); t1.test(4); //递归的规则 //1.执行一个方法时,就创建一个新的受保护的独立空间(栈空间) //2.方法的局部变量时独立的,不会相互影响 //3.如果方法中使用的是引用变量(如数组,对象),就会共享该引用类型的数据 //4.递归必须向推出递归的体哦阿健逼近,否则就是无限递归 //5.当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁 // //桃子问题: // int day = 1; // int peachNum = t1.peach(day); // if (peachNum != -1) { // System.out.println("第 " + day + "天有" + peachNum + "个桃子"); // } //迷宫问题: //1.先创建迷宫,用二维数组表示 //2.先规定map数组元素值,0表示可以走,1表示障碍物 // int[][] map = new int[8][7]; // //3.将最上面的一行和最下面的一行,全部设置为1 // for (int i = 0; i < 7; i++) { // map[0][i] = 1; // map[7][i] = 1; // } // //4.将最右和最左设置为1 // for (int i = 0; i < 7; i++) { // map[i][0] = 1; // map[i][6] = 1; // } // map[3][1] = 1; // map[3][2] = 1; // //输出当前地图 // for (int i = 0; i < map.length; i++) { // for (int j = 0; j < map[i].length; j++) { // System.out.print(map[i][j] + "") // } // System.out.println(); // } // //使用findWay给老鼠找路 // T t1 = new T(); // t1.findWay(map,1,1); // System.out.println("====找路的情况===="); // for (int i = 0; i < map.length; i++) { // for (int j = 0; j < map[i].length; j++) { // System.out.print(map[i][j] + "") // } // System.out.println(); // } //汉诺塔问题:// T tower = new T();// tower.move(2,'A','B','C'); //八皇后问题: int[][] map = new int[8][8]; for (int i = 0; i < map.length; i++) { for (int j = 0; j < map[i].length; j++) { map[i][j] = 0; } } }}class T { public void test(int n) { if (n > 2) { test(n - 1); } System.out.println("n=" + n); }// //猴子吃桃问题:// //思路分析:前一天的桃子 = (后一天的桃子 + 1) * 2// public int peach(int day) {// if (day == 10) { //第十天,只有一个桃子// return 1;// } else if (day >= 1 && day <= 9) {// return (peach(day + 1) +1) * 2;// } else {// System.out.println("day在1-10");// return -1;// }// } //迷宫问题:使用递归回溯思想来解决 //1.findWay方法就是专门来找出迷宫路径 //2.找到返回true,否则返回false //3.map就是二维数组,表示迷宫 //4.i,j就是老鼠的位置,初始化为(1,1) //5.因为是递归找路0 表示可以走 ,1表示障碍物,2表示可以走通,3表示走过,但是走不通是死路 //6.当map[6][5] = 2 就说明找到通路,就可以结束,否则继续找 //7.先确定老鼠找路策略 下 - 右 - 上 - 左 // public boolean findWay(int[][] map, int i, int j) { // if(map[6][5] == 2) { //说明找到 // return true; // } else { // if (map[i][j] == 0) { //当前这个位置0,说明表示可以走 // //我们假定可以走通 // map[i][j] = 2; // //使用找路策略,来确定该位置是否真的可以走通 // if (findWay(map,i + 1, j)) { //下 // return true; // } else if (findWay(map, i, j+1)) { //右 // return true; // }else if (findWay(map,i - 1, j)) { //上 // return true; // }else if (findWay(map, i, j -1)) { //左 // return true; // } else { // map[i][j] = 3; // return false; // } // } else { //map[i][j] = 1,2,3 // return false; // } // } // } //汉诺塔问题: // public void move(int num, char a ,char b ,char c) { // if(num == 1) { // System.out.println(a + "->" +c); // } else { // //如果有多个盘,可以看成两个,最下面和上面所有盘(num - 1) // //(1)先移动所有盘到b,借助c // move(num - 1, a, c, b); // // (2)把最下面的这个盘移动到c // System.out.println(a + "->" + c); // //(3)再把b塔的所有盘移动到c,借助a // move(num - 1, b, a, c); // } // } //八皇后问题: public void put(int[][] map, int row , int list) { if (map[row][list] == 0) { map[row][list] = 1; } put(map,row + 1, list); } }
重载
public class Overload { public static void main(String[] args) { MyCalculator mc = new MyCalculator(); System.out.println(mc.calculate()); } }class MyCalculator { //两个整数的和 public int calculate(int n1, int n2){ return n1 + n2; } public double calculate(int n1 ,int n2) { return n1 + n2; } public double calculate(double n1, int n2) { return n1 + n2; } public int calculate(int n1, int n2, int n3) { return n1 + n2 + n3; } //方法重载细节: //细节1:方法名相同 //细节2:形参列表不同(形参类型或者个数不同,至少有一样不同,参数名无要求) //细节3:返回类型:无要求}
可变参数
//可变参数案例public class VarParameter { public static void main(String[] args) { //案例演示 }}class HspMethod { public int sum(int n1, int n2) { return n1 + n2; } public int sum(int n1, int n2, int n3){ return n1 + n2 + n3; } public int sum(int n1, int n2, int n3, int n4) { return n1 + n2 + n3 + n4; } //上面三个方法名称相同,功能相同,参数个数不同 //1.int... 表示接受的是可变参数,类型是int,即接受多个int //2.使用可变参数,将nums当作数组 public int sum(int... nums) { int res = 0; for ( int i = 0; i < nums.length; i++) { res += nums[i]; } return res; } //注意事项和细节 // 1.可变参数的实参可以为数组 // 2.可变参数的本质是数组 // 3.可变参数和普通类型的参数一起放在形参列表,但是可变参数在最后 // 4.一个新参列表中只能有一个可变参数 // 5.可变参数的实参可以为0个或多个}
作用域
-
全局变量(属性)可以不赋值,直接使用,因为有默认值
-
局部变量必须赋值后,才能使用,因为没有默认值
-
注意事项和细节使用
-
属性和局部变量可以重名,访问时遵循就近原则
-
在同一个作用域中,比如同一个成员方法中,两个变量不能够重名
-
属性生命周期较长,伴随对象的创建而创建,伴随着对象销毁而销毁。局部变量生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而销毁。
-
作用范围不同:
全局变量/属性:可以被本类使用,或其它类使用(通过对象调用)
局部变量:只能在本类中对应的方法中使用
-
修饰符不同
全局变量/属性可以加修饰符
局部变量不能加修饰符
-
构造器
-
基本语法
[修饰符] 方法名(形参列表) {
? 方法体;
- 修饰符可以默认,也可以是其他三种
- 构造器没有返回值
- 方法名和类名相同
- 参数列表和成员方法一样的规则
- 构造器的调用由系统完成
public class Constructor { public static void main(String[] args) { //我们new一个对象时,直接用构造器指定名字和年龄 Person p1 = new Person("smith", 80); }}class Person { String name; int age; //构造器 // 1.构造器没有返回值,也不能写void // 2.构造器的名称和类Person一样 // 3.(String pName, int pAge)是构造器形参列表,规则和成员方法一样 public Person(String name, int pAge) { System.out.println("构造器被调用————完成对象的初始化") name = pName; age = pAge; }}
-
注意事项和使用细节
public class ConstructorDetail { public static void main(String[] args) { Person p1 = new Person("king", 40); Person p2 = new Person("shaigou"); }}class Person { String name; int age; // 细节1:一个类可以定义多个不同的构造器,即构造器重载 // 第一个构造器 public Person(String pName, int pAge) { name = pName; age = pAge; } // 第2个构造器 public Person(String pName) { name = pName; } //细节2:构造器名和类名相同 // 细节3:构造器没有返回值 // 细节4:构造器是完成对象的初始化,并不是创建对象 // 细节5:在创建对象是,系统自动调用该类的构造方法 // 细节7:一旦定义了自己的构造器,默认构造器就覆盖了,就不能再使用默认无参构造器 // 除非显式的定义一下,如:Person() {}}class Dog { // 细节6:如果没有定义构造器,系统会自动給类生成一个默认无参构造器(也叫默认构造器) // 比如Dog(){},使用javap指令,反编译查看}
-
对象流程分析
- 加载Person类信息(Person.class),只会加载一次
- 在堆中分配空间(地址)
- 完成对象初始化[3.1默认初始化 age = 0 name = null 3.2显式初始化 age=90, name = null, 3.3构造器的初始化 age = 20, name = 小倩]
- 将对象在堆中的地址,返回给p(p是对象名,也可以理解为对象的引用)
this
当前对象的属性
-
注意事项和细节
public class This { public static void main(String[] args) { // 细节1:可以用来访问本类的属性、方法、构造器 // 细节2:用于区分当前类的属性和局部变量 // T t1 = new T(); // t1.f2(); }}class T { // 细节4:访问构造器:this(参数列表);注意只能在构造器中使用(只能在 // 构造器中访问另一个构造器 // 注意:必须放在第一条语句 public T() { this("jack",19); System.out.println("T() 构造器"); // 这里去访问第二种构造器 } public T(String name, int age) { System.out.println("第二种 构造器"); } //细节3:访问成员方法的语句:this.方法名(参数列表) public void f1() { System.out.println("f1() 方法"); } public void f2() { System.out.println("f2() 方法"); // 调用本类的f1 // 第一种方式 f1(); // 第二种方式 this.f1(); } // 细节5:this不能在类定义外部使用,只能在类定义的方法中使用}
Intellij IDEA
快捷键(ecplise)
- 删除当前行:ctrl + D
- 复制当前行:ctrl + alt + 向下光标
- 补全代码:alt + /
- 添加注释:ctrl + /
- 导入该行需要的类,先配置auto import,然后使用shift + alt + enter
- 快速格式化代码:ctrl + shift + F /ctrl + alt + L
- 快速運行程序 shift + F10
- 快速生成构造器:alt + insert
- 可以查看类的层级(继承)关系: ctrl + h
- 将光标放在一个方法上,输入ctrl + b,可以定位到方法
- 自动的分配变量名,通过在后面.var
模板
包
-
基本语法
package com.hspedu
-
包的本质
包的本质就是创建不同的文件夹/目录来保存类文件
-
快速入门
-
包的命名规则:
只能包含数字、字母、下划线、小圆点,但不能用数字开头,不能是关键字或保留字
-
命名规范:
一般是小写字母 + 小圆点
com.公司名.项目名.业务模块 如:
com.sina.crm.user //用户模块
-
常用包
java.lang.* :默认引用,不用再引用
java.util.* :系统提供的工具包,工具类,使用Scanner
java.net.* :网络包,网络开发
java.awt.* :做java界面开发,GUI
-
如何引用包
package com.hspedu.pkg;import java.util.Arrays;// 最好需要什么类,就导入那个类中包//import java.util.Scanner; //表示只引入java.util包下的Scanner//import java.util.*; //表示将包下所有类导入public class Import01 { public static void main(String[] args) { //使用系统提供的Arrays完成数组排序 int[] arr = {-1, 20 , 12, 30}; //比如对其进行排序 //传统方法是自己编写 //系统提供相关的类,Arrays Arrays.sort(arr); //输出排序结果 for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } }}
- 注意事项和使用细节
//细节1:package的作用是声明当前类所在的包,需要放在类的最上面,一个类中最多有一句packagepackage com.hspedu.pkg;//细节2:import指令放在package下面,在类定义之前public class PkgDetail { public static void main(String[] args) { }}
访问修饰符
基本介绍
使用注意事项
-
修饰符可以用来修饰类中的属性,成员方法
-
只有默认的和public才能修饰类,并且遵守以上权限
-
成员方法的访问规则和属性一样
封装
封装的理解和好处
- 隐藏实现细节
- 可以对数据进行验证,保证安全合理
封装实现步骤
- 将属性私有化private
- 提供一个public set方法,用于对属性判断和赋值
- 提供一个public get方法,用于获取属性的值
-
快速入门
package com.hspedu.encap;public class Encapsulation01 { public static void main(String[] args) { //第一次使用鼠标点击形式运算程序,后面可以用快捷键 Person person = new Person(); person.setName("jack"); person.setAge(20); person.setSalary(3000); System.out.println(person.info()); }}class Person { public String name; private int age; private double salary; public void setAge(int age) { //判断 if (age >= 1 && age <= 120) { this.age = age; } else { System.out.println("你设置的年龄不对,给出默认年龄18"); this.age = 18; } } public void setName(String name) { //加入对数据的校验 if (name.length() >= 2 && name.length() <= 6) { this.name = name; }else { System.out.println("名字长度不对"); this.name = "无名人"; } } public void setSalary(double salary) { this.salary = salary; } public double getSalary() { //可以增加对当前对象的权力判断 return salary; } public int getAge() { return age; } public String getName() { return name; } public String info() { return "姓名:" + name + "年龄:" + age + "收入" + salary; }}
继承(代码复用性)
基本语法
class 子类 extends 父类 {
- 子类会自动拥有弗雷定义的属性和方法
- 父类又叫超类,基类
- 子类又叫派生类
细节问题
-
子类继承了所有的属性和方法,但是私有属性和方法不能在子类直接访问,要通过公共方法访问
-
子类必须调用父类的构造器,完成父类的初始化
-
当创建子类对象时,不管使用子类那个构造器,默认情况下总会调用父类无参构造器,如果父类没有提供无参构造器,必须在子类的构造器中用super去指定使用父类哪个构造器完成对父类的初始化工作,否则编译不会通过
-
如果希望指定去调用父类的某个构造器,则显示的调用一下
-
super在使用时,需要放在构造器第一行
-
super()和this()都只能放在构造器的第一行,因此这两种方法不能共存在一个构造器中
-
Java所有的类都是Object类
-
父类构造器的调用不限于直接父类,将一直往上追溯到Object类
-
子类最多只能继承一个父类,即Java中的单继承机制
-
不能滥用继承,子类和父类之间必须满足is-a的逻辑关系
super关键字
便利与细节
- 调用父类构造器的好处:分工明确,父类属性有父类初始化,子类属性有子类初始化
- 当子类中有和父类的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名时,使用super,this,直接访问都是一样的。
- super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;如果多个基类中都有同名的成员,使用super访问遵循就近原则。A->B -> C
super和this的区别
方法重写
?
注意事项和使用细节
- 子类的方法的参数,方法名称,要和父类方法的参数,方法名称完全相同
- 子类方法的返回类型和父类方法的返回类型一样,或者是父类返回类型的子类,如:父类 返回类型是Object,子类方法返回类型是String
- 子类方法不能缩小父类方法的访问权限
package com.hspedu.override_;public class Person { private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public int getAge() { return age; } public String getName() { return name; } public String say() { return "名字:" + getName() + " 年龄:" + getAge(); }}package com.hspedu.override_;public class Student extends Person { private String id; private String score; public Student() { } public Student(String id, String score) { this.id = id; this.score = score; } public Student(String name, int age, String id, String score) { super(name, age); this.id = id; this.score = score; } public String getId() { return id; } public String getScore() { return score; }//// public String say() {////// return "name:" + getName() + " age:" + getAge()////// + " id:" + getId() + " score:" + getScore();// } public String say() { return super.say() + " id:" + getId() + " score:" + getScore(); }}package com.hspedu.override_;public class OverrideExercise { public static void main(String[] args) { Person person = new Person(); Student student = new Student("傻狗", 10, "0119", "100"); System.out.println(student.say()); System.out.println(person.say()); }}
面向对象编程 - 多态
基本介绍
方法或对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承的基础之上
多态的具体体现
-
方法的多态
-
方法的重载
-
方法的重写
-
-
对象的多态
(1)一个对象的编译类型和运行类型可以不同
(2)编译类型在定义对象时,就确定了,不能改变
(3)运行类型可以变化
(4) 编译类型看定义时 = 号的左边,运行类型看 = 号的右边
多态注意事项和细节讨论
多态的前提是:两个对象(类)存在继承关系
-
多态的向上转型
1)本质:父类的引用指向了子类的对象
2)语法:父类编译 引用名 = new 子类类型
3)特点:编译类型看左边,运行类型看右边。
? 可以调用父类中的所有成员(需要遵守访问权限)
? 不能调用子类中特有成员
? 最终运行效果看子类的具体实现
-
多态的向下转型
1)语法: 子类类型 引用名 = (子类类型) 父类引用
2) 只能前传父类的引用,不能强转父类的对象
3)要求父类的引用必须指向的是当前目标类型的对象
4)可以调用子类类型中所有的成员
-
属性没有重写之说,属性的值看编译类型
-
instanceof 比较符,用于判断对象的运行 类型是否为xx类型或xx类型的子类型
Java的动态绑定机制(非常重要)
- 当调用对象方法的时候,该方法和该对象的内存地址/运行类型绑定
- 当调用对象属性时,没有动态绑定机制,哪里声明,那里使用
多态的应用
- 多态数组:数组的定义为父类类型,里面保存的实际元素类型为子类类型
package com.hspedu.poly_.polyarr;public class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String say() { return "name = " + this.name + "age = " + this.age; }}package com.hspedu.poly_.polyarr;public class Student extends Person{ private double score; public Student(String name, int age, double score) { super(name, age); this.score = score; } public double getScore() { return score; } public void setScore(double score) { this.score = score; } //重写父类的say @Override public String say() { return super.say() + "score = " + score; }}package com.hspedu.poly_.polyarr;public class Teacher extends Person{ private double salary; public Teacher(String name, int age, double salary) { super(name, age); this.salary = salary; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } @Override public String say() { return super.say() + "salary = " + salary; }}package com.hspedu.poly_.polyarr;public class PolyArray { public static void main(String[] args) { Person[] persons = new Person[5]; persons[0] = new Person("jack",20); persons[1] = new Student("jack", 18, 100); persons[2] = new Student("saith", 19, 30.1); persons[3] = new Teacher("scott", 30, 20000); persons[4] = new Teacher("king", 50, 25000); //循环调用多态数组 for (int i = 0; i < persons.length; i++) { //编译类型是person,运行类型根据实际情况 System.out.println(persons[i].say()); } }}
package com.hspedu.poly_.polyarr;public class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String say() { return "name = " + this.name + "age = " + this.age; }}package com.hspedu.poly_.polyarr;public class Student extends Person{ private double score; public Student(String name, int age, double score) { super(name, age); this.score = score; } public double getScore() { return score; } public void setScore(double score) { this.score = score; } //重写父类的say @Override public String say() { return super.say() + "score = " + score; } public void study() { System.out.println("学生 " + getName() + "正在上课"); }}package com.hspedu.poly_.polyarr;public class Teacher extends Person{ private double salary; public Teacher(String name, int age, double salary) { super(name, age); this.salary = salary; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } @Override public String say() { return super.say() + "salary = " + salary; } public void teach() { System.out.println("老师 " + getName() + "正在上课"); }}package com.hspedu.poly_.polyarr;public class PolyArray { public static void main(String[] args) { Person[] persons = new Person[5]; persons[0] = new Person("jack", 20); persons[1] = new Student("jack", 18, 100); persons[2] = new Student("saith", 19, 30.1); persons[3] = new Teacher("scott", 30, 20000); persons[4] = new Teacher("king", 50, 25000); //循环调用多态数组 for (int i = 0; i < persons.length; i++) { //编译类型是person,运行类型根据实际情况 System.out.println(persons[i].say()); if (persons[i] instanceof Student) { Student student = (Student) persons[i]; //向下转型 student.study(); // ((Student)persons[i]).study(); } else if (persons[i] instanceof Teacher) { ((Teacher) persons[i]).teach(); } else { System.out.println("你的类型有误,请检测"); } } }}
- 多态参数:方法定义的形参类型为父类类型,实参类型允许为子类类型
package com.hspedu.poly_.polyparamater;public class Employee { private String name; private double salary; public Employee(String name, double salary) { this.name = name; this.salary = salary; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } //得到年工资的方法 public double getAnnual() { return 12 * salary; }}package com.hspedu.poly_.polyparamater;public class Worker extends Employee{ public Worker(String name, double salary) { super(name, salary); } public void work() { System.out.println("普通员工 " + getName() + "正在工作"); } @Override public double getAnnual() { return super.getAnnual(); }}package com.hspedu.poly_.polyparamater;public class Manager extends Employee{ private double bonus; public Manager(String name, double salary, double bonus) { super(name, salary); this.bonus = bonus; } public double getBonus() { return bonus; } public void setBonus(double bonus) { this.bonus = bonus; } public void manage() { System.out.println("经理"+ getName() + "正在工作"); } @Override public double getAnnual() { return super.getAnnual() + bonus; }}package com.hspedu.poly_.polyparamater;public class PolyParamater { public static void main(String[] args) { Worker tom = new Worker("tom", 2400); Manager milan = new Manager("milan", 5000, 2000); PolyParamater polyParamater = new PolyParamater(); polyParamater.showEmpAnnual(tom); polyParamater.showEmpAnnual(milan); } public void showEmpAnnual(Employee e) { System.out.println(e.getAnnual()); //动态绑定 } public void testWork(Employee e) { if (e instanceof Worker) { ((Worker)e).work(); //向下转型 } else if (e instanceof Manager) { ((Manager) e).manage(); } else { System.out.println("death man"); } }}
Object类详解
equal方法
== 和equals的对比
- == :可以判断基本类型,又可以判断引用类型
- == :如果判断基本类型,判断的是值是否相等
- == :如果判断引用类型,判断的是地址是否相等,即判断是不是同一个对象
- equals:是Object类中的方法,只能判断引用类型
- 默认判断的是地址是否相等,子类(String,Integer等)中往往重写该方法,用于判断内容是否相等
hashCode() 方法
-
提高具有哈希结构的效率
-
两个引用,如果指向是同一个对象,则哈希值是相同的
-
两个引用,如果指向是不同对象,则哈希值是不同的
-
哈希值主要是根据地址号来的,不能完全将哈希值等价于地址
package com.hspedu.hashcode;import com.hspedu.modifier.A;public class HashCode_ { public static void main(String[] args) { A aa = new A(); A aa2 = new A(); A aa3 = aa; System.out.println("aa.hashcode(): " + aa.hashCode()); System.out.println("aa2.hashcode(): " + aa2.hashCode()); System.out.println("aa3.hashcode(): " + aa3.hashCode()); }}
toString方法
基本介绍
默认返回:全类名 + @ + 哈希值的十六进制,【查看Object的toString方法】,子类往往重写toString方法,用于返回对象的属性信息
当直接输出一个对象时,toString方法会被默认的调用,比如System.out.println(monster),默认调用toString()方法
package com.hspedu.hashcode;public class ToString_ { public static void main(String[] args) { /* Object的toString()源码 //getClass().getName()类的全类名(包名+类名) //Integer.toHexString(hashCode()) 将对象hashCode值转成16进制字符 public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } */ Monster monster = new Monster("妖怪", "巡山", 1000); System.out.println(monster.toString() + "hashcode:" + monster.hashCode()); System.out.println("===当直接输出一个对象时,toStirng方法会默认被调用==="); System.out.println(monster); }}class Monster { private String name; private String job; private double sal; public Monster(String name, String job, double sal) { this.name = name; this.job = job; this.sal = sal; } //重写toString方法,处处对象的属性 //使用快捷键即可alt + insert -> toString @Override public String toString() { //重写后一般是吧对象的属性值输出 return "ToString_{" + "name='" + name + '\'' + ", job='" + job + '\'' + ", sal=" + sal + '}'; }}
finalize()方法
- 当对象被回收时,系统自动调用该对象的finalize方法。子类可以重写该方法做一些释放资源的操作
- 什么时候回收:当某个对象没有任何引用时,jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁对象前,调用finalize方法
- 垃圾回收机制的调用,是由系统决定的,也可以通过System.gc()主动触发垃圾回收机制
package com.hspedu.hashcode;public class Finalize { public static void main(String[] args) { Car car = new Car("宝马"); car = null; //这时car对象就是一个垃圾,垃圾回收器就会回收对象, //在销毁对象前,会调用finalize方法程序员可以在finalize中, //写自己的业务逻辑代码,如:释放资源,数据库连接,或者打开文件 //如果补充些finalize,那么就会调用Object类的finalize,即默认处理 System.gc();//主动调用垃圾回收器 可能会成功 System.out.println("程序退出、、、、"); }}class Car { private String name; public Car(String name) { this.name = name; } //重写finalize @Override protected void finalize() throws Throwable { System.out.println("我们销毁汽车" + name); System.out.println("释放了一些资源"); }}
断点调试
提示:在断点调试中,是运行状态,是以对象的运行类型来执行的。
快捷键
F5:跳入方法内
F6:逐行执行代码
shift + F7:跳出方法
F8:resume,执行到下一个断点
package com.hspedu.smallchange.oop;import java.text.SimpleDateFormat;import java.util.Date;import java.util.Scanner;/** * 该类时完成零钱通的各个功能的类 * 使用oop(面向对象编程) * 将各个功能对应一个方法 */public class SmallChangeOOP { //属性 //定义相关变量 boolean loop = true; Scanner scanner = new Scanner(System.in); String key = ""; //2.完成零钱通明细 //(1)可以把收益入账和消费,保存到数组(2)可以使用对象(3)可以使用String拼接 String details = "---------零钱通明细----------"; //3.完成收益入账 完成功能驱动程序员增加新的变化和代码 double money = 0; double balance = 0; Date date = null; //date是java.util.Date类型,表示日期 SimpleDateFormat sdf = new SimpleDateFormat("yyy-MM-dd HH:mm"); //可以用于日期格式化 //4.消费 //定义消费的原因 String note = ""; //先完成显示菜单,并可以选择 public void mainMenu() { do { System.out.println("\n=======零钱通菜单========"); System.out.println("\t\t\t1 零钱通菜单"); System.out.println("\t\t\t2 收益入账"); System.out.println("\t\t\t3 消费"); System.out.println("\t\t\t4 退出"); System.out.println("请选择(1-4)"); key = scanner.next(); //使用switch分支控制 switch (key) { case "1" : this.detail(); break; case "2" : this.income(); break; case "3" : this.pay(); break; case "4" : this.exit(); break; default: System.out.println("选择有误,请重新选择"); } }while (loop); } //完成零钱通明细 public void detail() { System.out.println(details); } //完成收益入账 public void income() { System.out.print("收益入账金额"); money = scanner.nextDouble(); //money的值范围应该校验 //思路:找出不正确的金额条件,然后给出提示,就直接break// if(money <= 0) {// System.out.println("收益入账金额需要大于0");// break;// } //找出正确金额的条件 if(money > 0) { balance += money; //拼接收益入账信息到details date = new Date(); //获取当前日期 details += "\n收益入账\t" + money + "\t" + sdf.format(date) + "\t" + balance; } else { System.out.println("收益入账金额要大于0"); return; } return; } //完成消费 public void pay() { System.out.print("消费金额:"); money = scanner.nextDouble(); //money的值范围要校正 //找出金额不正确的情况 if (money <= 0 || money > balance) { System.out.println("你的消费金额应该在 0-" + balance); return; } System.out.print("消费说明:"); note = scanner.next(); balance -= money; date = new Date(); //获取当前信息 details +="\n" + note + "\t\t-" + money + "\t" + sdf.format(date) + "\t" + balance; return; } //退出 public void exit() { //(1)定义一个变量choice,接收用户的输入 //(2)使用while + break,来处理接收到的输入 //(3)退出while后,再判断choice是y还是n,就可以决定是否退出 //(4)建议一段代码只实现一个小功能,尽量不要混在一起 String choice = ""; while (true) { System.out.println("你确定要退出吗? y/n"); choice = scanner.next(); if ("y".equals(choice) || "n".equals(choice)){ break; } } //当用户退出while,进行判断 if (choice.equals("y")) { loop = false; } return; }}package com.hspedu.smallchange.oop;import com.hspedu.smallchange.SmallChangeSys;/** * 这里我们直接调用SmallChangeSysOOP对象,显示主菜单即可 */public class SmallChangeSysApp { public static void main(String[] args) { new SmallChangeOOP().mainMenu(); }}
类变量和类方法
定义语法
访问修饰符 static 数据类型 变量名;
static 访问修饰符 数据类型 变量名;
访问方法
类名.类变量名
对象名.类变量名【静态变量的访问修饰符的访问权限和范围和普通属性是一样的】
类变量使用注意事项和细节讨论
-
什么时候需要用类变量
当我们要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量),如:定义学生类,统计所有学生共交多少钱
-
类变量与实例变量(普通属性)区别
类变量是该类的所有对象共享的,而实例变量是每个对象共享的
-
加上static称为类变量或静态变量,否则称为实例变量/普通变量/非静态变量
-
类变量可通过 类名.类变量名 或者 对象名.类变量名 来访问,但Java设计者推荐我们使用 类名.类变量名方式访问
-
实例变量不能通过 类名.类变量名 访问
-
类变量是类加载时就初始化了,也就是说,即使没有创建对象,只要类加载,就可以使用类变量
-
类变量的生命周期是随类的加载开始,随着类消亡而销毁
类方法基本介绍
访问修饰符 static 数据返回类型 方法名(){}
static 访问修饰符 数据返回类型 方法名(){}
类方法的调用
使用方式:类名.类方法名 或者 对象名.类方法名 【前提是满足访问修饰符的权限和范围】
类方法使用的注意事项和细节讨论
-
类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区
类方法中无this的参数
普通方法中隐含this的参数
-
类方法可以通过类名调用,也可以通过对象名调用
-
普通方法和对象有关,需要通过对象名调用,比如对象名.方法名(参数),不能通过类名调用
-
类方法中不允许使用和对象有关的关键字,比如this和super。普通方法(成员方法)可以
-
类方法(静态方法)中只能访问 静态变量或静态方法。
-
普通成员方法,既可以访问 普通方法(方法),也可以访问静态成员和非静态成员(必须遵守访问权限)
理解mian方法语法
- java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public
- java虚拟机在执行main()方法是不必创建对象,所以该方法必须是static
- 该方法接收String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数
- java执行的程序 参数1 参数2 参数3
特别提示:
- 静态方法main可以访问本类的静态方法
- 静态方法main不能访问本类的非静态方法
代码块
基本语法
[修饰符] {
? 代码;
};
说明:
- 修饰符可选,要写的话,只能写static
- 代码块分为两类,使用static修饰的叫静态代码块,没有static修饰的,叫普通/非静态代码块
- 逻辑语句可以为任何逻辑语句
- ;可以写,也可以省略
package com.hspedu.codeblock_;
public class CodeBlock {
public static void main(String[] args) {
new Movie("哈");
Movie("姆", 120)
}
}
class Movie {
private String name;
private double price;
private String director;
//下面三个构造器中有相同的语句
//可以把相同的语句放到一个代码块中
//不管调用哪个构造器,创建对象,都会先调用代码块内容
//代码块调用顺序先与构造器
{
System.out.println("广告结束");
System.out.println("电影结束");
}
public Movie(String name) {
this.name = name;
}
public Movie(String name, double price) {
this.name = name;
this.price = price;
}
public Movie(String name, double price, String director) {
this.name = name;
this.price = price;
this.director = director;
}
}
注意事项和细节讨论
-
static代码块叫做静态代码块,作用是对类进行初始化,并且随着类的加载而执行,并且只会执行一次。而普通代码块,每创建一个对象,就执行
-
类什么时候被加载
-
创建子类对象实例时
-
创建子类对象实例,父类也会被加载,而且父类先被加载,子类后被加载
-
使用类的静态成员(静态属性,静态方法)
-
-
普通代码块,在创建对象实例时,会被隐式调用。被创建一次,就会被调用一次。如果只使用类的静态成员时,普通代码块不会被执行。
-
创建一个对象时,在一个类调用顺序是:
- 调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多尔静态变量初始化,则按他们定义的顺序调用)
以上内容通过韩顺平老师的视频做的笔记
https://space.bilibili.com/651245581?from=search&seid=11092504021297742460&spm_id_from=333.337.0.0