Return Java
第一章
Java了解
1.1Java诞生
1、1995 年 sun正式发布Java第一个版本(创始人:gosling)
2、最新版本Java15(使用时期不长)
3、Java现在属于甲骨文公司
1.2Java特点
-
Java语言是面向对象的(oop)
-
Java语言是健壮的。 Java的强类型机制、异常处理、垃圾的自动收集等是Java程序健壮性的重要保证
-
Java语言是跨平台性的, [即:一个编译好的.class文件可以在多个系统下运行,这种特性称为跨平台性
-
Java语言是解释型的
解释性语言: javascript,PHP java 编译性语言: c/c++
区别是: 解释性调言,译后的代码,不能宣接被机器执行,需要解得器来执行, 编译性语言,编译后的代码,可以直接被机器执行, c/c++
1.3 Java快速入门
编译过程:
E:\return java>javac Hello.java(javac 一个Java文件)
运行过程:
E:\return java>java Hello(java 你的类)
1. 4 Java中注意事项
1、Java源文件以 .java为扩展名,源文件的基本组成部分是类(class)
2、一个源文件中最多只能有一个public类,其他类可多。编译后,每一个类对应一个 .class
3、如果源文件中包含一个public类,则文件名必须按照该类名命名
4、也可以将main方法写在非public类中,然后指定运行非public类。(java 类名 即可)
1.5 转义字符
// 第一个\表示转义 第二个\表示输出
//在控制台(dos命令)可以用tab键实现补全
// \t: 一个制表位。实现对其功能
System.out.println("北京\t上海\t天津");
// \n:换行
System.out.println("北京\n上海\n天津");
// \\:输入一个\
System.out.println("北京\\上海\\天津");
// \": 输出一个"
System.out.println("我说\"北京上海天津\"");
// \r:回车 这个回车不是换行,而是直接跳转到第一个字符处
System.out.println("北京上海\r天津");
1.6 注释
单行: //
多行: /* */
文档:
注释的内容可以被jdk提供的工具javadoc所解析。生成一套以网页文件形式体现该程序的说明文档,一般写在类里面
/**
- 注释内容
*/
内容可以有:
·@author: 作者。
·@version: 版本。
·@docroot: 表示产生文档的根路径。
·@deprecated: 不推荐使用的方法。
·@param: 方法的参数类型。
·@return: 方法的返回类型。
·@see: 用于指定参考的内容。
·@exception: 抛出的异常。
·@throws: 抛出的异常,和exception同义
1.7 Java代码规范
1、选中代码,按 tab 键 整体右移
选中代码,按 shift+tab 整体左移
2、类、方法的注释要用javadoc(文档注释)
3、在写代码的时候,源文件要使用utf-8
4、每一行尽量不要超过80
5、次行风格和行尾风格
1.8 DOS命令
进入后默认在c盘
1、换盘方式: 盘:
例: d:(进入d盘)
2、创建文件夹: md 文件夹名字
例: md class
3、进入文件夹: cd 文件夹名字 或者 cd: 某盘|文件夹名字
例: cd class // cd d:|class
4、删除文件夹: rd 文件夹名字
例: rd class 注:当文件夹里面什么都没有时可用
5、删除文件: del 文件名
例; del 1.doc
6、退回上一级文件夹; cd…
7、退回根文件夹; cd/
8、退出dos命令行; exit
9、列出当前目录下的文件和文件夹: dir
10、清屏:cls
1.9 JDK、JRE、JVM 三者关系
第二章
Java变量
2.1 加号使用
两边都是加号(+)的时候,做加法运算
有一边为字符串时,做拼接运算
System.out.println(“100”+3) 输出:1003
System.out.println(100+3) 输出:103
System.out.println(“hello”+3+100) 输出:hello3100
System.out.println(100+3+“100”) 输出:103hello
2.2 数据类型
2.2.1 整形
byte【字节】 1字节:-128----127 1byte=1B
short【短整型】 2字节:-(2的15次方)—(2的15次方-1)即:
-32768–32767
int【整形】 4字节: -(2的31次方)—(2的31次方-1)即:-2147483648–2147483647
long【长整型】 8字节:-(2的63次方)—(2的63次方-1)
数据类型细节: long类型后面的值要记得加上L long n = 3L; bit最小存储单位 byte基本存储单位 1byte=8bit
2.2.2 浮点
浮点数=符号位+指数位
float:4字节 = 4B
double:8字节 = 8B
浮点型细节:
1、float记得值后面加f
2、十进制形式: 5.12 512…0 .512(相当于0.512 零可以省略
小数点不能省略)
3、科学计数法: 5.12e2 (5.12*10的2次方) 5.12E-2(5.12/10的2次方)
4、在对两个小数进行相等判断的时候,应该是以两个数差值的绝对值在某个精度范围内判断 绝对值:
Math.abs(num1-num2)
2.2.3 字符型
‘ ’单引号引着
字符型细节:
1、‘\’这个不是字符 这是转义字符的标志 例如:‘\t’调节成表位
2、在java中,char的本质其实是整形,因为char存入的是Unicode码对应的数值
例:
char a = ‘王’;
system.out.println((int)a)
3、char类型是可以参与运算的,因为本质是整形,在运算的时候会先转换成对应字符再进行计算
例:
system.out.println(‘a’+ 10)//等于 107
这就是先吧‘a’转换成unico码 97 ,再进行与10的加法
字符和码值的对应关系:
ASCII码(一个字节表示字符,一共128个字符,实际最多表示256个字符)美版
Unicode码 (两个字节来表示字符 字母和汉字一样,不区分汉字)
utf-8(大小可变,字母一个字节,汉字三个字节)
gbk(字母一个字节,汉字两个字节)
gb2312(也可以表示汉字,gb2312<gbk)
4. 布尔类型:
大部分是1B,有时候是4B
低精度转化到高精度:
char —> int —> long —> float --> double;
byte --> short —> int —> long—> float —> double
例:
int a = ‘a’
2.3基本类型和字符串的转换
2.3.1 基本数据类型转换为字符串:
语法:将基本数据类型 + “ ”即可
例:
int i1 = 100; String s1 = i1 + “ ”;
2.3.2字符串转成基本数据类型:
语法:通过基本类型的包装类调用parseXX(XX代表基本类型)方法
例:
String s2 = “123”;int num1 = Integer.parseInt(s5);
2.3.3字符串转换成字符
意义;把字符串的第一个字符取出来
2.3.4 转换细节
1、在从String类型转换到基本数据类型的时候,要确保能够转换成有效的数据
例:
“123”可以转换成一个整形(int),但是“hello”就不能转换
2、格式不正确就会抛出异常
2. 4运算符
2.4.1 算术运算符:
- (正)
- (负)
- (加)
- (减)
- (乘)
/ (除)1
% (取模)
-
- (自增(前))
例:a = 2; ++a先加再用
- (自增(前))
-
- (自增(后))
例:a = 2; a++ 先用再加
- (自增(后))
-
- (自增(前))
例:a = 2; - -a先减再用
- (自增(前))
-
- (自增(后))
例:a = 2; a - 先用再减
- (自增(后))
- :字符串相加
注:在取模运算中:
有小数运算的时候,公式: a = a - (int)a / b * b
例:
2.4.2 运算符优先级
优先级、下表从高到低减小
![在这里插入图片描述](https://img-blog.csdnimg.cn/d562c04118294d61b9f851b234ba2f15.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA562x5rGQ5ZCW,size_ 20,color_FFFFFF,t_70,g_se,x_16)
2.4.3.关系运算符
关系运算符:
= = , !=
, <
= , < =
==情况,基本数据类型比较的时候,只比较值,引用数据类型时,判断内存地址(堆中)
2.4.4.逻辑运算符
逻辑运算符:
两个(符号) && 短路
单个(符号) & 逻辑
注:
对于短路与(&&),当第一个条件为假的时候,不判断第二个条件
对于逻辑与(&),当第一个条件为假的时候,仍然要判断第二个条件
短路或和逻辑或一样的:
短路或:第一个真第二个不判断
逻辑或:第一个真第二个还要判断
2.4.5.三元运算符
三元运算符:
语法:条件表达式?表达式1:表达式2
①条件表达式为true,返回表达式1
②条件表达式为false,返回表达式2
表达式1和表达式2要为可以赋给接受变量的类型(或者可以自动转换或者强制转换)
(错的,double不能够转换给int)
例:
①分开两句实现:
②一句话实现:
2.4.6.位运算符
5.4.6.1.按位与:
2.4.6.2 按位取反:
注:在补码回原码的时候,可以把首尾的1给定下来不动,中间的数按位取反就可以得到原码(图中蓝色笔记)
2.4.6.3.算术右移 AND 算术左移:
注:
算术右移本质:原来的数后面/2
算术左移本质:原来的数后面*2
例:
2. 5标识符规则
标识符规则(必须遵守)
标识符规范(建议)
2. 6关键字和保留字
关键字、保留字不能做标识符
2.6.1 关键字:
2.6.2 保留字:
2.7进制
2.7.1 进制:
2.7.2 进制的图示:
3 进制的转换
3.1 二进制转十进制:
注:二进制是 0b 开头
3.2 八进制转十进制:
注:八进制是 0 开头
3.3十六进制转十进制:
注:十六进制是 0x 开头
3.4十进制转二进制
注:0b是表示二进制
3.5十进制转八进制:
注:0是表示八进制
3.6十进制转十六进制
注:0x是表示十六进制
3.7二进制转八进制
3.8二进制转十六进制
3.9八进制转二进制
3.10十六进制转二进制
2.8原码、反码、补码
第三章
程序控制结构
3.1、 顺序控制
程序从上往下逐行执行,中间没有跳转和判断
3.2、分支控制
3.2.1、单分支
单分支基本语法:
注:
①、只有一条语句的时候,可以不用 { }(大括号),建议无论是多条还是一条执行语句,都使用 { }(大括号)
②、当if(条件为正的时候)执行大括号里面的执行语句
3.2.2、双分支
双分支基本语法:
注:
如果if(表达式为真)则执行代码块1,否则执行代码块2
3.2.3、多分支
多分支基本语法:
注:哪条条件表达式正确则执行哪条执行语句
多分支对应流程图:
例:
3.2.4、嵌套分支
例:
import java.util.Scanner;
public class Singer {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入成绩及性别:");
double grade = scanner.nextInt();
char sex = scanner.next().charAt(0);
if(grade>8.0){
if(sex == '男'){
System.out.println("恭喜你进入男子组决赛");
}else if (sex == '女'){
System.out.println("恭喜你进入女子组决赛");
}
}else{
System.out.println("很遗憾,你淘汰了");
}
}
}
3.2.5、switch分支结构
switch分支结构:
switch语句的流程图:
注:匹配到case常量,执行完语句块后,要是没有break,则不执行下一个case常量,直接执行下一个case常量里面的语句块
例题:
import java.util.Scanner;
public class Switchyuju {
public static void main(String[] args) {
System.out.println("请输入字符:");
Scanner scanner = new Scanner(System.in);
char day =scanner.next().charAt(0);//只要有返回值都可以作为常量
switch (day){
case 'a':
System.out.println("星期一");
break;
case 'b':
System.out.println("星期二");
break;
case 'c':
System.out.println("星期三");
break;
default:
System.out.println("你输入有错");
}
}
}
3.2.6、 switch和if的区别
3.3、for循环
for循环执行流程:
注意事项和细节说明:
注:在for循环的时候,先做到 化繁为简(从简单到复杂),先死后活(从死变量到活变量)
- 增强for
for(类型 变量 : 数组/集合){
代码
}
int [] arr = {1,2,3,4,5,6,7,8,9,10};
for(int a : arr){
System.out.println(a);//数组中的每一个元素
}
变量是指向后面数组/集合里面的每一个元素
3.4、while循环
while执行流程分析:
注意事项和细节:
1、循环条件是布尔类型
2、while循环先循环再执行
3.5、do while 循环
说明:
流程图:
注意事项和细节:
1、循环条件是布尔类型
2、while循环先循环再执行(至少执行一次)
3.6、多重循环
打印空心金字塔:
3.7、break语句
3.8、continue语句
3.9、return语句
第四章
数组
4.1、一维数组
4.1.1、基本介绍
4.1.2、动态初始化
动态初始化1、
语法:数据类型 [ ] 数组名 = new 数据类型 [ 大小 ] or 数据类型 数组名 [ ] = new 数据类型 [ 大小 ]
例: int [ ] a = new int [5]; or int [ ] a = new int [5]; //创建一个数组 数组名为a 长度为5 的int数组
动态初始化2、
①先声明数组:
语法: 数据类型 [ ] 数组名 or 数据类型 数组名 [ ]
例: int a[ ]; or int[ ] a;
②再创建数组
语法: 数组名 = new 数据类型 [ ];
例: a = new int [10];
静态初始化:
语法:数据类型 数组名 [ ] = {元素值,元素值,…};
例: int [a] = {1,2,3,4,5…};//已知数组长度以及元素值的时候使用
4.1.3、注意事项
4.1.4、数组赋值机制
4.1.5、数组拷贝
数组拷贝,要求拷贝的数组有独立地址空间:
4.1.6、数组的添加、扩容
4.2、二维数组
4.2.1、基本介绍
1、定义形式: int [ ] [ ]
2、理解:二维数组的元素由多个一维数组构成
3、所以一般要进行两次遍历才能获得数组,外层遍历二维数组的元素(也就是一维数组),内存遍历一维数组的元素
4.2.2、动态初始化
1、动态初始化
语法定义: 类型[ ] [ ] 数组名 = new 类型 [大小] [大小] //定义时直接new
解读:前一个大小代表二维数组的元素(一维数组)有多少个,后一个大小是每一个一维数组的元素个数。
例:
int [ ] [ ] arr = new int [3] [2]
2、动态初始化
①先声明: 类型 数组名 [ ] [ ];
②再定义(开辟空间) 数组名 = new 类型 [大小][大小]
③赋值:有默认值,比如int,默认值就是0
3、动态初始化—列数不确定时
①先开展 二维数组的空间(其一维数组的个数)
②再开展一维数组的空间,在进行遍历赋值
例:输出这样一个数组
代码:
public class P179ArrayBuQueDing {
public static void main(String[] args) {
int arr [] [] ;//声明一个二维数组
arr = new int[3][];//开展二维数组的空间,定义二维数组有多少个一维数组
for (int i =0;i< arr.length;i++){
arr[i]=new int[i+1];//开展一维数组的空间(定义一维数组可以存放多少个元素)
for (int j =0;j< arr[i].length;j++){
arr[i][j]=j+1;//遍历给其一维数组元素赋值
}
}
for (int i = 0;i< arr.length;i++){
for (int j =0;j<arr[i].length;j++){
System.out.print(arr[i][j]+"\t");//输出整个二维数组
}
System.out.println();
}
}
}
4.2.3、静态初始化
语法:
类型[] [] 数组名 = {{值1,值2…},{值1,值2…},{值1,值2…},{值1,值2…}}
使用方法:固定的方法(依次遍历)
实例:
int[ ] [ ] arr={{1,2,3},{1,5,8},{1}}
注:二维数组的元素必须是一维数组
4.2.4、杨辉三角
打印一个10行的杨辉三角图像:
public class P182YangHuiSanJiao {
public static void main(String[] args) {
int [][] arr =new int [10][];//定义二维数组(一维数组的个数)
for (int i =0;i< arr.length;i++){
arr[i]=new int[i+1];//定义开展一维数组的空间
for (int j=0;j<arr[i].length;j++){
if (j==0 || j==i)//第一个和最后一个数字为1
arr[i][j]=1;
else {//中间数的取值
arr[i][j]=arr[i-1][j-1]+arr[i-1][j];
}
}
}
System.out.println("====杨辉三角====");
for (int i=0;i< arr.length;i++){
for (int j=0;j<arr[i].length;j++){
System.out.print(arr[i][j]+"\t");
}
System.out.println();
}
}
}
4.3、数组的细节
4.4、练习题
代码:
public class P187KuoRongChaRu {
public static void main(String[] args) {
int[] arr = {10, 12, 45, 90};
int[] Newarr = new int[arr.length + 1];//定义新数组长度
int index = -1, add = 23;//index作为标志,标记插入的位置
for (int i = 0, j = 0; i < arr.length; i++) {
if (add <= arr[i]) {//判断插入的位置
index = i;//找到将其下标赋值给index,然后退出
break;
}
}
if (index == -1){//如果没有找到,则添加在最后
index = arr.length;
}
for (int i =0,j=0;i< Newarr.length;i++){
if (index != i){//将原来的数组拷贝到新数组,遇到插入位置的时候不拷贝
Newarr[i]=arr[j];
j++;
}else {
Newarr[i]=add;
}
}
for (int i =0;i< Newarr.length;i++) {
System.out.print(Newarr[i]+"\t");
}
}
}
import java.util.Random;
/**
* @Author wanghaiyang
* id 201931101227
* @date 2021/7/29 11:56
*/
/*随机生成100以内的十个数,找出其最大值下标和最小值下标,将其降序排序,
输出总和,并查询是否含有8这个数字。*/
public class P188SunJiShu {
public static void main(String[] args) {
int [] arr =new int[10];//定义数组,长度为十
int sum=0;
int index2 =-1;//建立索引,判断是否含有8
Random random =new Random();//随机数生成语法
for (int i =0;i<10;i++){
arr[i]=random.nextInt(100);//生成100以内的随机数
sum +=arr[i];//求和
while (arr[i]==8){//含有8,将其标记下来
index2 =1;
}
}
System.out.print("随机数组:");
for (int i =0;i< arr.length;i++) {
System.out.print( arr[i]+"\t");
}//打印随机数数组
System.out.println("");
//通过index2的值来判断是否含有8
if (index2 ==-1){
System.out.println("随机数中没有8");
}else if(index2 ==1){
System.out.println("随机数中有8");
}
int max =arr[0];
int maxindex =0;//标记最大值下标
int min =arr[0];
int minindex =0;//标记最小值下标
//最大值最小值下标确定
for (int i =1;i< arr.length;i++){
if (max <=arr[i]){
max=arr[i];
maxindex =i;
}else if (min>arr[i]){
min =arr[i];
minindex =i;
}
}
System.out.println("最大下标:"+maxindex);
System.out.println("最小下标:"+minindex);
int temp =0;//中间变量,进行元素判断以及调整位置
for (int j =0;j< arr.length-1;j++) {
for (int i = 0; i < arr.length-1-j; i++) {
if (arr[i] < arr[i + 1]) {
temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
}
}
}
System.out.print("倒序顺序:");
for (int i =0;i< arr.length;i++) {
System.out.print( + arr[i]+"\t");
}//打印降序数组
System.out.println("");
System.out.println("总和为:"+sum);
}
}
4.5、排序
4.5.1冒泡排序
用中间变量 temp 来置换两个字符的位置。通常使用两重for循环,外层循环长度为数组长度代表循环轮数,内存循环代表每一轮的循环次数。
例题:
将下列无序数组按照从小到大的顺序排序【24、69、87、12、58】
代码:
public class P174maopao {
public static void main(String[] args) {
int [] arr = {1,2,3,4,5};
int temp =0;//中间变量,用于置换字符。
for (int i =0; i< arr.length-1;i++){
for (int j = 0;j< arr.length-1-i;j++){
if (arr[j]>arr[j+1]){
temp = arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
else {
break;
}//当一个数组是有序的时候,一次都不用比较,直接输出。
}
}
for (int i =0; i< arr.length;i++){
System.out.print(arr[i]+"\t");// \t制表符
}
}
}
4.5.2顺序查找
将要查找的内容与已知数组元素,从第一个 一一比较是否相等。
例题:
有一个数列:向红、小王、江仔、啦啦、露露,从键盘输入任意一个名称,判断此数列包不包含这个名称,有则输出其下标,没有则输出:不好意思,没有找到
public class P176ShunXuChaZhao {
public static void main(String[] args) {
String [] Name ={"向红","小王","江仔","啦啦","露露"};
System.out.println("请输入你所要查找的名称:");
Scanner scanner = new Scanner(System.in);
String FindName = scanner.next();
int index = -1;//创建索引
for (int i =0;i<Name.length;i++){
if (FindName.equals(Name[i])){//字符的比较用equals
System.out.println("恭喜你,"+FindName+"找到,其下标为:"+i);
index =i;//索引标记找到
}
}
if (index==-1){//当索引不变时,代表没有找到
System.out.println("不好意思,没有找到");
}
}
}
第五章
类与对象
5.1、概念
定义一个类(相当于 数据类型),包括 其属性(自身存在的属性) 行为(类对象所有的行为动作)
实例化一个对象
5.2内存方式
类与对象的内存分配机制:
Java内存结构分析:
1、栈:一般存放基本数据类型(局部变量)
2、堆:存放对象(Cat cat,数组等)
3、方法区:常量池(常量、比如:字符串),类加载信息
4、示意图:[Cat(name、age、price]
Java创建对象的流程简单分析:
5.3、属性/成员变量
名称:
属性 == 成员变量 == field(字段)
注:
属性是类的一个组成部分,一般是基本数据类型,也可以是引用类型(对象、数组)
基本数据类型:
int、double、float等
引用类型:
int [ ] name = {…}
string name 等…
属性的注意事项:
①、属性的定义和变量一样的 例: 访问修饰符、属性 、 属性名
注释:访问修饰符 (public、protected、private、默认) 控制属性的访问范围
②、属性的定义可以任意,基本类型、引用类型
③、不赋值的话有默认值:
int 0 、 short 0 、 byte 0 、 long 0 、 float 0.0 、 double 0.0
char \u0000 、 Boolean false 、String null
5.4、创建与访问
对象的创建:
①、直接创建:
例:Cat cat = new cat();
②先声明再创建:
例: Cat cat; //声明对象
cat = new cat();//创建对象
访问属性:
对象名.属性名
例: cat.name;
cat.age;
5.5、行为方法
一个类,除了有属性外,还有行为(也就是成员方法)比如:说话、跑步、学习
另注:当方法返回值是数据类型的时候,但是要输出具体句子,返回值使用null,但是此时的返回类型应该是用类来包装完成
Person类为例子:
class Person{
int age;//属性
String name;//属性
public void speak(){//行为方法
System.out.println("玛卡巴卡");
行为方法解读:
public 代表公共使用
void 代表没有返回值
speak()代表方法名,()括号里面叫做形参列表
{ }内部是方法体
方法调用:
先创建一个Person类对象,调用方法使用:对象名.方法名 例:Person.speak
Person p1 = new Person();
p1.speak();
编写方法思路:
//编写方法的思路:
//1、方法的返回类型
//2、方法的名字
//3、方法的形参
//4、方法体
方法入门例题:
1、创建speak方法输出”玛卡巴卡“
2、创建cal01方法,输出1+…+1000的和
3、创建cal01方法,输出1+…+n的和
4、创建getsum方法,计算两个数的和
public class P202Method {
public static void main(String[] args) {
Person p1 = new Person();
p1.speak();
p1.cal01();
// p1.cal02();//直接通过键盘输入 n 的值去计算和
p1.cal02(5);//5 是形参 在这里直接可以更改n的值计算,不用再通过键盘输入
//将getsum方法的返回值接收给returnSum将其输出,不接收则不输出
int returnSum = p1.getsum(10,20);
System.out.println("两数和为:"+returnSum);
}
}
class Person{
int age;
String name;
//创建speak方法输出”玛卡巴卡“
/*
public 代表公共使用
void 代表没有返回值
speak()代表方法名,()括号里面叫做形参列表
{ }内部是方法体
*/
public void speak(){
System.out.println("玛卡巴卡");
}
//创建cal02方法,输出1+....+1000的和
public void cal01(){
int sum =0;
sum =(1000*(1000+1))/2;
System.out.println("1+....+1000 和为:"+sum);
/* 通过for循环完成
for (int i=1 ;i<=1000;i++){
sum += i;
}
System.out.println("1+....+1000 和为:"+sum);
*/
}
//创建cal01方法,输出1+....+n的和
/*第一个方法:
public void cal02(){
int n;
int sum =0;
System.out.println("请输入n的值:");
Scanner scanner = new Scanner(System.in);
n = scanner.nextInt();
sum =(n*(n+1))/2;
System.out.println("1+....+n 和为:"+sum);
}
*/
//第二个方法:
public void cal02(int n){//(int n)是形式参数,直接在此方法中定义变量n
int sum =0;
sum =(n*(n+1))/2;
System.out.println("1+....+n 和为:"+sum);
}
//创建getsum方法,计算两个数的和
/*
public 代表公共使用
int 代表有返回值int类型,返回的值接不接受看自己
getsum()代表方法名,(int m, int n)括号里面叫做形参列表,用户可以输入两个
{ }内部是方法体
*/
public int getsum(int m, int n){
int sum = m+n;
return sum;
}
}
5.6、成员方法
5.6.1成员方法介绍
形式:
public(访问修饰符) 返回数据类型 方法名 (形参列表…){//里面存放方法体
语句;
return 返回值;
}
1、访问修饰符 (public、protected、private、默认) 控制属性的访问范围
2、返回数据类型:表示成员方法的输出,void表示没有返回值
3、方法体:表示为了实现某一代码块
4、return语句不是必须的
注意事项和使用细节
1、访问修饰符(作用是控制方法的使用范围)
2、返回数据类型:
①、一个方法最多有一个返回值,要返回多个时,返回数组即可
例:
public class P207ShiYongXiJie {
public static void main(String[] args) {
Arr a = new Arr();//实例化一个对象
int[] res1 = a.arr(5,4);//调用类的方法体
System.out.println("和="+res1[0]);
System.out.println("差="+res1[1]);
}
}
//1、返回多个结果,例:返回两个数的和 and 差
class Arr{
public int[] arr(int m,int n){//创建和、差方法,返回到一个数组
int[] res = new int[2];//创建一个数组寸和、差的值
res[0]=m+n;
res[1]=m-n;
return res;//返回数组元素
}
}
②、返回类型可以是任意类型,包括基本类型和引用类型
③、如果方法有返回数据类型,则方法体内必须有return值(数值、变量等),而且返回值的类型和return的值一致或者兼容(低精度–》高精度的转换)
④、如果方法是void,则方法体中没有return值 或者 只有return
3、方法名:最好是见明知意,比如,获得两数和: getSum
方法调用:
1、同一个类中的方法可以直接调用 例:
2、跨类中的方法 A类调用B类方法:需要通过对象名调用 例:
4、跨类的方法调用跟方法的访问修饰符有关系。
5.6.2练习题
//1、编写类AA 有一个方法:判断一个数是奇数还是偶数,返回boolean
/*
public class P210MethodTest {
public static void main(String[] args) {
AA aa = new AA();
/*第一种方法:
boolean odd = aa.PanDuan(5);
System.out.println("数是不是奇数:"+odd);
*/
/*第二种方法
if(aa.PanDuan(5)){
System.out.println("数是奇数");
}else{
System.out.println("数是偶数");
}
}
}
class AA{
public boolean PanDuan (int n){
/*第一种方法:
if (n % 2 ==0){
return false;
}
else {
return true;
}
*/
/*第二种方法:
return n % 2 == 0 ? false : true;//三元运算符,真返回前者,假返回后者
return n % 2 != 0 ;//本身返回的就是一个boolean值了
}
}
*/
//2、根据行、列、字符打印 对应行数和列数的字符
/* 例:行:4 列:4 字符:*
****
****
****
****
*/
public class P210MethodTest {
public static void main(String[] args) {
AA aa = new AA();
aa.ZiFuBiao(4,4,'*');//输入三个实参
}
}
class AA{
public int [][] ZiFuBiao(int num1,int num2,char n){//三个形参
//方法体中可以不用数组,直接两层for循环即可
int[][] arr = new int[num1][num2];
for (int i =0;i<num1;i++){
for (int j =0;j<num2;j++){
System.out.print(n+"\t");// \t 制表符
}
System.out.println();//每一行结束换行
}
return arr;//因为有返回值,所以必须有return
}
}
5.6.3方法的调用机制
5.6.4方法的传参机制
1、基本数据类型,传递的是值(值拷贝)
注:形参(类方法中的参数)的任何改变不影响实参(main方法内的参数)
**2、引用数据类型,**传递的是地址(传递的也是值,但是这个值是地址):数组、对象、、
注:形参影响实参
① 当形参里面将其引用对象置空 则此对象不再指向原来的地址 直接指向空 对实参没有任何影响 p–》null
②当形参里面再次新建一个对象的时候 此对象则不指向原来地址 对实参没有任何影响 p = new p
方法克隆的例题
/*编写一个方法 copyPerson,可以复制一个Person对象。返回复制的对象,也就是克隆属性
* 注意要求得到的新对象和原来的对象是来给你个独立的对象,只是属性相同*/
public class P214CopyObject {
public static void main(String[] args) {
Person1 p = new Person1();//开辟一个Person1对象,及定义属性
p.name = "小王";
p.age = 20;
System.out.println("p的属性"+p.name+"\t"+p.age);
MyTools tool = new MyTools();//开辟一个方法类的对象
Person1 p1 =tool.copyPerson(p);//调用方法类的方法体进行克隆
System.out.println("p1的属性"+p.name+"\t"+p.age);
System.out.println(p==p1);//判断是否是独立的空间
}
}
class Person1{//创建一个Person1的类
String name;
int age;
}
//编写方法的思路:
//1、方法的返回类型 Person1
//2、方法的名字 copyPerson
//3、方法的形参 Person1 p
//4、方法体。克隆语句,返回属性值
class MyTools{//创建一个方法类
public Person1 copyPerson(Person1 p){//创建克隆的方法
Person1 p1 = new Person1();//开辟一个新的Person1对象
p1.name = p.name;
p1.age = p.age;
return p1;//返回克隆后的Person1属性
}
}
5.7递归方法
5.7.1递归方法介绍
递归方法:
就是自己调用自己,每次调用时传入不同的变量,有助于解决复杂问题,
在哪发生递归调用,递归后的值就返回到哪里
5.7.2递归的重要规则
5.7.3递归的内存分析图
打印递归的内存图分析:
阶乘递归的内存图分析:
5.7.4斐波那契数列例题
/*请使用递归的方式求出斐波那契1,1,2,3,5,8,13…给你一个整数n,求出它的值
- 分析:
- 1、当n = 1 时,斐波那契数 为 1
- 2、当n = 2 时,斐波那契数 为 1
- 3、当n >= 3 时,斐波那契数 为 (n-1)+(n-2)*/
public class P220FeiBoNaQieShu {
public static void main(String[] args) {
ShuLie list = new ShuLie();//实例化一个对象
int list1 = list.FeiBo(7);//斐波那契数列的返回值
System.out.println("斐波那契数为:"+list1);
}
}
class ShuLie{
public int FeiBo(int n){
if (n>0) {
if (n == 1 || n == 2) {
return 1;
} else{//大于等于3的时候,使用递归
return FeiBo(n - 1) + FeiBo(n - 2);
}
}
else {
System.out.println("要求输入的数时正数");
return 1;
}
}
}
5.7.5猴子吃桃例题
/*猴子吃桃问题:有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个! 以后每天猴子都吃其中的一半,然后再多吃一个。当到第十天时,想再吃但是还没有吃发现只有一个桃子了。问:最初有多少个桃子?
- 分析:
- day 10 桃子有 1个
- day 9 桃子有(1+1)*2个 =4个 ==》(day10+1)*2
- day 8 桃子有(4+1)*2个 =10个 ==》(day9+1)*2
- 规律:前一天的桃子数 = (后一天的桃子数+1)*2
*/
public class P221MonkeyEatpean {
public static void main(String[] args) {
Peach peach = new Peach();
int n=1;//n代表第几天
int sum1= peach.sum(n);
System.out.println("第"+n+"天的桃子总数为:"+sum1);
}
}
class Peach{
public int sum(int n){
if (n==10){
return 1;
}else if(n>0 && n<10){
return (sum(n+1)+1)*2;
}else {
System.out.println("天数只能在1-10之间");
return 0;
}
}
}
5.8方法重载
5.8.1概念和细节
方法重载:
java中允许同一个类中,多个同名方法的存在,但是,形参列表不一致。
注意细节和注意事项:
1、方法名:必须相同
2、形参列表:必须不同(形参类型或者个数或者顺序,至少有一个不同即可,形参名无要求)
3、返回类型:没有任何要求
4、只能通过参数去确定是调用那个方法
例题1:
类Method中定义三个重载方法并调用。方法名为m,三个方法分别接收一个int参数,两个int参数,一个字符串参数分别执行平方运算、相乘运算、输出字符串的结果,在mian方法中调用
import org.w3c.dom.ls.LSOutput;
import java.sql.SQLOutput;
public class P230OverLoad {
public static void main(String[] args) {
Method method = new Method();
System.out.println(method.m(5));
System.out.println(method.m(5,6));
System.out.println(method.m("abc"));
}
}
class Method {
public int m(int a){
return a*a;
}
public int m(int a,int b){
return a*b;
}
public String m(String a){
return a;
}
}
例题2:
在Method类中,定义三个重载方法max(),第一个方法返回两个int种发最大值第二个返回两个double中最大值,第三个返回三个double中的最大值,并调用
public class P231OverLoad2 {
public static void main(String[] args) {
Method1 method = new Method1();
/*第一种方法输出
method.max(5,6);
method.max(5.5,6.6);
method.max(1.1,5.5,3.3);
*/
//第二种方法输出
System.out.println(method.max(5,6));
System.out.println(method.max(5.4,6.6));
System.out.println(method.max(1.1,2.2,3.3));
}
}
class Method1{
/*第一种方法,用if语句来判断
public void max(int a,int b){
if (a>b){
System.out.println(a);
}else {
System.out.println(b);
}
}
public void max(double a,double b) {
if (a > b) {
System.out.println(a);
} else {
System.out.println(b);
}
}
public void max(double a,double b,double c){
double max = a;
if (max<b) {
max = b;
if (max < c) {
max = c;
}
}
System.out.println(max);
}
*/
//第二种方法:用三元运算符
public int max(int a,int b){
return a > b ? a : b;
}
public double max(double a,double b){
return a > b ? a : b;
}
public double max(double a,double b,double c){
double max1 = a > b ? a : b;//先比较a和b的大小将其大的存放在max1中再与c进行比较
return max1 > c ? max1 : c;
}
}
5.8.2可变参数
基本概念:
java允许将同一个类中,多个同名、同功能,但 参数个数不同 的方法,封装成一个方法,就可以通过可变参数实现
基本语法:
访问修饰符 返回类型 方法名 (数据类型 … 形参名){
}
如图:
注意事项与细节:
1、可变参数的实参可以为0个或者任意多个
2、可变参数的实参可以是数组
3、可变参数的本质就是数组
4、
5、
例题:
三门方法:分别返回,姓名和两门课成绩,姓名和三门课成绩,姓名和五门课成绩 类名称:HspMehtod 方法名:showScore
public class P236KeBianCanShu {
public static void main(String[] args) {
HspMehtod has = new HspMehtod();
has.showScore("江仔",80,90,50);
has.showScore("小王",99,91,100,150);
}
}
class HspMehtod {
public void showScore(String a ,double... socre){//score本质就是一个数组
System.out.print("姓名:"+a);
double sum =0;
for (int i =0; i< socre.length;i++){
sum += socre[i];
}
System.out.println("一共有"+socre.length+"门课,总成绩为:"+sum);
}
}
5.9作用域(变量)
细节与注意事项:
5.10构造器
5.10.1概念介绍
构造方法又叫做构造器(constructor),是类的一种特殊的方法,他的主要作用事完成新对象的初始化
基本语法:
[修饰符] 方法名 (形参列表){
方法体
}
特点:
1、方法名和类名相同
2、没有返回值
3、在创建对象的时候,系统会自动的调用该类的构造器完成对象的初始化
4、构造器的修饰符可以是默认的,也可以是 public、protected、private
5、参数列表 和 成员方法一样的规则
//在创建人的对象时,就直接指定这个对象的年龄和姓名
public class P240Constructor01 {
public static void main(String[] args) {
Person01 P1 = new Person01("江仔",21);//直接初始化了属性值
}
}
class Person01{
String name;
int age;
/*构造器:
1、没有返回值
2、构造器名字和类名相同
3、形参列表规则和成员方法一样
*/
public Person01(String Pname,int Page){
System.out.println("构造器被调用,初始化属性");
name = Pname;
age = Page;
}
}
5.10.2注意事项与细节
例题:
//在创建人的对象时,就直接指定这个对象的年龄和姓名
public class P240Constructor01 {
public static void main(String[] args) {
Person01 P1 = new Person01("江仔",21);//直接初始化了属性值
System.out.println("P1的年龄"+P1.age+"P1的名字"+P1.name);
Person01 P2 = new Person01();
System.out.println("P2的年龄"+P2.age+"P2的名字"+P2.name);
}
}
class Person01{
String name;
int age;
/*构造器:
1、没有返回值
2、构造器名字和类名相同
3、形参列表规则和成员方法一样
*/
public Person01(String Pname,int Page){
System.out.println("构造器呗调用,初始化属性");
name = Pname;
age = Page;
}
public Person01(){
age = 18;
}
}
//输出:
5.11对象的创建
5.12 this关键字
5.12.1栈理解this关键字
那个对象调用,this就代表那个对象
5.12.2注意事项与细节:
输出:
输出:
5.12.3练习题:
//第一题:编写A01类,定义方法max,实现求某个double数组的最大值,并返回
public class P251ThisTestAll {
public static void main(String[] args) {
A01 a01 = new A01();
double [] arr1 = {1,2,5,10};
// double [] arr1 ={};
a01.max(arr1);
}
}
//第一题:编写A01类,定义方法max,实现求某个double数组的最大值,并返回
class A01{
public void max(double[] arr) {
//当输入的数组没有值时,则arr[0]不存在,所以要报错,
//当传入空数组时,又要报错,所以,解决:
if (arr != null &&arr.length > 0) {
double max = arr[0];
for (int i = 0; i < arr.length; i++) {
if (max <= arr[i]) {
max = arr[i];
}
}
System.out.println("最大值为:" + max);
}else {
System.out.println("输入的数组有误,数组不能为null");
}
}
}
第二题:编写A02,定义方法find,实现查找某 字符串 是否在某 字符串数组 中的元素查找, 并返回索引,找不到返回-1
public class P251ThisTestAll {
public static void main(String[] args) {
A02 a02 = new A02();
String[]arr = null;
// String[]arr = {};
// String[]arr = {"zai","jiang"};
int i =a02.find("jiang",arr);
System.out.println("字符串所在位置:"+i);
}
}
class A02{
public int find(String findstr,String [] strarr) {
//要考虑健壮性,即数组不能为null
if (strarr != null) {
for (int i = 0; i < strarr.length; i++) {
if (findstr.equals(strarr[i])) {
return i;
}
}
return -1;
}
else{
return -1;
}
}
}
第三题:编写类Book,定义方法updatePrice,实现更改某本书的价格,具体:如果价格>150,则改成150,如果价格>100,则改成100,否则不变
public class P251ThisTestAll {
public static void main(String[] args) {
Book book = new Book("江仔",20);
book.message();//输出价格变动前
book.updatePrice();
System.out.println("价格更改后的信息:");
book.message();//输出价格变动后
}
}
class Book{
String name;
double price;
//先创造一个构造器将书类初始化,使传入的时候可以直接传入书籍的信息
public Book (String name,double price){
this.name = name;
this.price = price;
}
//改价方法:
public void updatePrice(){
if (price>150){
price = 150;
}else if (price>100 && price<150){
price = 100;
}
}
//显示书籍的信息
public void message(){
System.out.println("书名="+this.name+" "+"价格="+this.price);
}
}
5.13类与对象练习题
A06创建一个Cale计算类,在其中定义2个变量表示两个操作数,定义四个方法实现求和、差、乘、商(要求除数为0的话,要提示)并创建两个对象,分别测试
public class P256this06 {
public static void main(String[] args) {
Cale cale1 = new Cale(1,0);
Cale cale2 = new Cale(2,0);
System.out.println("加法="+cale1.sum());
System.out.println("减法="+cale1.cha());
System.out.println("乘法="+cale1.cheng());
Double divRes = cale1.chu();//Double类来传递
if (divRes !=0){
System.out.println("除法="+cale1.chu());
}
}
}
class Cale{
double test1;
double test2;
public Cale(double test1,double test2){//写入一个构造器,方便创建对象时直接传入参数值
this.test1=test1;
this.test2=test2;
}
public double sum(){
return test1+test2;
}
public double cha(){
return test1-test2;
}
public double cheng(){
return test1*test2;
}
public Double chu(){//Double是类,double是数据类型,因为要输出null,所以需用Double类来实现
if (test2==0) {
System.out.println("除数不能为0");
return null;
}else{
return test1 / test2;
}
}
}
A07分析下列代码输出结果
A01题: 设计一个Dog类,有名字、颜色、年龄属性,定义输出方法show()显示其信息并创建对象,进行测试(提示 this.属性)
public class P257this07 {
public static void main(String[] args) {
System.out.println("A01题:狗狗类");
Dog dog = new Dog("江仔","白色",20);
dog.show();
}
}
class Dog{
String Name;
String Color;
int Age;
public Dog(String name,String color,int age){//构造器
this.Name=name;
this.Color=color;
this.Age=age;
}
public void show(){
System.out.println("姓名:"+this.Name+" 颜色:"+this.Color+" 年龄:"+this.Age);
}
}
A02题: 定义Music类,里面有音乐名name,音乐时长times,并有播放play功能和返回本身属性信息的功能方法
public class P257this07 {
public static void main(String[] args) {
System.out.println("A02题:音乐类");
Music music = new Music("江仔",52);
music.play();
music.message();
}
}
class Music{
String name;
double times;
public Music(String name,double times){
this.name=name;
this.times=times;
}
public void play(){
System.out.println("播放音乐");
}
public void message(){
System.out.println("音乐名:"+this.name+" 音乐时长:"+this.times+"分钟");
}
}
A12 创建一个Employee类,属性有(名字,性别,年龄,职位,薪水),提供3个构造方法,可以初始化(1)(名字,性别,年龄,职位,薪水) (2)(名字,性别,年龄)(3)(职位,薪水)要求充分复用构造器(主要体现 复用 二字)
class Employee{
String name;
String sex;
String position;
int age;
double salary;
//先定义(职位,薪水)构造器,因为属性最少,便于后面复用
public Employee(String position,double salary){
this.position=position;
this.salary=salary;
}
//(名字,性别,年龄)构造器
public Employee(String name,String sex,int age){
this.name=name;
this.sex=sex;
this.age=age;
}
//(名字,性别,年龄,职位,薪水)构造器
public Employee(String name,String sex,String position,int age,double salary){
this(name,sex,age);//复用(名字,性别,年龄)构造器
//this(positong,salary)这样是错的,因为this方法关键字调用,只能在方法的第一条语句中
this.position=position;
this.salary=salary;
}
}
public class P260this013 {
public static void main(String[] args) {
Circle5 circle5 = new Circle5();
PassObject passObject = new PassObject();
passObject.printAreas(circle5,5);
}
}
class Circle5{
double radius;
//构造器可要可不要
public double findArea(){//返回圆的面积
return Math.PI*radius*radius;
}
public double setRadius(double radius){//修改半径的方法
this.radius=radius;
return radius;
}
}
class PassObject{//方法签名
public void printAreas(Circle5 c,int times){//用一个对象来作为参数
for (int i=1;i<times;i++){//每一次循环,圆的半径都要改变,所以圆类里面需要一个修改半径的方法
c.setRadius(i);//用i的值去改变c对象的半径值
System.out.println("半径:"+c.radius+" 面积:"+c.findArea());
}
}
}
5.14 面向对象基本原则
在封装的时候尽可能遵循如下的原则
5.14.1、单一职责
在代码中,一个类负责一个职责
优点:降低了类的复杂度,一个类负责一个职责会比一个类负责多个职责更简单,更容易维护。提升了类的可读性和可维护性。
5.14.2、里氏替换原则
如果在一个程序中,每一个类型为T1的对象o1,都有类型为T2的对象o2,使用程序中的o1被替换成o2后,程序没有发生改变,那么T2可以称为T1的子类型。所有引用基类的地方都可以透明使用其子类的对象。
通俗的讲:子类可以扩展父类的功能,但是一般不建议修改父类的原有功能
5.14.3、依赖倒转原则
抽象不应该依赖于细节,细节应该依赖于抽象
相对于细节实现中的多样性,抽象的东西要稳定的多,以抽象为基础搭建起来的架构比以细节为基础搭建起来的架构更加稳定。
比如:java操作数据库的时候,jdbc就是典型的依赖倒转。
5.14.4、迪米特法则
最少知道原则,如果两个类不需要直接通信,那么这两个类就不需要直接相互作用,如果确实一个类要调用另一个类的时候,可以由第三方来转发调用
一个对象保证对其他对象的最少了解
5.14.5、开放-封闭原则
一个软件对外扩展应该是开放的,修改应该是封闭的,可以扩展,但是不能修改
当软件确实需要修改的时候,应该通过扩展的方式实现变化,而不是通过修改原有代码实现变化
5.14.6、接口隔离原则
一个程序不应该依赖于他不需要的接口,一个类对另一个类的依赖应该建立在最少的接口上,如果接口过于臃肿,那么实现他的类不管用不用得到,都必须实现接口中的所有方法,这显然是不好的设计。那么这个时候就应该遵循接口隔离原则,对臃肿的接口进行拆分,原则上尽可能建立单一接口,尽可能去细化接口,使接口中的方法尽可能的少,换句话来说为每一个类建立专用的接口,而不是试图建立一个庞大的公用接口。
第六章
包与封装
6.1、包
6.1.1包的定义
同一个包里面不能有同名的类,不同的包里面可以有同名的类。
包的作用:
1、区分相同名字的类
2、当类很多的时候,可以更好地管理类
3、控制访问范围(访问修饰符作用)
包的语法:
package com.person
1、package 关键字.表示打包
2、com.person :包名
包的本质:
创建不同的文件夹来保存类,示意图:
应用不同包的同类名类时
1、可以两个都用包名区分
2、第一个代码前引入包名,第二个用包名fig定义
6.1.2包命名
命名规则:
只能包含数字、字母、下划线、小圆点,但是不能用数字开头,不能是关键字或者保留字。
例如:
demo.classs.exec1//错的
demo.12a//错的
demo.ab12.oa//对
命名规范:
一般是小写字母+小圆点
例如:
com.公司名.项目名.业务模块
com.sina.crm.user//用户模块
常用包:
Java.lang.* //lang包时基本包,默认引入,不需要再引入
java.util.* //util包,系统提供的的工具包,工具类,使用Scanner
java.net.* //网络包,网络开发
java.awt.* //做java界面开发,GUI
6.1.3包细节
package com.xiaowang.pack;
import java.util.Arrays;
//import java.util.Scanner; 表示具体引入util包的具体哪一个类
// import java.util.*; 表示引用 util包的所有类
//将下列数组排序,通过Arrays类
public class Import01 {
public static void main(String[] args) {
int[] arr={1,8,2,-2,5};
Arrays.sort(arr);//自动排序方法
for (int i = 0; i <arr.length ; i++) {
System.out.print(arr[i]+"\t");
}
}
}
细节:
1、package的作用时声明当前类所在的包,需要放在类的最上面,一个类最多只有一句package
2、import指令 位置在package下面,在类定义的前面,可以有多句且顺序没有要求
6.2、封装
6.2.1封装定义
封装:
就说把抽象出的数据[属性]和对数据的操作[方法]封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作[方法],才能对数据进行操作。就是getter、setter方法
封装的理解和好处:
1、隐藏实现细节 方法(例如 连接数据库)<—调用(传入参数)
2、可以对数据进行验证,保证安全合理。
6.2.2封装步骤
6.2.3封装快速入门
题:
在一个Person类中,不能随便查看人的年龄,工资等隐私,并对设置的年龄进行合理的验证,年龄在(1-120之间),年龄合理则设置,否则给默认年龄18,工资不能直接查看,name的长度在2-6个字符之间。
package com.xiaowang.encapsulation;
public class P283Encap01 {
public static void main(String[] args) {
Person p = new Person();
p.setName("小王");
p.setAge(21);
p.setSalary(5200);
System.out.println("信息为:"+p.info());
}
}
class Person{
public String name;
private int age;
private double salary;//私用即不能直接查看,必须使用get方法去调用获取
public String getName() {//获取name的值
return name;
}
public void setName(String name) {//对名字的判断并赋值
//加入对名字数据的业务逻辑 (name的长度在2-6个字符之间)
if (name.length()>=2 && name.length()<=6){
this.name = name;
}else {
System.out.println("名字长度有误,应该在(2-6)之间 默认名字为 江仔");
this.name="江仔";
}
}
public int getAge() {//获取年龄的值
return age;
}
public void setAge(int age) {//对年龄的判断并赋值
//加入对名字数据的业务逻辑 (age的范围在1-120之间)
if (age>=1 && age<=120) {
this.age = age;
}else {
System.out.println("年龄输入错误,应该在1-120之间 默认年龄为 18");
this.age=18;
}
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public String info(){//输出人的信息方法
return "\t姓名="+name+"\t年龄="+age+"\t工资="+salary;
}
}
输出:
信息为: 姓名=小王 年龄=21 工资=5200.0
6.2.4封装和构造器
接封装快速入门案例,在里面没有写构造器,所以对数据(属性)是有限制 的,如果我们写了构造器,那么在创建对象的时候,限制就不起作用了,那么这时我们想要在构造器中限制也起作用,那么我们可以结合封装的set和构造器,即 在构造器中结合set,即在构造器中也可以对数据有限制作用
改进后的快速入门案例:
package com.xiaowang.encapsulation;
public class P283Encap01 {
public static void main(String[] args) {
Person p = new Person();
p.setName("小王");
p.setAge(21);
p.setSalary(5200);
System.out.println("信息为:"+p.info());
System.out.println("=====结合后的信息======");
Person p2 = new Person("小猪", 200, 5200);
System.out.println("信息为:"+p2.info());
}
}
class Person{
public String name;
private int age;
private double salary;//私用即不能直接查看,必须使用get方法去调用获取
public Person() {//无参构造器,可以通过ALT+SHIFT+INSRT快捷键然后点击construct
}
public Person(String name, int age, double salary) {//带参构造器
this.setName(name);//构造器与set功能的结合,可以让set的限制对构造器有用
this.setAge(age);
this.setSalary(salary);
}
public String getName() {//获取name的值
return name;
}
public void setName(String name) {//对名字的判断并赋值
//加入对名字数据的业务逻辑 (name的长度在2-6个字符之间)
if (name.length()>=2 && name.length()<=6){
this.name = name;
}else {
System.out.println("名字长度有误,应该在(2-6)之间 默认名字为 江仔");
this.name="江仔";
}
}
public int getAge() {//获取年龄的值
return age;
}
public void setAge(int age) {//对年龄的判断并赋值
//加入对名字数据的业务逻辑 (age的范围在1-120之间)
if (age>=1 && age<=120) {
this.age = age;
}else {
System.out.println("年龄输入错误,应该在1-120之间 默认年龄为 18");
this.age=18;
}
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public String info(){//输出人的信息方法
return "\t姓名="+name+"\t年龄="+age+"\t工资="+salary;
}
}
输出:
信息为: 姓名=小王 年龄=21 工资=5200.0
=====结合后的信息======
年龄输入错误,应该在1-120之间 默认年龄为 18
信息为: 姓名=小猪 年龄=18 工资=5200.0
6.2.5练习题
题目:
创建程序,在其中定义两个类:Account和AccountTest类体会Java的封装性
1、Account类要求具有属性:姓名(长度为2或3或4位)、余额(必须>20)、密码(必须是6位),如果条件不满子,则给出提示信息,并给默认值(自己设定)。
2、通过setXxx的方法给Account的属性赋值、
3、在AccountTest中测试
提示知识点:
String name=" ";
int len = name.length();
提示知识点:
String name=" ";
int len = name.length();*/
//AccountTest类的代码
public class AccountTest {
public static void main(String[] args) {
Account account = new Account();
account.setName("小王");
account.setMoney(5000);
account.setPassword("121456");
System.out.println(account.info());
System.out.println("=====带参构造器输出=====");
Account account1 = new Account("小王", 12, "1235687415");
System.out.println(account1.info());
}
}
//Account类的代码
class Account{
public String name;
private double money;
private String password;
public Account() {//无参构造器
}
//带参构造器来约束一次
public Account(String name, double money, String password) {
this.setName(name);
this.setMoney(money);
this.setPassword(password);
}
public String getName() {
return name;
}
public void setName(String name) {//姓名(长度为2或3或4位))
if (name.length()==2 || name.length()==3 || name.length()==4){
this.name = name;
}else {
System.out.println("名字长度错误 默认名字为 江仔");
this.name="江仔";
}
}
public double getMoney() {
return money;
}
public void setMoney(double money) {//、余额(必须>20)
if (money>20) {
this.money = money;
}else {
System.out.println("余额少于基本余额 默认余额为 20");
this.money=20;
}
}
public String getPassword() {
return password;
}
public void setPassword(String password) {//、密码(必须是6位)
if (password.length()==6) {
this.password = password;
}else {
System.out.println("密码位数错误 必须为6位 默认密码为:000000");
this.password="000000";
}
}
public String info(){
return "信息为:\t姓名="+name+"\t余额="+money+"\t密码="+password;
}
}
输出:
信息为: 姓名=小王 余额=5000.0 密码=121456
=====带参构造器输出=====
余额少于基本余额 默认余额为 20
密码位数错误 必须为6位 默认密码为:000000
信息为: 姓名=小王 余额=20.0 密码=000000
Process finished with exit code 0
第七章
继承与多态
7.1、继承
7.1.1继承的介绍
继承基本介绍:
继承示意图:
继承基本语法:
class(子类)extends 父类{
1、子类就会自动拥有父类定义的属性和方法
2、子类又叫做派生类
3、父类又叫做超类、基类
}
7.1.2继承快速入门
创建一个学生类,共有属性有:姓名、年龄、成绩,共有方法:得分情况、学生得分情况,创建一个pupil(小学生类)和graduate(毕业生类),其共有学生类的属性和方法,pupil含有自己的方法:考试方法;graduate也有自己的考试方法,用继承实现
public class Student {
public String name;
public int age;
public double score;
//共有方法:
//得分
public void setScore(double score) {
this.score = score;
}
//总体信息
public void info(){
System.out.println("\t姓名="+name+"\t年龄="+age+"\t成绩"+score);
}
}
//pupil含有自己的方法:考试方法
public class pupil extends Student{
public pupil(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
public void testing(){
System.out.println("小学生\t"+name+"\t正在考试小学数学");
}
}
public class Graduate extends Student{
public Graduate(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
public void testing(){
System.out.println("大学生\t"+name+"\t正在考大学数学");
}
}
//测试用例
public class Extends01 {
public static void main(String[] args) {
pupil pupil1 = new pupil("小王", 21, 80);
Graduate graduate = new Graduate("江仔", 22, 50);
pupil1.testing();
pupil1.info();
graduate.testing();
graduate.info();
}
}
7.1.3继承细节
细节1:子类继承父类所有的属性和方法,但是私有的不能直接访问,要通过父类的公共的方法去访问
例:
①子类要访问父类一个所以有属性的时候,可以在父类中中定义一个共有方法(getXxx),方法体里面就说所要调用的私有属性
②子类要调用父类的私有方法的时候,可以在父类中定义一个共有的方法,方法体里面就是执行的私有方法
细节2:子类必须调用父类的构造器,完成父类的初始化。
也就是在你调用子类构造器的时候,明明没有调用父类的构造器,但是父类的构造器也被调用了,其主要是因为有一个隐藏的super()在其中。
细节3、当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中使用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过。
细节4:如果希望指定去调用父类的某个构造器,则显式的调用一下。
(super(参数列表))
细节5:super在使用的时候,需要放在构造器的第一行,并super()只能在构造器中使用。
细节6:super()和this()都只能放在构造器第一行,因此这两个方法不能共存一个构造器。
细节7:Java所有类都是Object类的子类,Object是所有类的基类
细节8:父类构造器的调用不限于直接父类,将会一直向上追溯到Object类(顶级父类)
细节9:子类最多只能继承一个父类(指直接继承),即Java是单继承机制。如果想要A继承B了再继承C,则可以让B继承C
细节10:不能滥用继承,子类和父类之间必须满足is-a的逻辑
7.1.4继承的本质
继承的内存布局:
7.1.5练习题
/A01判断下面代码会输出什么/
public class Test01 {
public static void main(String[] args) {
B b = new B();//1、建立一个B的无参对象,所以调用B对象的无参构造函数,
}
}
class A{
A(){
System.out.println("a");
}
A(String name){
System.out.println("a name");
}
}
class B extends A{
B(){//2、调用B对象的无参构造函数
this("abc");// 3、用this函数调用B带参构造器
System.out.println("b");
}
public B(String name) {//4、调用B的带参构造函数
// 注意,在这里的时候,有一个 super()无参构造器先被调用,所以要先输出父类(A)的的无参构造器
System.out.println("b name");
}
}
输出:
a
b name
b
/A02判断下面代码会输出什么/
/*判断输出结果
* 分析后:
* 第一个输出: hahah 我是B类的有参构造器
* 第二个输出: 我是C类的有参构造器
* 第三个输出: 我是C类的无参构造器
* //分析失误:在public B(String name)这里有一个默认的super()无参构造器去访问父类C的无参构造函数public A()
* */
public class Test02 {
public static void main(String[] args) {
C c = new C();//1、创建C类的无参构造器对象
}
}
class A{
public A() {
System.out.println("我是A类");
}
}
class B extends A{
public B() {
System.out.println("我是B类的无参构造器");
}
public B(String name){//5、第一个输出:hahah 我是B类的有参构造器
//分析失误:在这里有一个默认的super()无参构造器去访问父类C的无参构造函数public A()
System.out.println(name+"我是B类的有参构造器");
}
}
class C extends B{
public C(){//2、调用C类无参
this("hello");//3、调用C类有参public C(String name)构造器
System.out.println("我是C类的无参构造器");//7、第三输出我是C类的无参构造器
}
public C(String name){
super("hahah");//4、调用父类的public B(String name)构造器
System.out.println("我是C类的有参构造器");//6、第二输出 我是C类的有参构造器
}
}
输出:
我是A类
hahah我是B类的有参构造器
我是C类的有参构造器
我是C类的无参构造器
/*A03编写Computer类,包含CPU、内存、硬盘等属性,getDetails方法用于返回Computer的详细信息
- 编写PC子类,继承Computer类,添加特有属性【品牌brand】
- 编写NotePad子类,继承Computer类,添加特有属性【color】
- 编写Test03类,在main方法中创建PC和NotePad对象,分别给对象中特有的属性赋值,
- 以及从Computer类继承的属性赋值,并使用方法并打印出输出信息*/
public class Test03 {
public static void main(String[] args) {
PC pc = new PC();
pc.brand="YSL";
//直接通过基类Computer的共有方法去初始化pc的属性,后面加上前面赋值的pc.brand
pc.Computer(123,123,"PC的硬盘"+" 品牌="+pc.brand);
System.out.println(pc.getDetails());//调用getDetails()的方法输出信息
System.out.println("=====================");
NotePad notePad = new NotePad();
notePad.color="金色";
notePad.Computer(456,456,"notePad的硬盘"+" 颜色="+notePad.color);
System.out.println(notePad.getDetails());
}
}
//编写Computer类,包含CPU、内存、硬盘等属性,getDetails方法用于返回Computer的详细信息
class Computer{
private int CPU;//CPU
private int memory;//内存
private String hardDisk;//硬盘
public void Computer(int CPU, int memory, String hardDisk) {
this.CPU = CPU;
this.memory = memory;
this.hardDisk = hardDisk;
}
public String getDetails(){
return "信息为:CPU="+CPU+" 内存="+memory+" 硬盘="+hardDisk;
}
}
//编写PC子类,继承Computer类,添加特有属性【品牌brand】
class PC extends Computer{
String brand;
}
//编写NotePad子类,继承Computer类,添加特有属性【color】
class NotePad extends PC{
String color;
}
输出:
信息为:CPU=123 内存=123 硬盘=PC的硬盘 品牌=YSL
=====================
信息为:CPU=456 内存=456 硬盘=notePad的硬盘 颜色=金色
/*A04
(1)设计一个表示二维平面上点的类Point,包含有表示坐标位置的protected类型的,成员变量x和y,获取和设置x和y值的public方法。
(2)设计一个表示二维平面上圆的类Circle,它继承自类Point,还包含有表示圆半径的protected类型的成员变量r、
获取和设置r值的public方法、计算圆面积的public方法。
(3)设计一个表示圆柱体的类Cylinder,它继承自类Circle,还包含有表示圆柱体高的protected类型的成员变量h、
获取和设置h值的public方法、计算圆柱体体积的public方法。
(4)建立Cylinder对象,输出其轴心位置坐标、半径、面积、高及其体积的值。
*/
package com.xiaowang.extendset.test2;
/*(1)设计一个表示二维平面上点的类Point,包含有表示坐标位置的protected类型的,
成员变量x和y,获取和设置x和y值的public方法。
*/
public class Point {
protected int x;
protected int y;
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
/*
* 设计一个表示二维平面上圆的类Circle,它继承自类Point,还包含有表示圆半径的protected类型的成员变量r、
获取和设置r值的public方法、计算圆面积的public方法。
* */
public class Circle extends Point{
protected int r;
public int getR() {
return r;
}
public void setR(int r) {
this.r = r;
}
public double getArea(){
return Math.PI*r*r;
}
}
//(3)设计一个表示圆柱体的类Cylinder,它继承自类Circle,还包含有表示圆柱体高的protected类型的成员变量h、
//获取和设置h值的public方法、计算圆柱体体积的public方法。
public class Cylinder extends Circle{
protected int h;
public int getH() {
return h;
}
public void setH(int h) {
this.h = h;
}
public double getVolume(){
return super.getArea()*h;
}
public double getVolumeArea(){
return 2*super.getArea()+2*Math.PI*super.r*h;
}
public void details(){
System.out.println("圆柱体的底面轴心坐标:("+super.getX()+","+super.getY()+")");
System.out.println("圆柱体的底面半径:"+super.getR());
System.out.println("圆柱体的高:"+h);
System.out.println("圆柱体的面积:"+getVolumeArea());
System.out.println("圆柱体的体积:"+getVolume());
}
}
(4)建立Cylinder对象,输出其轴心位置坐标、半径、面积、高及其体积的值。
*/
public class Test {
public static void main(String[] args) {
Cylinder cylinder = new Cylinder();
cylinder.setX(1);
cylinder.setY(1);
cylinder.setR(5);
cylinder.setH(3);
cylinder.details();
}
}
输出:
圆柱体的底面轴心坐标:(1,1)
圆柱体的底面半径:5
圆柱体的高:3
圆柱体的面积:251.32741228718345
圆柱体的体积:235.61944901923448
7.1.6 super关键字
基本介绍:
super代表父类的引用,用于访问父类的属性、方法、构造器
基本语法:
1、访问父类的属性,但不能访问父类的private属性
super.属性;
2、访问父类的方法,但不能访问父类的private方法
super.方法名(参数列表);
3、访问父类的构造器
super(参数列表);
注:只能放在构造器里面去访问父类构造器,同时super关键字必须是第一行,也只能有一句super
细节与注意事项
1、调用父类的构造器好处(分工明确,父类属性由父类初始化,子类的属性由子类初始化,在子类初始化的时候可以用super关键字继承父类的属性一起初始化)
2、当子类有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super关键字。
如果没有重名,使用this 和直接访问是一样的效果,访问顺序如下(没有重名的时候),super关键字就是用来访问父类的
3、super的访问不限于直接父类,如果爷爷类和本类有同名的成员,也可以使用super去访问爷爷类的成员;如果多个基类中都有同名的成员,使用super访问遵循就近原则 A->B->C
super和this的区别
7.1.7方法重写/重载
基本介绍:
方法覆盖就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么我们就说子类的这个方法覆盖了父类的方法
覆盖细节:
1、子类方法的形参列表、方法名称,要和父类方法的形参列表、方法名完全一样。
2、子类方法的返回类型和父类的返回类型一样,或者子类的返回类型是父类的返回类型的子类,不能搞反
例: 父类的返回类型是Object 子类是String; 因为Object是所有类的基类
3、子类方法不能缩小父类方法的访问权限,可以扩大。
访问权限:publi》protected》默认》private
方法重写和重载的区别
练习题
/1、编写一个Person类,包括属性 private(name、age)
构造器、方法say(返回自我介绍的字符串)
2、编写一个Student类,继承Person类,增加private(id、score属性)
以及构造器,定义say方法(返回自我介绍的信息)
3、在main中,分别建立Person和Student对象,调用say方法输出自我介绍/
public class Override {
public static void main(String[] args) {
Person person = new Person("小王",21);
Student1 student = new Student1("江仔", 22, 123, 85);
System.out.println(person.say());
System.out.println(student.say());
}
}
//1、编写一个Person类,包括属性 private(name、age)
// 构造器、方法say(返回自我介绍的字符串)
class Person{
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String say(){
return "name="+name+" age="+age;
}
}
/*2、编写一个Student1类,继承Person类,增加private(id、score属性)
以及构造器,定义say方法(返回自我介绍的信息)*/
class Student1 extends Person{
private int id;
private double score;
public Student1(String name, int age, int id, double score) {
super(name, age);//子类自己初始化自己的构造器,利用了super关键字调用父类
this.id = id;
this.score = score;
}
public String say(){//通过super关键字调用父类的say()方法
return super.say()+" id="+id+" scroe="+score;
}
}
输出:
name=小王 age=21
name=江仔 age=22 id=123 scroe=85.0
7.2 多态
7.2.1多态的介绍
多[多种]态[状态]基本介绍
方法或者对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承继承之上的
多态的体现
1、方法的多态:
重载和重写体现多态(通过不同的参数列表个数顺序去实现多态,提高代码复用性)
2、对象的多态
7.2.2多态快速入门
现在有一个Animal类,包含Dog、Cat、食物类Food,包含Bone、Fish,现在主人Master要给动物喂食,狗啃骨头、猫吃鱼、后面可能还有更多的动物和食物,请用编程语言实现。
分析:
注意每一个动物吃东西的方式,以及分析清楚各对象所含有的属性
将其分为,动物类(Animal) 食物类(Food) 主人类(Master) 测试类(Test)
//动物类
public class Animal{
private String name;
public Animal(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Dog extends Animal {
public Dog(String name) {
super("狗"+name);
}
}
class Cat extends Animal{
public Cat(String name) {
super("猫"+name);
}
}
//食物类Food,包含Bone、Fish、Rice子类,
public class Food {
private String name;
public Food(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Bone extends Food{
public Bone(String name) {
super("啃"+name);
}
}
class Fish extends Food{
public Fish(String name) {
super("吃"+name);
}
}
//现在主人Master要给动物喂食,狗吃骨头、猫吃鱼、猪吃米饭,后面可能还有更多的动物和食物
public class Master {
private String name;
public Master(String name) {
this.name = name;
}
//喂食方法
public void feed(Animal animal,Food food ){
System.out.println(name+"喂"+animal.getName()+food.getName());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
/*现在有一个Animal类有其名字属性,包含Dog、Cat、Pig子类,食物类Food,包含Bone、Fish、Rice子类,
现在主人Master要给动物喂食,狗吃骨头、猫吃鱼、猪吃米饭,后面可能还有更多的动物和食物,请用编程语言实现。*/
public class Test {
public static void main(String[] args) {
Master master = new Master("小王");
Animal dog = new Dog("江仔");
Food bone = new Bone("骨头");
master.feed(dog,bone);
//下面可以再去创建猫对象
}
}
输出:
小王喂狗江仔啃骨头
7.2.3多态的向上转型
多态的向上转型:
1、父类的引用指向了子类的对象
2、语法:父类类型 引用名 = new 子类类型;
Animal animal = new Cat();
3、特点:编译类型看左边,运行类型看右边。
4、向上转型规则:
(1)、可以调用父类中的所有成员(需遵守访问权限)
(2)、不能调用子类(运行类型)的特有成员,因为在编译阶段,能调用哪些成员是由编译类型来决定的。
(3)、最终运行效果看子类(运行类型)的具体实现,即方法调用时,按照从子类(运行类型)开始查找,然后调用,调用规则和前面讲的的方法一致。
7.2.4多态的向下转型
多态的向下转型:
1、语法: 子类类型 引用名 = (子类类型) 父类引用
Cat cat = (Cat)animal;
2、只能强制转换父类的引用(就是向上转型时 父类的引用名),不能强转父类的子类对象(一个父类有多个子类,父类 父类1=new 子类1,父类 父类2= new 子类2 ,就不能:子类1 子类11 = (子类1)父类2,也就是不能强转的是另外个子类的向上转型的父类引用对象)
3、要求父类的引用必须指向的是当前目标类型的对象(向上转型时 父类所转型的子类类型,不能是父类的其他继承子类类型)
4、当向下转型后,可以调用子类类型中所有的成员(因为此时运行类型已经是子类类型了)
7.2.5注意事项与细节
多态的细节注意事项
1、多态的前提是:两个对象(类)存在继承关系
2、属性没有重写之说,属性的值看编译类型。
3、instanceOf比较操作符,用于判断对象的运行类型是否为XX类型或者XX类型的子类型。
7.2.6多态练习题
分析下列代码:
分析下列代码:
public class Polymorphism0 {
public static void main(String[] args) {
Sub s = new Sub();//创建一个Sub对象 s,编译类型Sub 运行类型Sub
System.out.println(s.count);//20,属性的值看编译类型
s.display();//20,方法看运行类型
Bass b = s;//编译类型是Bass,运行类型是Sub
System.out.println(b==s);//true,此时s和b指向同一个对象
System.out.println(b.count);//属性的值看编译类型 10
b.display();//20,看运行类型,依次查找咯
}
}
class Bass{
int count = 10;
public void display(){
System.out.println(this.count);
}
}
class Sub extends Bass{
int count = 20;
public void display(){
System.out.println(this.count);
}
}
7.2.7多态的动态绑定机制
Java的动态绑定机制:
1、当调用对象方法的时候,该方法会和该对象的内存地址(运行类型)绑定
2、当调用对象属性时,没有动态绑定机制,哪里声明哪里使用。
public class Test {
public static void main(String[] args) {
A a = new B();//编译类型 A, 运行类型 B
System.out.println(a.sum());//调用时看运行类型
System.out.println(a.sum1());
}
}
public class A {
public int i = 10;
public int sum(){
return getI()+10;
//因为B类也有getI()方法,所以在这里看a的对象的内存地址(运行类型),
// 所以调用B类的getI()方法,如果B类没有此方法则按规则依次找父类的方法
}
public int sum1(){
return i+10;//属性没有绑定机制,所以还是A类的i
}
public int getI(){
return i;
}
}
class B extends A{
public int i = 20;
public int sum(){
return i+20;
}
public int sum1(){
return i+10;
}
public int getI(){
return i;
}
}
7.2.8多态数组
1、多态数组:
数组的定义类型是父类类型,里面保存的实际元素类型为子类类型
例题:
1、先有一个继承结构如下:要求创建1个Person对象、2个Student对象,2个Teacher对象 统一放在数组里面,并且调用每一个对象的say()方法
2、老师有特有的teach方法,学生有特有的study方法,如何调用
public class Polyarr {
public static void main(String[] args) {
Person []person=new Person[5];//创建一个数组存放对象
//定义类型(相当于编译类型)为父类,元素类型为父类或其子类对象
person[0]=new Person("aaa",20);
person[1]=new Student("bbb",18,52);
person[2]=new Student("ccc",20,68);
person[3]=new Teacher("ddd",62,52620);
person[4]=new Teacher("eee",26,56642);
for (int i = 0; i < person.length; i++) {
//person[i]的编译类型是 Person,运行类型根据实际情况由JVM来判断
System.out.println(person[i].say());//这里有意i个动态绑定机制
//实现特有的方法 可以用 类型判断+向下转型
if (person[i] instanceof Student){//instanceof用于判断运行类型是否是XXX类型
Student student = (Student) person[i];
student.study();
//也可以合成一句 ((Student)person[i]).study();
}else if(person[i] instanceof Teacher){
((Teacher) person[i]).teach();
}else if (person[i] instanceof Person){
}else {
System.out.println("抱歉,你的输入有误,请重写输入");
}
}
}
}
//Person类
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+"\t"+"年龄="+age;
}
}
//Student类
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;
}
@Override
public String say() {
return "学生="+super.say()+"\t"+"成绩="+score;
}
//特有方法
public void study(){
System.out.println("学生"+"\t"+super.getName()+"\t"+"正在学习");
}
}
//Teacher类
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()+"\t"+"薪水"+salary;
}
//特有方法
public void teach(){
System.out.println("老师"+"\t"+super.getName()+"\t"+"正在上课");
}
}
7.2.9多态参数
多态参数:
方法定义的形参类型是父类类型,实参类型允许为子类类型。
应用实例1:主人喂食
应用实例2:员工
/*
*1、定义员工类Employee,包含姓名和月工资(private),以及计算年工资getAnnual的方法。
- 普通员工和经理继承了员工,经理类多了奖金bonus属性和管理manage方法,
- 普通员工多了work方法,普通员工和经理类要求分别重写getAnnual方法
- 2、测试类中添加一个方法showEmpAnnual(Employee e),实现获取任何员工对象的年工资,
- 并在main方法中调用该方法(e.getAnnual())
- 3、测试类中添加一个方法,testWork,如果是普通员工,则调用work方法,如果是经理,则调用manage方法*/
//1、定义员工类Employee,包含姓名和月工资(private),以及计算年工资getAnnual的方法。
public class Employee {
private String name;
private double monthSalary;
public Employee(String name, int monthSalary) {
this.name = name;
this.monthSalary = monthSalary;
}
public double getAnnual(){
System.out.print(getName()+"年工资=");
return 10*monthSalary;//一年工作10个月
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getMonthSalary() {
return monthSalary;
}
public void setMonthSalary(double monthSalary) {
this.monthSalary = monthSalary;
}
}
//普通员工和经理继承了员工,经理类多了奖金bonus属性和管理manage方法,
// * 普通员工多了work方法,普通员工和经理类要求分别重写getAnnual方法
public class OrdinaryEmp extends Employee{
public OrdinaryEmp(String name, int monthSalary) {
super(name, monthSalary);
}
public void work(){
System.out.println(getName()+"正在工作...");
}
@Override
public String getName() {
return "普通员工"+super.getName();
}
@Override
public double getAnnual() {
return super.getAnnual();
}
}
//普通员工和经理继承了员工,经理类多了奖金bonus属性和管理manage方法,
//* 普通员工多了work方法,普通员工和经理类要求分别重写getAnnual方法
public class Manager extends Employee{
private double bonus;
public Manager(String name, int monthSalary, double bonus) {
super(name, monthSalary);
this.bonus = bonus;
}
public void manage(){
System.out.println(getName()+"正在管理...");
}
@Override
public String getName() {
return "经理"+super.getName();
}
@Override
public double getAnnual() {
return super.getAnnual()+bonus;
}
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
}
public class Test {
public static void main(String[] args) {
Employee employee = new OrdinaryEmp("江仔", 5200);
System.out.println(employee.getAnnual());
//注意:因为编译类型是employee,所以能够调用的方法只能是employee的,不能去调用运行类型OrdinaryEmp的方法
//employee.work(); 这种是错的
//下面将其employee向下转型了,编译类型则变成了OrdinaryEpm,所以可以调用其含有的方法了
OrdinaryEmp ordinaryEmp = (OrdinaryEmp)employee;
ordinaryEmp.work();
Employee employee1 = new Manager("小王",5200,2000);
System.out.println(employee1.getAnnual());
Manager manager =(Manager) employee1;
manager.manage();
System.out.println("=============================");
Test test = new Test();
test.showEmpAnnual(employee);
test.testWork(employee);
test.showEmpAnnual(employee1);
test.testWork(employee1);
}
public void showEmpAnnual(Employee e){
System.out.println(e.getAnnual());
}
public void testWork(Employee e){
if (e instanceof OrdinaryEmp){
((OrdinaryEmp) e).work();
}else if (e instanceof Manager){
((Manager) e).manage();
}else {
System.out.println("输入有误,请重新输入");
}
}
}
第八章
Object类详解
8.1 equals方法
8.1.1 equals方法介绍
equals方法:
= = 和equals的对比(面试题)
= = 是一个比较运算符
1、 = = 既可以判断基本类型,也可以判断引用类型
2、 = = 如果判断基本类型,是判断值是否相等。 示例: int i = 10; double d = 10.0;
3、 = = 如果判断引用类型,判断的是地址是否相等,也就是判定是不是同一个对象
4、equals:是Object类中的方法,只能判断引用类型,也就是判定是不是同一个对象
5、默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等
8.1.2 equals源码查看
1、把光标移动到要查看的方法上去,然后快捷键,快捷键一般是 CTRL+B,我的是F12
2、如果快捷键不行,按照以下方法可查看
3、实在没有就只能配置咯,要记得自己的jdk放在哪个位置的哦
8.1.3 equals方法重写
应用实例:判断两个Person对象的内容是否相等,如果两个Person对象的各个属性值都一样,则返回true,反之返回false
注:在没有重写equals方法的时候,调用的equals还是Object超类的equals。
/*判断两个Person对象的内容是否相等,如果两个Person对象的各个属性值都一样,则返回true,反之返回false*/
public class EqualsExsercise01 {
public static void main(String[] args) {
Person person1 = new Person("tom", 20, '男');
Person person2 = new Person("tom", 20, '男');
System.out.println(person1.equals(person2));
//1、false,此时是调用Object超类的equals,因为是两个对象所以false
//2、true,此时重写过后,是调用Object子类的equals方法,因为属性完全相同所以返回true
}
}
class Person{
private String name;
private int age;
private char gender;
public Person(String name, int age, char gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
public boolean equals(Object obj) {//重写equals方法
//先判断是不是同一个对象,如果是则直接返回true
if (this == obj) {//使用this关键字来表示Person对象
return true;
} if (obj instanceof Person) {//先判断两个对象是否是同一个类型,同一个类型才能够进行比较
//如果不是同一个对象,要通过向下转型为Person对象,再去调用其属性去判断
Person person = (Person) obj;
return this.name.equals(person.name) && person.age == this.age && person.gender == this.gender;
} else {//不是同一个类型就返回false
return false;
}
}
}
8.1.4 equals 练习题
8.2 hashCode方法
8.2.1 hashCode介绍
1、可以提高具有哈希结构的容器的效率
2、两个引用,如果指向同一个对象,哈希值肯定一样
3、两个引用,如果指向不同的对象,哈希值肯定不一样
4、哈希值主要是根据地址号来的,不能完全将哈希值与地址搞成等价的
5、在集合中,hashCode如果需要,一般都是要重写的。见后面详解
8.3 toString方法
8.3.1 toString介绍
基本介绍:
默认返回:全类名(包名+方法名)+@+哈希值的十六进制,【查看Object的toString方法】,子类往往重写toString方法,用于返回对象的属性
package com.xiaowang.hashCode;
//如果没有重写toString方法,则会调用Object的toString方法,
// 返回:全类名(包名+方法名)+@+哈希值的十六进制
public class A {
public static void main(String[] args) {
Monster monster = new Monster("wang", "sing", 1200);
System.out.println(monster.toString());
//未改写输出:com.xiaowang.hashCode.Monster@10f87f48
System.out.println(monster);
//默认调用toString,输出:Monster{name='wang', joc='sing', salary=1200.0}
}
}
8.3.2 toString方法重写
重写toString方法:
打印对象或者拼接对象时,都会自动调用toString形式。
public class A {
public static void main(String[] args) {
Monster monster = new Monster("wang", "sing", 1200);
System.out.println(monster.toString());
//未改写输出:com.xiaowang.hashCode.Monster@10f87f48
System.out.println(monster);
//默认调用toString,输出:Monster{name='wang', joc='sing', salary=1200.0}
}
}
class Monster{
private String name;
private String joc;
private double salary;
public Monster(String name, String joc, double salary) {
this.name = name;
this.joc = joc;
this.salary = salary;
}
//toString重写的方法可以通过快捷键ALT+SHIFT+INSERT改写,
// 一般是直接返回类的属性,当然可以自己更改返回的数据
//返回:Monster{name='wang', joc='sing', salary=1200.0}
@Override
public String toString() {
return "Monster{" +
"name='" + name + '\'' +
", joc='" + joc + '\'' +
", salary=" + salary +
'}';
}
}
3、当直接输出一个对象时,toString方法会被默认调用
上面代码中的这部分:
System.out.println(monster);
//默认调用toString,输出:Monster{name='wang', joc='sing', salary=1200.0}
8.4 finalize方法
8.4.1 finalize方法介绍
1、当对象被回收时,系统自动调用该对象的finalize方法,子类可以重写该方法,做一些释放资源的操作
但是当运行的时候,不一定会运行出程序员重写的finalize方法,因为这是系统自己有一个GC算法来调用,
2、什么时候被回收:当某一个对象没有任何引用时,则jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁对象,在销毁该对象之前,会先调用finalize方法
3、垃圾回收机制的调用,是由系统来决定(即有自己的GC算法),可以通过System.gc()主动触发垃圾回收机制,但是有概率,不一定会调用成功
因为gc算法不会阻塞,所以下面的输出语句也可以输出来
8.5断点调试
8.5.1断点调试介绍
断点调试,是在运行状态中,也就是在对象运行类型中进行调试。
断点调试是指在程序的某一行设置一个端点,调试时,程序运行到这一行就会停住,然后可以一步一步的往下调试,调试过程中可以看各个变量当前的值,出错的话,会在其行先报错误,停下,进行分析找到这个bug。
例:
A extends B; B b = new A();当调用 b.xx()时,是在运行类型A中,而不是编译类型 B
断点各种控制的快捷键:
F7(跳入方法体(函数方法)) F8(跳过 单步调试)
SHIFT + F8 (跳出方法体) F9(resume,执行下一个断点)
界面断点调试时的控制台
8.5.2 断点调试查看源码
1、使用快捷键强制进入方法体查看: AIT + Shift + F7
2、配置一下环境,然后使用 F7 Step Into跳入方法体的快捷键
配置过程:
只要进入的方法体里面还有方法,可以一直F7 Step Into 进入查看源码
跳出则 Shift + F8,跳出时一层一层的跳出
8.5.3 断点调试出现的问题
当我今天去调试我的代码的时候,发现debug控制台的所有按钮都不能用,如下图:
但是,运行时完全没有问题的,然后转换到输出控制台时下面错误:
Unexpected error (103) returned by AddToSystemClassLoaderSearch Unable to add C:\Users\�����\AppDa
通过我的不懈努力,终于解决,解决方案:
然后关闭debug调试,重写进行调试,就可以用啦。