Java笔记(上)
一.Java的背景及环境
1.命令行的使用
1.跳转到d盘–d:
2.显示目录–dir
3.新建文件夹–md 文件夹名字
4.进入某个文件夹–cd 文件名
5.直接进入某个文件–cd d:\java\class
6.返回上层–cd…
7.删除txt文件–del 文件名
8.把所有txt结尾的都删掉–del *.txt
9.删除文件目录(要保证文件夹内为空的)–rd 文件夹名字
10.删除文件夹中的所有内容–del 文件夹名字
11.System.exit(0);终止程序后面不再执行
12.ctrl+左键–跳转至该方法
13.alt+ins–加入set/get方法及构造器
2.Java语言特点
面向对象(封装 继承 多态)、健壮性、跨平台性
3.Java环境的搭建
建立顺序:项目–模块–类
4.idea快捷键
1.ctrl+d–复制当前行到下一行
2.ctrl+p–查看构造器
3.ctrl+x(y)–剪切也可用于删除所在行
4.ctrl+alt+l–格式化代码
5.alt+shift+↑或↓–上下移动代码
6.ctrl+/–注释
7.ctrl+shift+/–多行注释
8.ctrl+F12–在类中搜索方法
9.alt+a–跳转到本行结尾
10.ALT+CTRL+T–异常
11.ALT+INS–get/set/构造器/toString等
5.导入、重命名、与删除
1.导入:文件–新建–从现有导入模块–从带黑点处的文件导入
新建模块–然后直接把别人的代码复制粘贴过来
2.重命名:全部修改
3.删除:文件直接删除。
//模块用移出模块,但是没有真的删除仅在界面中删除了。点击对应模块,然后在上面进行删除,删不干净。(删干净要去对应文件夹中删除,然后重启idea)
4.打开工程:文件–打开–找带黑点的
5.关闭工程:文件–关闭工程
6.标题
public static void main(String[] args){
}
二.java语言基础
一.变量
1.注释
1.单行注释://
2.多行注释:/* */ (打/ *+回车)
3.文档注释(java所特有的):/** */ (打/**+回车)可以被JDK提供的工具javadoc解析,生成一套以网页文件形式体现的该程序的说明文档。
2.字面量
1.字面量:数据在程序中的书写格式,如String s=“123”;
2.转义字符:’/n‘–换行,'/t’制表符等
3.输出:system.out.printfln(“字符串”+数字);
3.变量
变量在其作用域内有作用。同一个作用域内变量名不能相同
1.定义: 数据类型 变量名 = 初始值;
2.关键字:如static等
3.保留字:现在未使用,但是以后可能会使用,所以尽量不用的字。
4.标识符:字母、数字、下划线、$,数字不能打头,区分大小写。java采用unicode编码,因此可以用中文,但是不建议这样写。
5.变量类型:按照数据类型来分,基本数据类型:字节byte short int long float double char boolean 引用数据类型:类(字符串)class 接口interface 数组
4.基本数据类型的使用
基本数据类型有各自的默认值,引用数据类型默认值均为空
1.byte:8bit(8位=1个字节) -128~127
2.short:2字节
3.int:4字节(常用)
4.long:8字节 定义时最好以l结尾(大小写都可以;没加l默认时int型,超出范围会报错。)
byte bb=125;//写129会出错
System.out.println(bb);
long l=123l;
System.out.println(l);
5.float:4字节 后面要加f float ff=22.2f;
6.double:8字节
float ff=12.3f;
System.out.println(ff);
double dd=21.3;
System.out.println(dd);
7.char及转义字符或uncode:2字节
char c1='\u0043';//uncode
System.out.println(c1);
8.boolean:仅可取true false
boolean bl=true;
if (bl)
{
System.out.println("yes\\n123");\n换行 \\n输出\n
}
else
System.out.println("no");
5.数据类型转换与强制类型转换
1.数据类型转换(自动类型提升,不包括boolean):byte、char、short→int→float→double
’a‘+10=asc码+10,即byte、char、short之间做运算结果为int以上的类型,除非用强制类型转换(同类型也一样)
2.强制类型转换:注:string不能被转换除非用包装类: int num1= Integer.parseInt(str1);
float f=43.9f;
char c=(char)f;
System.out.println(c);//ASC码对应的的符号+,不会四舍五入
int num=130;
byte b=(byte)num;
System.out.println(b);//输出-126
short s=5;
s=(short)(s+2);//对
s=s+2;//不对,int=short+int
6.String类型(引用类型)
+时碰上引用类型则为连接
1.String 可以和八种基本类型做运算,运算结果为String类型,且只可以为连接运算,相当于连接到一起。(单个字符做运算最好用字符,做连接最好用字符串)
String s1="1";//注意首字母大写。
int s2=2;
String str2=3.5f+""//数字与字符串连接,str2为"3.5"
String s3=s1+s2;//连接运算等价于拼接到一起
System.out.println(s3);//输出12
System.out.println("可以用加号与数据进行连接,如130+0="+i2);
//练习
char cs='a';
int num=10;
String str="hello";
System.out.println(cs+num+str);// 107hello
System.out.println(cs+str+num);// ahello10
System.out.println(cs+(num+str));// a10hello
System.out.println(str+num+cs);// hellow10a
System.out.println("* *");//* *
System.out.println('*' + '\t' + '*');//93
System.out.println('*' + "\t" + '*');//* *
System.out.println('*' + '\t' + "*");//51*
System.out.println('*' + ('\t' + "*"));//* *
二.运算符
1.算术运算符
int a=12;
int b=5;
double c = a / (b + 0.0);
// double c = (double) a / b;
// double c = a / (double) b;
System.out.println(c);
System.out.println(a / b * b);
2.%的正负与前面的数一致
short i=1;
i++;//自增一不会改变变量的一致性,仍为short,与i+1不一样
System.out.println(i);//2
byte by=127;
by++;
System.out.println(by);//-128
2.赋值运算符
1.=:赋值运算符(支持连续赋值)
c%=b;//c=c%b;前者不会改变变量的数据类型
开发中,让一个整数实现+1的方式的几种方式
int d = 1;
d = d + 1;
d += 1;//+n(n非1)比较好
d++;//+1 比较好
int i = 1;
i *= 0.1;//0 1*0=0
i++;//1
System.out.println(i);
int n=10;
n += (n++) + (++n);//n=n+(n++)+(++n) n=10+10+10+12
3.比较运算符
4.逻辑运算符
&& 、||注意短路法则:从前往后依次运算,与在前知结果不再运算,或在前知结果要全算。
&、|没有短路法则。
&与&&运算结果相同,参与运算的变量结果可能会不同;
5.位运算
仅支持整形或字符型
反移与异或
6.三元运算符
条件表达式?表达式1:表达式2
条件表达式的类型时boolean
7.运算符的优先级
三.程序流程控制(顺序、选择、循环)
1.if-else if-else结构
2.switch case结构
3.特殊关键字的使用(break continue)
int i, j;
a1: for ( i = 1; i < 4; i++) {
for ( j = 1; j < 10; j++) {
if (j % 4 == 0)
continue a1;输出123123123
//break a1; 输出123
else
System.out.print("j1="+j);
}
System.out.println("j2="+j);
}
四、数组
数组属于引用类型,数组元素的类型可以是引用类型(String)也可以是基本数据类型。
1.一维数组
(1)数组的建立
①静态初始化
int[] a;
a = new int[]{1, 2, 3, 4};
②动态初始化
String[] s = new String[5];
③匿名初始化
test.show(new String[]{“aa”,“bb”,“cc”});
char型为0并不是’0’
(2)注意
①字符数组c[2]={‘a’,‘b’,‘c’};System.out.println©输出的不是地址值而是abc,这一行的值(\0结束);
②其他的数组(包括字符串)则为地址值。println相当于%s。char的二维数组c输出地址,c【i】输出i行的字符。
④Customer[] custs = customerList.getAllCustomer();数组对象可以一下获得所有的对象数组
(3)数组元素的默认初试化值
(1) int-0
(2) float、double-0.0
(3) char-0(ASC码为0并不是’0)’
(4) String-null不是"null"
(5) boolean-false
char[] c=new char[]{'a','b','c'};
System.out.println(c);//abc
String[] s=new String[]{"123","345"};
System.out.println(s);//地址
//1.一维数组的声明和初始化,一旦初始化完成其长度就确定了
//静态初始化
int[] a;
a = new int[]{1, 2, 3, 4};
int[] a ={1, 2, 3};
//动态初始化(前面[]空着 ,后面的要么写出长度要么写出元素)
String[] s = new String[5];
//2.调用指定位置的元素
s[0] = "王明";
s[1] = "王赫";
s[2] = "3";
s[4] = "4";
//3.获取数组的长度,借助length属性
System.out.println(s.length);
System.out.println(a.length);
//4.遍历数组元素
for(int i=0;i<s.length;i++)
{
System.out.println(s[i]);
}
//5.数组元素的默认初试化值:
// (1) int-0
// (2) float、double-0.0
// (3) char-0//ASC码为0并不是'0'
// (4) String-null不是"null"
// (5) Boolean-false
char[] arr=new char[4];
for (int i=0;i<arr.length;i++)
System.out.println(arr[i]);
//char型为0并不是'0'--有一个字符数组c[2]={'a','b','c'};System.out.println(c)输出的不是地址值而是abc,这一行的值(\0结束);其他的数组(包括字符串)则为地址值。println相当于%s
if(arr[0]==0)
{
System.out.println("as");
}
//6.数组的输入
Scanner scanner=new Scanner(System.in);
int[] a=new int[3];
for(int i=0;i<a.length;i++)
{
a[i]=scanner.nextInt();
}
for(int i=0;i<a.length;i++)
{
System.out.println(a[i]);
}
test.show(new String[]{"aa","bb","cc"});//创建数组,仅用一次时可以这样使用
2.二维数组
(1)二维数组的建立
①静态初始化
int[][] arr=new int[][]{{1,2,3},{4,5,6}};
int arr[][]=new int[][]{{1,2,3},{4,5,6}};
int[] arr[]=new int[][]{{1,2,3},{4,5,6}};
int[][] arr= {{1,2,3},{1,2,3}};
②动态初始化
String[][] arr = new String[3][2];//不可以写{}赋值
String[][] arr = new String[3][];//可以省略后面的列,行的个数必须写
③匿名初始化
f.show(new arr{{1,2,3},{1,2,3}});
(2)获取数组长度
arr1[0].length--列数
System.out.println(arr1.length);//长度为2
System.out.println(arr1[1].length);
(3)初始化
int[][] a1 = new int[3][3];
System.out.println(a1);//地址值(第0行列地址)
System.out.println(a1[0]);//地址值(第0行首列地址)
System.out.println(a1[0][0]);//0
float[][] a2=new float[3][3];
System.out.println(a2);//地址值(第0行列地址)
System.out.println(a2[0]);//地址值(第0行首列地址)
System.out.println(a2[0][0]);//0.0
String[][] a3=new String[3][3];
System.out.println(a3);//地址值(第0行列地址)
System.out.println(a3[0]);//地址值(第0行首列地址)
System.out.println(a3[0][0]);//null
boolean[][] a4=new boolean[3][3];//
System.out.println(a4);//地址值(第0行列地址)
System.out.println(a4[0]);//地址值(第0行首列地址)
System.out.println(a4[0][0]);//flase
int[][] a5=new int[4][];
System.out.println(a5[2]);//不论什么数据类型用此方法定义则a5[1]为null
System.out.println(a5[2][0]);//报错,空指针异常;因为列为空,找不到元素。
总结:定义时:a[1][1]; 则,a:行地址 a[0]:列地址 a[0][0]:对应类型默认值
定义时:a[1][]; 则,a:行地址 a[0]:null a[0][0]:报错
三.面向对象
三条主线
1.Java类及类的成员:属性、方法、构造器、代码块、内部类
2.面向对象的三大特征:封装性、继承性、多态性
3.其他关键字:this、super、static、final、abstract、interface、package、import等
一、类的成员(属性、方法)及如何设计类的成员
1.类与对象的创建和使用
①person p1=new person();(调用哪个类的属性和方法就要先创建哪个类中的对象)
②person p3=p1; p1与p3相当于是一个,即p3指向p1的空间,对p3的属性进行修改p1的属性也会修改。
③person p=null;使其成为空指针,不指向任何对象,为垃圾对象。
④如果创建了多个对象,每个对象都独立的拥有一套类的属性(非Static属性)。意味着对一个对象修改了属性a的值,则不影响另一个对象属性a的值。
public class java10 {
public static void main(String[] args) {
//创建Person类的对象=类的实例化=实例化一个类,Scanner scanner=new Scanner(System.in);就是类的实例化
person p1=new person();
Scanner scanner=new Scanner(System.in);
//调用属性 对象.属性
p1.name=scanner.next();
p1.isMale=scanner.nextBoolean();
System.out.println("姓名"+p1.name+"性别"+p1.isMale);
//调用方法 对象.方法
p1.eat();
p1.sleep();
p1.talk("chinese");
//如果创建了多个项目,每个对象都独立的拥有一套类的属性(非Static属性)。意味着对一个对象修改了属性a的值,则不影响另一个对象属性a的值。
person p2 = new person();
System.out.println(p2.name);//每一个对象都是一个独立的,p2.name与p1.name不是一个。所以为默认值null
System.out.println(p2.isMale);//false]
//可以创建p3,让p3等于p1,则对p3的属性进行修改p1的属性也会修改。
person p3=p1;//将p1对象的地址赋给p3(p1与p3共用一个对象实体),导致p1和p3指向了堆空间的同一个对象
p3.age=10;
System.out.println(p3.name+" "+p1.age);
}
}
//创建类并设计类的成员
class person{
//属性(成员变量、field、域、字段)--身高、体重
String name;
int age=1;
boolean isMale;
//方法(行为、功能、成员方法、函数、method)
public void eat(){
System.out.println("人可以吃饭");
}
public void sleep(){
System.out.println("人可以睡觉");
}
public void talk(String lanuage){
System.out.println("人可以说话,使用的是"+lanuage);
}
2面试题练习
public class java11 {
public static void main(String[] args) {
test t=new test();
int aa=1;
t.a=1;
t.fun(t.a);
}
}
class test{
int a;
int b=2;
public void fun(int i){
System.out.println(2+i+b);//2+1+2=5
}
}
3.属性与局部变量的比较
(1)相同点:
①定义变量的格式:数据类型 变量名 = 变量值;
②先声明,后使用
③变量都有其对应的作用域
(2)不同点:
①在类中声明的位置不同
属性:直接定义在类的一对{}内
局部标量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量
②关于权限修饰符的不同
属性:可以在声明属性时,指明其权限(常用权限修饰符:private、public、缺省、protected)
局部变量:不允许使用权限修饰符
③默认初始化值的情况:
属性:根据其类型有默认初始化值。
局部变量:没有默认初始化值,必须要显示赋值。(形参变量在调用时赋值即可)
④在内存中加载的位置不一样
属性:加载在堆空间中(非static属性)
局部变量:加载在栈空间中
4.类中方法的创建与使用
(1)方法的声明:权限修饰符 返回值类型 方法名(形参列表–可有可无,可以有多个){方法体;}
①权限修饰符:private、public、缺省、protected
②有返回值:如果方法有返回值,必须在方法声明时,指定返回值的类型,同时方法中需要使用retuen返回指定类型的变量或常量。
有返回值时必须要返回一个
if(a>18)retuen name;会报错,因为只有满足条件时才有返回值,不满足必须有返回值的情况。
else return name1;加上else这样就就不会报错了
③无返回值:如果方法无返回值,用void,通常不需要使用retuen,如果使用也可以return; return后不可以有表达式。
④方法名:遵守标识符的规范。
⑤形参列表:方法可以声明0个、1个或多个形参,格式: 数据类型1 形参1,数据类型2 形参2,…
⑤方法体:方法功能的体现。
(2)return关键字的使用
①使用范围:使用在方法体中
②作用:结束一个方法、针对于有返回值的方法"return 数据"返回指定的数据。
③注意:return关键字后面不可以有声明执行语句。
(3)方法的使用
①方法的使用中,可以直接调用当前类中的属性、方法。
public int getSum(int[] arr) {
int sum = 0;
for (int i = 0; i < 10; i++) {
sum += arr[i];
}
return sum;
}
//求数组平均值
public int getAvg(int[] arr) {
return getSum(arr) / arr.length;
}
②在类外调用该类属性以及方法:类名.属性 类型.方法(参数)
③方法中可以再调用方法
④方法a中也可以调用方法a–递归(必须有递归结束的标志)
⑤方法中不可以定义方法。
⑥进入该类则可以调用该类中的属性以及方法
⑦想要调用哪个类(非本类)的方法就创建哪个类的对象
⑧方法的形参可以是类
public void fun(fun1 f){
f.funs();
}
5.练习
练习1(文档注释)
//练习2--一般分开写
public class java14_2 {
public static void main(String[] args) {
Person person=new Person();
person.name="波波";
person.age=24;
person.sex=0;//添加文档注释后,可显示文档注释
person.study();
person.showAge();
person.addAge(2);
person.showAge();//system.out.plintln("person.age");
Person person2=new Person();
person2.study();
person2.showAge();
person2.addAge(2);
person2.showAge();
}
}
class Person{
String name;
int age;
/**
* sex:0--女性 1--男性
*/
int sex;
public void study(){
System.out.println("studying");
}
public void showAge(){
System.out.println(age);
}
public int addAge(int i){
age+=i;
return age;
}
}
练习2(求圆的面积)
public class java14_3 {
public static void main(String[] args) {
Circle circle=new Circle();
Scanner scanner=new Scanner(System.in);
circle.r=scanner.nextDouble();
circle.area();
}
}
class Circle{
double r;
double area;
public void area(){
double area = Math.PI * r * r * r;//如果getr的方可,也可以用getr()
System.out.println(area);
}
}
* 练习3(创建对象,想要调用谁的方法就创建哪个类的对象)
public class java14_4 {
public static void main(String[] args) {
java14_4 test = new java14_4();//创建对象,想要调用谁的方法就创建哪个类的对象
System.out.println(test.method());
System.out.println(test.methods(3,4));
}
public int method() {
int i, j = 0;//j必须要赋值的原因时for的内部循环以一定会执行,为了保证他有值所以必须要给j赋值
for (i = 0; i < 10; i++) {
for (j = 0; j < 8; j++) {
System.out.print("*");
}
System.out.println();
}
return i * j;
}
public int methods(int m, int n) {
int i = 0, j = 0;
for (i = 0; i < m; i++) {
for (j = 0; j < n; j++) {
System.out.print("*");
}
System.out.println();
}
return n * m;
}
}
* 练习4(对象数组,创建后需要给每一个位置都new一个对象)
1.(目标类型)(math.random()*(b-a+1)+a)–取[a,b]的随机数
2.给数组元素赋值,即给每一个位置(数组元素)创建一个对象
3.students仅在定义他的方法中有效,在其他方法中使用时需要设置形参进行传递,传递后与,其他方法中的形参与定义处的实参具有相同的功能
4.对象数组,创建后需要给每一个位置都new一个对象
for (int i = 0; i < students.length; i++) {
students[i] = new student();//给数组元素赋值,即给每一个位置(数组元素)创建一个对象
students[i].number = i + 1;//给每一个对象的number成员赋值。
}
练习
public class java15 {
public static void main(String[] args) {
student[] students = new student[20];//定义对象数组
for (int i = 0; i < students.length; i++) {
students[i] = new student();//给数组元素赋值,即给每一个位置(数组元素)创建一个对象
students[i].number = i + 1;//给每一个对象的number成员赋值。
students[i].state = (int) (Math.random() * (6 - 1 + 1) + 1);
students[i].score = (int) (Math.random() * (100 + 1));//(100-0+1)+0)==(100-0+1))
//Math.random();随机生成随机数,返回类型为double,生成[0.0~1.0)的随机数。
// Math.random() * ((b - a + 1) + a);范围i从a~b。
//students[i].number = (int)Math.random();
//Math.round(double d);//四舍五入取整返回类型long
}
//遍历学生数组
for (int i = 0; i < students.length; i++) {
students[i].show();
}
System.out.println();
for (int i = 0; i < students.length; i++) {
System.out.println(students[i].show1());
}
System.out.println("年纪为3的信息");
for (int i = 0; i < students.length; i++) {
if (students[i].state == 3) {
students[i].show();
}
}
System.out.println("使用冒泡还需按成绩排序");
for (int i = 0; i < students.length - 1; i++) {
for (int j = 0; j < students.length - 1 - i; j++) {
if (students[j].score < students[j + 1].score) {
student s = new student();//交换所有变量,则定义一个新的对象
s = students[j];
students[j] = students[j + 1];
students[j + 1] = s;
}
}
}
for (int i = 0; i < students.length; i++) {
System.out.println(students[i].show1());
}
}
}
class student {
int number;
int state;
int score;
public void show() {
System.out.println(number + " " + state + " " + score);
}
public String show1() {
return number + " " + state + " " + score;
}
}
优化后
public class java16 {
public static void main(String[] args) {
student[] students = new student[20];
java16 use=new java16();
use.inse(students);
System.out.println("遍历");
use.prints(students);
System.out.println("满足年纪为3的信息");
use.query(students,3);
System.out.println("排序后遍历");
use.sort(students);
use.prints(students);
}
//赋值
public void inse(student[] s){
for (int i = 0; i < s.length; i++) {
s[i] = new student();//给数组元素赋值,即给每一个位置(数组元素)创建一个对象
s[i].number = i + 1;//给每一个对象的number成员赋值。
s[i].state = (int) (Math.random() * (6 - 1 + 1) + 1);
s[i].score = (int) (Math.random() * (100 + 1));//(100-0+1)+0)==(100-0+1))
}
}
//方法:遍历
public void prints(student[] s) {
for (int i = 0; i < s.length; i++) {
s[i].show();
}
}
//输出年纪为3的学生的信息
public void query(student[] s,int n) {
for (int i = 0; i < s.length; i++) {
if (s[i].state == n) {
s[i].show();
}
}
}
//按成绩冒泡排序
public void sort(student[] s){
for (int i = 0; i < s.length - 1; i++) {
for (int j = 0; j < s.length - 1 - i; j++) {
if (s[j].score < s[j + 1].score) {
student ss=new student();//交换所有变量,则定义一个新的对象
ss = s[j];
s[j] = s[j + 1];
s[j + 1] = ss;
}
}
}
}
}
class student {
int number;
int state;
int score;
public void show() {
System.out.println(number + " " + state + " " + score);
}
}
6.方法的重载
1.方法的重载:在同一个类中,允许存在一个以上的同名的方法,只要他们的参数个数或者参数类型不同即可。
2.满足条件:两同一不同:同一个类、相同方法名;
参数列表不同:参数个数不同,参数类型不同。
和其他无关:和方法的权限修饰符、返回值类型、形参变量名、方法体没有关
3.通过对象调用方法时,如何确定某一个指定的方法:
(1)方法名
(2)参数列表
public class java21 {
public static void main(String[] args) {
java21 test=new java21();
System.out.println(test.max(2,3));
}
//如下的三个方法构成重载
public void mOL(int i) {
System.out.println(i * i);
}
public void mOl(int i, int j) {
System.out.println(i * j);
}
public void mOL(String i) {
System.out.println(i);
}
//如下的三个方法构成重载
public int max(int i, int j) {
if (i > j)
return i;
else
return j;
}
public double max(double i, double j) {
if (i > j)
return i;
else
return j;
}
public double max(double i,double j, double k){
if(i>j && i>k)
return i;
else if(i>k && i<j)
return j;
else
return k;
}
}
4.可变个数的形参(jdk5.0新增内容)
①格式: 数据类型…变量名
②可变形参的传递参数可以是0个1个或多个,但是类型要符合形参的类型
③可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载
④数组和可变形参相当于是一种方法,不可以共存。即可变个数形参的方法与本类中方法名相同、形参类型也相同的数组不构成重载。
⑥在一个方法中可变个数形参最多只可以声明一个,且如果有多个参数,可变形参必须声明在末尾。
public class java22 {
public static void main(String[] args) {
java22 test = new java22();
test.show(12);
test.show("hellow");
test.show("helloe", "world");//可变形参的传递参数可以是0个1个或多个,但是类型要符合形参的类型
test.show();
test.show(new String[]{"aa","bb","cc"});//创建数组,仅用一次时可以这样使用
}
public void show(int i) {
System.out.println(1);
}
public void show(String s) {
System.out.println(2);
}
public void show(String... strs) {//可变个数的形参
for (int i=0;i<strs.length;i++){
System.out.println(strs[i]);//相当于数组
}
}
public void show(double s,String...strs) {//可变个数形参在方法的形参中,必须声明在末尾
System.out.println(2);
}
//public void show(String[] str){}数组和可变形参相当于是一种方法,不可以共存,即可变个数形参的方法与本类中方法名相同、形参类型也相同的数组不构成重载。
}
7.方法参数的值传递机制
1.变量或引用类型
如果变量是基本数据类型,此时赋值的是变量所保存的数据值;如果是引用数据类型,此时赋值的是变量所保存的数据的地址值。
public static void main(String[] args) {
int m = 10;
int n = m;
System.out.println("m=" + m + ",n=+" + n);
n = 20;
System.out.println("m=" + m + ",n=+" + n);
System.out.println("************引用数据类型************");
Order o1 = new Order();
o1.orderId = 1001;
Order o2 = o1;//复制以后o1与o2指向同一个变量实体
o2.orderId = 1002;
System.out.println("o1.orderId=" + o1.orderId + ",o2.orderId=" + o2.orderId);//1002 1002
}
}
2.形参的值传递机制
(1)形参与实参
①形参:方法定义时,声明的小括号内的参数。
②实参:方法调用时实际传递给形参的数据。
(2)传递机制:
实参是基本数据类型:传递的时实参真实存储的数据值。在方法中更改传入的值,主函数中的值不变。(值传递不改变的值:方法随用随分配用完就释放)
public class java24 {
public static void main(String[] args) {
int m = 10;
int n = 20;
System.out.println("m=" + m + ",n=" + n);
java24 test = new java24();
test.swap(m, n);//方法:随用随分配,用完就释放,所以尽在方法中交换了m与n的值,实际的m与n没有交换
System.out.println("m=" + m + ",n=" + n);//10 20
int[] a=new int[]{10,20,30};//地址传递可以改变
test.swap(a,0,1);
System.out.println(a[0]+" "+a[1]);//20 10
}
public void swap(int m, int n) {
int t;
t = m;
m = n;
n = t;
}
public void swap(int[] arr, int i, int j) {
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
参数是引用数据类型:此时实参赋给形参的是实参存储数据的地址值。(地址传递会改变实参的值)
public class java25 {
public static void main(String[] args) {
Data d = new Data();//d指向类的空间
d.m = 10;
d.n = 20;
System.out.println("m" + d.m + ",n=" + d.n);
// int t;
// t = d.m;
// d.m = d.n;
// d.n = t;
// System.out.println("m" + d.m + "n=" + d.n);m.n的值不会改变
java25 test=new java25();
test.swap(d);
System.out.println("m" + d.m + ",n=" + d.n);
}
public void swap(Data data){//也指向类的的空间,不同指针指向同一个空间,更改空间内变量的值,释放指针,虽然释放了指针但空间还在
int t;
t = data.m;
data.m = data.n;
data.n = t;//m、n的值改变了
}
}
class Data {
int m;
int n;
}
每次new一个对象相当于建立了一个新的空间,相应的操作则修改空间中相应的值,传递参数为地址时,让新的对象(形参)也指向这个空间。如果v(形参)=va,则形参指向va的存储空间,实参仍指向原来的空间。调用对应空间的方法时如果用到该类中的成员则会使用对应空间的值。当形参为非基本数据类型时,则实参传形参相当于将形参指向实参存储空间的地址。
public class java26 {
public static void main(String[] args) {
java26 j=new java26();
j.first();
}
public void first() {
int i = 5;
value v = new value();//创建了一个存储空间d1与对象v1,v1指向c1,v1--→d1: i=18;
v.i = 25;//d1空间中的v.i修改为了25 v1--→d1: i=25;
second(v, i);//传递参数
System.out.println(v.i);//输出v1,所以上面的结果为18,下面的结果为20
}
public void second(value v, int i) {//创建对象v2,并指向d1 v2--→v1--→d1: i=25;
i = 0;
v.i = 20;//v2--→v1--→d1: i=20;
value va = new value();//创建对象v3并且开辟了一个新的空间 v2--→v1--→d1: i=20; v3--→v2:d2.i=18
v.i=va.i;//基本数据类型的改变,不涉及到地址。所以仅将值赋予v.i。v.i=18 v2--→v1--→d1: i=18; v3--→d2:d2.i=18
// v=va;替换上面的则为: 让v2指向了d2 v1--→d1: i=20; v2--→v3--→d2:v2.i=18
System.out.println(v.i + " " + i);//输出v2.i=18,i=0
}
}
class value {
int i = 18;
}
练习:
public class java27 {
public static void main(String[] args) {
Circle c=new Circle();
PassObject p=new PassObject();
p.printAreas(c,5);
System.out.println("now c.radus "+c.radius);
}
}
class Circle{
double radius;
public double findArea(){
return Math.PI*radius*radius;
}
}
class PassObject{
public void printAreas(Circle c,int time){
System.out.println("r\t\tArea");
for(int i=1;i<=time;i++){
c.radius=i;
System.out.println("i="+c.radius+"area="+c.findArea());//可以调用对应存储空间中的方法,如c.radius设置为5,则c.findArea()带入的radius就是5
}
c.radius=c.radius+1;
}
}
8.方法的递归调用(recursion)
1.什么是递归:一个方法体内调用它自身。
public class java28 {
public static void main(String[] args) {
// int sum = 0;
// for (int i = 1; i <= 100; i++) {
// sum += i;
// }
java28 j=new java28();
System.out.println( j.getSum(10));
}
public int getSum(int n) {
if (n == 1)
return 1;
else {
return n + getSum(n - 1);
}
}
public int getSum1(int n) {
if (n == 1)
return 1;
else {
return n * getSum(n - 1);
}
}
}
9.实例
1.内存解析说明
引用类型变量仅可以存储两类值:(1)null(2)地址值(含变量类型)
引用类型:s[i]、s
基本类型s[i].num
2.匿名对象
1.匿名对象:创建的对象没有名字(创建的对象没有显示的赋给一个变量名p)。匿名对象只可以调用一次–以下的两个对象不是同一个,因为没new一个都是新的。
2.匿名对象常用的使用方法:仅想调用一个类中的方法且不考虑或不需要一个值的时候使用匿名对象。(想用phone中的方法,phone作为另一个类中的方法的形参,直接用匿名对象的方法传参即可)
public class java17 {
public static void main(String[] args) {
phone p = new phone();
System.out.println(p);
p.sendEmail();
p.playGame();
//匿名对象--创建的对象没有名字(创建的对象没有显示的赋给一个变量名p)。匿名对象只可以调用依次--以下的两个对象不是同一个,因为没new一个都是新的。
new phone().sendEmail();
new phone().playGame();
new phone().price = 1999;
new phone().showPrice();//0.0
phomemall pm=new phomemall();
pm.show(p);
//匿名对象常用的使用方法:仅想调用一个类中的方法且不考虑或不需要穿一个值的时候使用匿名对象。(想用phone中的方法,phone作为另一个类中的方法的形参,直接用匿名对象的方法传参即可)
pm.show(new phone());//否则还需要创建一个对象,用该对象作为参数。
new phonemall(new phone());
}
}
class phomemall{
public void show(phone phone){
phone.sendEmail();
phone.playGame();
}
}
class phone {
double price;
public void sendEmail() {
System.out.println("发送邮件");
}
public void playGame() {
System.out.println("玩游戏");
}
public void showPrice() {
System.out.println("手机价格为" + price);
}
}
3.注意
1.主函数中调用此类中的方法,需要创建当前类的对象
public class java16 {
java16 use=new java16();
use.inse(students);
public void inse(student[] s){
for (int i = 0; i < s.length; i++) {
s[i] = new student();//给数组元素赋值,即给每一个位置(数组元素)创建一个对象
s[i].number = i + 1;//给每一个对象的number成员赋值。
s[i].state = (int) (Math.random() * (6 - 1 + 1) + 1);
s[i].score = (int) (Math.random() * (100 + 1));//(100-0+1)+0)==(100-0+1))
}
}
}
2.调用不同类中的方法,需要创建方法所在类的对象,或者用匿名对象(仅用一次的话)。(总结下来就是调哪个类中的方法创建哪个类的对象)
两种写法:
(1)int[] a=f.fun();
(2)int[] a=new int[n];
a=f.fun;
(3)int[] as;
as=f.copyArr(a);
对象也一样:
(1)fun f ;
f= new fun();
(2)fun f = new fun();
4.类的封装
package java18;
public class text {
public static void main(String[] args) {
fun f = new fun();
int[] a = new int[]{1, 2, 3, 43, 53, 23, 14, 91, 81, 71};
int max = f.getMax(a);
System.out.println("最大值是" + max);
int min = f.getMin(a);
System.out.println("最小值是" + min);
int sum = f.getSum(a);
System.out.println("数组的和是" + sum);
int avg = f.getAvg(a);
System.out.println(avg);
f.rearr(a);
f.outArr(a);
System.out.println();
int[] arrcopy=f.copyArr(a);
f.outArr(arrcopy);
System.out.println();
f.sortArr(a);
f.outArr(a);
System.out.println();
int location=f.findArr(a,1);
System.out.println(location);
}
}
//在另一个类文件中
package java18;
public class fun {
//求数组最大值
public int getMax(int[] arr) {
int max = arr[0];
for (int i = 1; i < 10; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}
//求数组最小值
public int getMin(int[] arr) {
int min = arr[0];
for (int i = 1; i < 10; i++) {
if (arr[i] < min) {
min = arr[i];
}
}
return min;
}
//求数组总和
public int getSum(int[] arr) {
int sum = 0;
for (int i = 0; i < 10; i++) {
sum += arr[i];
}
return sum;
}
//求数组平均值
public int getAvg(int[] arr) {
return getSum(arr) / arr.length;
}
//反转数组
public void rearr(int[] arr) {
for (int i = 0; i < arr.length / 2; i++) {
int t;
t = arr[i];
arr[i] = arr[9 - i];
arr[9 - i] = t;
}
}
//复制数组
public int[] copyArr(int[] arr) {
int[] arr2 = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
arr2[i] = arr[i];
}
return arr2;
}
//数组排序
public void sortArr(int[] arr) {
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]) {
int t;
t = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = t;
}
}
}
}
//遍历数组
public void outArr(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]+" ");
}
}
//查找指定元素
public int findArr(int[] arr, int n) {
int i;
for(i=0;i<arr.length;i++)
{
if(arr[i]==n) {
break;
}
}
return i+1;
}
}
二、封装与隐藏
1.封装
比如洗衣机和车,你只需要了解怎么用,不用管他的结构。
程序设计的追求:低耦合,高内聚
(1)低耦合:仅对外暴露少量的方法用于使用
(2)高内聚:类的内部数据操作细节自己完成,不允许外部干涉
总结:把该隐藏的隐藏起来,该暴露的暴露出来。
2.封装性的体现
当我们创建一个类的对象以后,我们可以通过"对象.属性"的方式,对对象的属性进行赋值。这里,赋值操作收到数据类型和存储范围的制约。除此之外,没有其他制约条件。但是,在实际问题中,我们往往需要给属性赋值加入额外加的限制条件。这个条件就不能在属性声明时体现,我们只能通过方法进行限制条件的添加(如:set/get)。同时我们需要用户避免使用"对象.属性"的方式进行赋值。则需要将属性声明为私有的(private)。此时,针对于属性就体现了封装性。
(1)即使设计方一个不可以输入负数的方法,但是还是可以输入负值,因此把这个禁掉,使用private来修饰此属性,使外部不可以调用给此属性赋值,仅可以通过规定的方法来实现
(2)对属性的获取:在想要获取legs属性时无法使用a.legs进行获取,因此要建立一个获取属性的方法,使用时用a.getLegs即可
(3)封装性的体现不等同于封装性:
①将类的属性(xx)私有化(private),同时,提供公共的(public)方法来获取(getxx)和设置(setxx)此属性
②不对外暴露的私有的方法
③单例模式
等
public class java30 {
public static void main(String[] args) {
Animal a = new Animal();
a.setName("柯基");
a.setAge(1);
a.setLegs(4);
//a.legs = 4;
a.show();
a.setLegs(-6);
a.show();
//a.legs = -4;即使设计方一个不可以输入负数的方法,但是还是可以输入负值,因此把这个禁掉,使用private来修饰此属性,使外部不可以调用给此属性赋值,仅可以通过规定的方法来实现
}
}
class Animal {
private String name;
private int age;
private int legs;//存在但是调用不了,即为封装性
//对属性的设置
public void setName(String str) {
if (str != null) {
name = str;
} else
name = null;
}
public String gegName() {
return name;
}
public void setAge(int a) {
if (a > 0)
age = a;
else
age = 0;
}
public int getAge() {
return age;
}
public void setLegs(int l) {
if (l >= 0) {
legs = l;
} else
legs = 0;
//抛出一个异常
}
//对属性的获取:在想要获取legs属性时无法使用a.legs进行获取,因此要建立一个获取属性的方法,使用时用a.getLegs即可
public int getLegs() {
return legs;
}
public void eat() {
System.out.println("动物进食");
}
public void show() {
System.out.println("name=" + name + ",age=" + age + ",leg=" + legs);
}
}
3.权限修饰符(封装性的体现需要权限修饰符来配合)
1.Java规定的4种权限(从小到大排列):private、缺省(defaule–即什么也不写)、protect、public(见java)
修饰符 | 类内部 | 同一个包 | 不同包的子类 | 同一个工程 |
---|---|---|---|---|
private | Yes | |||
(缺省) | Yes | Yes | ||
protected | Yes | Yes | Yes | |
public | Yes | Yes | Yes | Yes |
2.4种权限可以用来修饰类及类的内部结构:属性、方法、构造器、内部类
3.具体的,4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类
修饰类的话只可以用:缺省、public
4.总结封装性:Java提供了4种权限修饰来修饰类及类的内部结构,体现类及类的内部结构在被调用时的可见性的大小。
//包1
public class Order {
private int orderPrivate;
int orderDefault;
protected int orderProtected;
public int orderPublic;
private void methodPrivate() {
orderPrivate = 1;
orderDefault = 2;
orderProtected = 3;
orderPublic = 4;
}
void methodDefault() {
orderPrivate = 1;
orderDefault = 2;
orderProtected = 3;
orderPublic = 4;
}
protected void methodProtected() {
orderPrivate = 1;
orderDefault = 2;
orderProtected = 3;
orderPublic = 4;
}
public void methodPublic() {
orderPrivate = 1;
orderDefault = 2;
orderProtected = 3;
orderPublic = 4;
}
}
public class Test {
public static void main(String[] args) {
Order o = new Order();
//同一个包中的不同类不可以调用private权限
o.orderDefault = 1;
o.orderProtected = 2;
o.orderPublic = 3;
o.methodDefault();
o.methodProtected();
o.methodPublic();
}
}
//包2
//继承类
public class SubOrder extends Order {
public void method(){
//缺省:只可以在同一个包中使用,不同包的子类中不可以调用private与确省
orderProtected=1;
orderPublic=2;
methodProtected();
methodPublic();
}
}
//普通类
public class OrderTest {
public static void main(String[] args) {
Order order=new Order();
//不同包下的普通类(非子类)要使用Order类,不可以调用声明为private、缺省、protected权限的属性与方法
order.orderPublic=1;
order.methodPublic();
}
public void show(Order order){
order.orderPublic=1;//尽可以用公共的
}
}
练习
public class java32 {
public static void main(String[] args) {
Person p=new Person();
p.setAge(23);
System.out.println(p.getAge());
}
}
class Person {
private int age;
public void setAge(int i) {
if (i >= 0 && i <= 130)
age = i;
else
throw new RuntimeException("传入的数据非法");
}
public int getAge(){
return age;
}
}
三、构造器constructor
1.构造器的作用
(1)创造对象:Person p=new Person();//Person()为构造器
(2)初始化对象的属性或结构。
2.构造器的说明
(1)如果没有显示的定义类的构造器的话,则系统默认提供一个空参的构造器,如,Person()
(2)定义构造器的格式:权限修饰符 类型(形参列表){}
(3)一个类中定义的多个构造器,彼此构成重载
(4)一旦我们显示的定义了类的构造器之后,系统就不再提供默认的空参构造器
(5)一个类中至少会有一个构造器
(6)构造器的默认权限与类的权限是相同的
(7)如果属性是一个数组或者是一个对象数组,需要在构造器中来创建每个数组的对象,即使不在构造器中创建对象,也要明确数组的长度
public class java33 {
public static void main(String[] args) {
//创建类的对象
Person p = new Person();//Person()为构造器
p.eat();
Person p1 = new Person("柯基");//初始化对象的属性
System.out.println(p1.name);
}
}
class Person {
//属性
String name;
int age;
//构造器
public Person() {//一旦注释掉,就会报错,因为一旦显示的定义了构造器则系统就不会提供默认的构造器了,然后下面没有可以不写参数的构造器,因此就会报错
System.out.println("person()");//执行Person p=new Person();的时候调用了本构造器
}
public Person(String n) {//创建对象时对name初始化
name = n;//柯基赋值给p1的name
}
public Person(String n, int a) {//创建对象是对name和age初始化
name = n;
age = a;
}
//方法
public void eat() {
System.out.println("人吃饭");
}
}
3.构造器的练习
1.使每次创建构造器时默认给age设置为18
public Person(){每次通过构造器造对象时会使age都为18,注意要给属性age加上private
age=18;
}
public class java34 {
public static void main(String[] args) {
Person p=new Person("柯基",1);
System.out.println(p.getName()+":"+p.getAge());
}
}
class Person {
private String name;
private int age;
public Person(String n, int m) {
name = n;
age = m;
}
public void setName(String n){
name=n;
}
public String getName(){
return name;
}
public void setAge(int n){
age=n;
}
public int getAge(){
return age;
}
}
package java35;
public class java35 {
public static void main(String[] args) {
TriAngle ta=new TriAngle();
ta.setBase(12.2);
ta.setHeight(13.3);
System.out.println("base="+ta.getBase()+",height="+ta.getHeight());
TriAngle tt=new TriAngle(10.1,11.1);
System.out.println("base="+tt.getBase()+",height="+tt.getHeight());
}
}
class TriAngle {
private double base;//底边长
private double height;//高
public TriAngle() {
}
public TriAngle(double b, double h) {
base = b;
height = h;
}
public void setBase(double n) {
base = n;
}
public double getBase() {
return base;
}
public void setHeight(double n) {
height = n;
}
public double getHeight() {
return height;
}
}
4.属性的赋值过程
1.总结:属性赋值的先后顺序
(1)默认初始化值(属性中)
(2)显示初始化值(创建对象时)
(3)构造器中赋值(在构造器中)
(4)通过"对象.方法"或"对象.属性"赋值
1~3叫做初始化,仅可以执行一次,4可以多次执行,最后的值时看复制的先后顺序决定的。
四、拓展知识JavaBean
1.JavaBean:一种Java语言写成的可重用组件。
2.满足以下条件的类则为JavaBean
(1)类是公共的
(2)有一个无参的公共的构造器
(3)有属性,且有对应的get、set方法
public class java36 {
private int id;
private String name;
public java36(){
}
public void setId(int i){
id=i;
}
public int getId(){
return id;
}
public void setName(String str){
name=str;
}
public String getName(){
return name;
}
}
3.UML类图
java10
五、this关键字
1.构造器的使用范围
①this:可以用来修饰属性、方法、构造器,可以理解为当前对象或当前正在创建的对象(构造器中)
2.this修饰属性和方法
①this修饰非构造器中的属性和方法:this理解为当前对象。如有p这个对象,则this.age=age;等价于p.age=age;
②this修饰构造器中的属性和方法:this理解为正在创建的对象。this.属性,this.方法调用当前正在创建的属性或方法。
注:
①在类的方法中,我们可以使用"this.属性"或"this.方法"的方式,调用当前对象的属性或方法,但通常情况下我们选择省略this,但是当方法的形参和类的属性同名时,我们必须显示的使用"this.变量"的方式,表明此变量是属性,而非形参。
②this.show();调用本类中的show方法,但是可以省略this为show();
3.this调用构造器
①我们在类的构造器中,可以显示的使用"this(形参列表)"方式,调用本类中指定的其他(并非本构造器)构造器,但是算是一个对象而并非多个对象
②构造器中不能所用"this(形参列表)"的方式调用自己
③如果一个类中有n个构造器,则最多有n-1个构造器中使用了"this(形参列表)",剩下一个为super()来显示的调用父类构造器;不可以互相调用(形成闭环),比如1掉了2,2掉了3,3不能掉1或2
④规定:"this(形参列表)"必须声明在当前构造器的首行,否则会报错
⑤一个构造器中不可以声明多个"this(形参列表)"去调用多个构造器,比如1掉了2,1再调个3
⑥this调用构造器必须在首行
4.案例
(1)案例一:
public class java37 {
public static void main(String[] args) {
Person p=new Person();
p.setAge(1);
System.out.println(p.getAge());
p.eat();
}
}
class Person {
private String name;
private int age;
//this在关键字中的使用
public Person(String name){
this(name,1);//this()调用构造器必须在首行
this.name=name;
}
public Person(String name,int age){
this.name=name;
this.age=age;
}
//this对方法中的属性使用
public void setName(String name,int age) {
this.name = name;//this表示当前属性
this.age=age;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return this.age;//可为this.age;
}
//this对方法中调用方法的使用
public void eat(){
System.out.println("吃饭");
study();//此行代码省略了一个this,应该为this.study();
}
public void study(){
System.out.println("人学习");
}
}
(2)案例二:在一个类中仅用this表示当前对象,如在Girl中使用this表示当前这个Gril的对象
public class java38 {
//public void names(Boy boy){形参的类型可以是另一个类的类型}
public static void main(String[] args) {
Girl g = new Girl("MM",24);
Boy b = new Boy("GG",24);
b.shot();
b.marry(g);
g.marry(b);
Girl g1=new Girl("M",19);
System.out.println(g1.compare(g));
}
}
class Boy {
private String name;
private int age;
//构造器
public Boy() {
}
public Boy(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 void marry(Girl girl) {
System.out.println("我想娶" + girl.getName());
}
public void shot() {
if (this.age >= 22)
System.out.println("你可以去合法登记结婚了");
else
System.out.println("可以去多谈一谈恋爱");
}
}
class Girl {
private String name;
private int age;
//构造器
public Girl() {
}
public Girl(String name, int age) {
this.name = name;
this.age = age;
}
//封装
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//方法
public void marry(Boy boy) {//可以传入别的类的类型
System.out.println("我想嫁给" + boy.getName());
boy.marry(this);//指当前调用这个方法的girl对象
}
public int compare(Girl gril) {//对象排序,比较两个对象的大小,如果返回正数,则当前对象大;负数,当前对象小;0,当前对象有与形参对象相等
// if (this.age > gril.age) {
// return 1;
// } else if (this.age < gril.age) {
// return -1;
// } else
// return 0;
// }
return this.age - gril.age;
}
}
(3)案例三:一个类作为另一个类的属性,用类的连续调用来区分账号
public class CustomerTest {
public static void main(String[] args) {
Customer customer = new Customer("Jane", "Smith");//Customer中有三个属性,通过构造器仅对前两个初始化了,Account还未初始化
Account account = new Account(1000, 2000, 0.0123);
customer.setAccount(account);//利用set方法将给Customer中Account属性赋值,相当于Customer中Account指向account
// 对象中的值(注意,凡是不是基本数据类型作为形参基本都是指向改形参)
customer.getAccount().deposit(100);//因为要调用的是Jane的存储情况,所以要用这样写,如果直接用account.deposit则不是Jane的,而是account这个对象的,若有多个人直接用的话则会在同一个账户操作
customer.getAccount().withdraw(960);
customer.getAccount().withdraw(2000);
System.out.println("Customer[" + customer.getFirstName() + "," + customer.getLastName() + "] has a account: " +
"id id" + customer.getAccount().getId() + ", annualInterestRate is " + customer.getAccount().getAnnualInterestRate() * 100 + "%,balance is " + customer.getAccount().getBalance());
}
}
class Account {
private int id;//账号
private double balance;//余额
private double annualInterestRate;//年利率
public Account(int id, double balance, double annualInterestRate) {
this.id = id;
this.balance = balance;
this.annualInterestRate = annualInterestRate;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public double getAnnualInterestRate() {
return annualInterestRate;
}
public void setAnnualInterestRate(double annualInterestRate) {
this.annualInterestRate = annualInterestRate;
}
public void withdraw(double amount) {
if (balance >= amount) {
balance = balance - amount;
System.out.println("成功取出" + amount + "钱");
} else
System.out.println("您的余额不足,取款失败");
}
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
System.out.println("成功存入" + amount + "钱");
}
}
}
public class Customer {
private String firstName;
private String lastName;
private Account account;//在一个类中出现自定义类(可以与String做对比,String是一个类,Account也是一个类)
public Customer(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
}
(4)案例四:
(1)每一个对象包含若干属性与方法,使用对象.属性或构造器或者是set方法对该对象中的属性进行赋值;可以使用这个类中的方法完成相关操作,比如获得某属性值直接调用get方法。
(2)连续调用方法时的原理:如bank.getCustomer(1).setAccount(new Account(200));
①bank.getCustomer(1)会进入getCustomer所在的类并且再调用方法是可以使用该类中的属性,如customer中的account
正解,应为getCustomer()有返回值,其返回值类型为Customer所以它可以调用Customer类中的方法
②因为此类中有accout类型的属性,所以可以使用account类中的方法,如setAccount;
总结:调用即进入,进入可使用。
(3)注意类中的属性是数组,要对数组进行对象的建立即对数组长度进行明确(可以构造器中初始化)
(4)同类的方法可以直接调用,不同类的方法需要通过对象才可以调用,或者如(2)中使用返回该类型的函数
public class BankTest {
public static void main(String[] args) {
Bank bank = new Bank();
bank.addCustomer("Josn", "Ms");//注意bank里的报错信息(第八行)
bank.getCustomer(0).setAccount(new Account(2000));
bank.getCustomer(0).getAccount().deposit(100);
double balances = bank.getCustomer(0).getAccount().getBalance();
System.out.println("余额为:" + balances);
bank.addCustomer("c2","ss");
bank.getCustomer(1).setAccount(new Account(200));
bank.getCustomer(1).getAccount().withdraw(100);
double b2=bank.getCustomer(1).getAccount().getBalance();
System.out.println(b2);
System.out.println(bank.getNumberOfCustomer());
}
}
public class Bank {
private Customer[] customers;//可以存放多个客户的数组
private int numberOfCustomer = 0;//记录客户的个数,不写=0默认为0(除了类为null其他都有自己的默认值)
public Bank() {
customers = new Customer[10];//保证创建对象是要把数组建出来,否则就会报错
}
public void addCustomer(String f, String l) {
Customer c = new Customer(f, l);
customers[numberOfCustomer++] = c;//customers的每个元素都是Customer型,所以直接把c赋给第0个元素,正好用numberOfCustomer作为数组的计数。
}
public int getNumberOfCustomer() {
return numberOfCustomer;
}
//获取指定位置上的客户
public Customer getCustomer(int index) {
if (numberOfCustomer > index && index >= 0)
return customers[index];
else
return null;
}
}
public class Customer {
private String firstName;
private String lastName;
private Account account;
public Customer(String f, String l) {
this.firstName = f;
this.lastName = l;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
}
public class Account {
private double balance;
public Account(double init_balance) {
this.balance = init_balance;
}
public double getBalance() {
return balance;
}
public void deposit(double amt){
if(amt>0){
balance+=amt;
System.out.println("存钱成功。");
}
}
public void withdraw(double amt){
if(balance>=amt){
balance-=amt;
}
else
System.out.println("余额不足。");
}
}
六、pakcage与import关键字
1.package关键字的使用:
(1)为了更好的实现项目中类的管理,则提供了包的概念
(2)使用package声明类或者接口所属的包,声明在源文件的首行
(3)包:属于标识符,需要遵循标识符的命名规则或规范、见名知意
(4)每点一次代表一次文件目录
补充:同一个包下,不可以命名同名的接口或类(想要使用其他的包中的类导包即可);不同的包下可以命名同名的接口或类。
2.import关键字的使用
(1)improt:导入(写到(落脚点)类或接口)
(2)在源文件中显示的使用improt结构导入指定包下的类、接口。
(3)声明在包的声明和类的声明之间。
(4)如果需要导入多个结构,则并列写出即可
(5)可以使用"xxx.*"的方式,表示可以导入XXX包下的所有结构
(6)如果使用的类或接口是java.lang包下定义的,则可以省略improt结构。
(7)如果是使用的是本包下定义的类或接口,则可以省略import结构
(8)如果在源文件中,使用了不同包下同名的类,则必须至少有一个类需要以全类名的方式显示。
(9)使用"xxx.*"方式表明可以调用xxx包下所有的结构。但是如果使用的是xxx子包下的结构,则仍需要显示导入(仅可以导入某包内第一层的类或接口,更深层需要再次导入)。
(10)import static:导入指定类或接口中的静态结构。(写到(落脚点)结构–结构:属性或方法)
import java40.Account;
import java40.Bank;
import java40.Customer;
import java.util.Arrays;
import java.util.Date;
import static java.lang.System.*;
import static java.lang.Math.*;
public class java41 {
public static void main(String[] args) {
String info= Arrays.toString(new int[]{1,2,3});
Bank b=new Bank();
Customer c=new Customer("a","b");
java39.Customer c1=new java39.Customer("aa","bb");//两个类或接口同名,则用全类名引用
Data data=new Date();
java.sql.Date d1=new java.sql.Date(213123);
out.println(" ");
Math.round(123.44);
}
}
七、综合运用一:存钱取钱练习
package java42.bean.bean;
/**
* @author
* @Description Customer为实体对象,用来封装客户信息
* @verson
* @date
*/
public class Customer {
private String name;
private char gender;
private int age;
private String phone;
private String email;
public Customer() {
}
public Customer(String name, char gender, int age, String phone, String email) {
this.name = name;
this.gender = gender;
this.age = age;
this.phone = phone;
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
package java42.bean.view;
import com.sun.org.apache.xerces.internal.impl.xs.SchemaNamespaceSupport;
import java42.bean.bean.Customer;
import java42.bean.service.CustomerList;
import java.util.Scanner;
/**
* Descripton:负责菜单的显示和处理用户操作
*/
public class CustomerView {
private CustomerList customerList = new CustomerList(10);
/**
* 显示客户信息管理软件界面
*/
public void enterMainMenu() {
boolean isFlag = true;
while (isFlag) {
System.out.println("----------客户信息管理界面----------");
System.out.println(" 1.添加用户");
System.out.println(" 2.修改用户");
System.out.println(" 3.删除客户");
System.out.println(" 4.客户列表");
System.out.println(" 5.退 出\n");
System.out.println(" 请选择1~5:");
System.out.println("----------客户信息管理界面----------\n");
char menu;
Scanner s = new Scanner(System.in);
menu = s.next().charAt(0);
switch (menu) {
case '1':
addNewCustomer();
break;
case '2':
modifyCustomer();
break;
case '3':
deleteCustomer();
break;
case '4':
listAllCustomer();
break;
case '5':
System.out.print("确认是否推出(Y/N):");
char c;
c = s.next().charAt(0);
if (c == 'y' || c == 'Y') {
isFlag = false;
System.out.println("退出");
}
}
}
}
public CustomerView() {
Customer customer = new Customer("王涛", '男', 23, "123213132", "12@mail.com");//设置一个客户列表,测试有数据的情况
customerList.addCustomer(customer);//把对象加入客户列表
}
/**
* 添加用户操作
*/
private void addNewCustomer() {
System.out.println("----------添加----------");
System.out.print("姓名:");
Scanner s = new Scanner(System.in);
String f = s.next();
System.out.print("性别:");
char gender = s.next().charAt(0);
System.out.print("年龄:");
int age = s.nextInt();
System.out.print("电话:");
String phone = s.next();
System.out.print("邮箱:");
String email = s.next();
//将以上数据封装到对象中
Customer customer = new Customer(f, gender, age, phone, email);//只是将值存入了对象,要将此对象存入顾客数组
boolean isSuccess = customerList.addCustomer(customer);//将customer对象添加至顾客数组,注意addCustomer是有返回值的。添加成功返回1,否则返回0
if (isSuccess) {
System.out.println("添加完成");
} else
System.out.println("客户目录已满,未添加成功");
}
/**
* 修改用户操作
*/
private void modifyCustomer() {
System.out.println("----------修改客户----------");
Scanner s = new Scanner(System.in);
int num;
Customer cust;
for (; ; ) {
System.out.print("请选择待修改客户的编号(-1退出):");
num = s.nextInt();
if (num == -1)
return;
cust = customerList.getCustomer(num - 1);
if (cust == null) {
System.out.println("无法找到指定客户");
} else {//找到了相应的客户
break;
}
}
//找到了客户,修改客户信息
System.out.print("姓名(" + cust.getName() + "):");
String fn = s.next();
if (fn == null)
fn = cust.getName();
System.out.print("性别(" + cust.getGender() + "):");
char gender = s.next().charAt(0);
if (gender == '\0')
gender = cust.getGender();
System.out.print("年龄(" + cust.getAge() + "):");
int age = s.nextInt();
if (age == 0)
age = cust.getAge();
System.out.print("电话(" + cust.getPhone() + "):");
String phone = s.next();
if (phone == null)
phone = cust.getPhone();
System.out.print("邮箱(" + cust.getEmail() + "):");
String email = s.next();
if (email == null)
email = cust.getEmail();
Customer newcust = new Customer(fn, gender, age, phone, email);
Boolean isRepacle = customerList.replaceCustomer(num - 1, newcust);
if (isRepacle == true) {
System.out.println("修改完成");
}
}
/**
* 删除用户操作
*/
private void deleteCustomer() {
System.out.println("----------删除----------");
Scanner scanner = new Scanner(System.in);
int num;
for (; ; ) {
System.out.print("输入待删除客户的编号(-1)退出:");
num = scanner.nextInt();
if (num == -1) {
return;
}
Customer customer = customerList.getCustomer(num - 1);
if (customer == null) {
System.out.println("没有该用户");//没找到进入下一次for循环再让用户输入
} else
break;//找到了就退出,执行相应操作
}
System.out.print("是否确认删除(Y/N):");
char dede = scanner.next().charAt(0);
if (dede == 'Y' || dede == 'y') {
Boolean isdelete = customerList.deleteCustomer(num - 1);
if (isdelete)
System.out.println("删除成功");
else {//可以不写
System.out.println("删除失败");
return;
}
} else
return;//可以不写
}
/**
* 显示用户列表用户操作1
*/
private void listAllCustomer() {
System.out.println("----------客户列表----------");
int total = customerList.getTotal();
if (total != 0) {
System.out.println("编号\t姓名\t性别\t年龄\t电话\t\t邮箱");
Customer[] custs = customerList.getAllCustomer();
for (int i = 0; i < custs.length; i++) {//custs.length写total也可以
Customer c = custs[i];//找一个Customer类型的变量接受,方便后面的使用
System.out.println(i + 1 + "\t" + c.getName() + "\t" + c.getGender() + "\t" + c.getAge() + "\t" + c.getPhone() + "\t" + c.getEmail());//\t不可以用单引号因为用单引号用+会转为整形
}
} else {
System.out.println("没有客户信息");
}
System.out.println("-------客户列表完成----------");
}
public static void main(String[] args) {
CustomerView view = new CustomerView();
view.enterMainMenu();
}
}
/**
* @Decsription:CustomerList为Customer对象的管理模块,内部用数组管理一组Customer对象,并提供相应的添加、修改、删除和遍历方法,共CustomerVew调用
*/
public class CustomerList{
private Customer[] customers;
private int total=0;//记录已保存客户对象的数量
/**
* 用来初始化customers数组的构造器
* @param totalCustomer,指定数组的长度
*/
public CustomerList(int totalCustomer){
customers =new Customer[totalCustomer];//保证数组的建立
}
/**
* @Description 将指定的客户添加到数组中
* @param customer
* @return true:添加成功 false:添加失败
*/
public boolean addCustomer(Customer customer){
if (total>=customers.length){
return false;
}
customers[total++]= customer;
return true;
}
/**
* Description:修改指定索引上的客户信息
* @param index
* @param cust
* @return true:修改成功 flase:修改失败
*/
public boolean replaceCustomer(int index,Customer cust){
if(index<0 || index>=total)
return false;
customers[index]= cust;//内部结构含义让数组对应位置指向新的对象
return true;
}
/**
* Description:删除指定索引上的元素
* @param index
* @return true:删除成功 false:删除失败
*/
public boolean deleteCustomer(int index){
if(index<0 || index>=total)
return false;
for(int i=index;i<total-1;i++){//数组下标
customers[i]=customers[i+1];//要保证数组是连续的
}
customers[--total]=null;
return true;
}
/**
* 获得所有顾客
* @return 返回每个顾客的内容
*/
public Customer[] getAllCustomer(){
Customer[] c=new Customer[total];//因为customers中可能存在空位置(没有放满),空位置不用输出,因此要创建一个新的数组,保证返回的数组没有空值
for(int i=0;i<total;i++){
c[i]=customers[i];//让c相应位置指向customer相应位置的地址
}
return c;//返回整个数组类型 public Customer[] getAllCustomer(){ Customer[] c=new Customer[total];... return c}
}
/**
* 获得所有顾客
* @return 找到了返回对应索引的值,没找到返回null
*/
public Customer getCustomer(int index){//注意这里Customer没有[]而上面的由[],因为这个只返回一个,上面返回一组
if (index<0 || index>=total)
return null;
else
return customers[index];//返回某一个位置的元素用return customers[index];
}
/**
* 获得用户个数
* @return 用户个数
*/
public int getTotal(){
return total;
}
}
八、继承性–extents
1.继承性的说明
①使用extents可以继承其他类中已有的属性和方法,在新的类中可以不再设置。
②一旦子类继承父类以后,子类就获取了父类B中声明的结构:属性、方法。也会获取父类中private的属性和方法,只是由于封装性的影响,子类不可以直接调用父类中声明为private的属性和方法,如想要使用需要用set/get方法调用。
③子类继承父类以后,还可以声明自己特有的属性和方法,实现功能的拓展。使子类比父类功能更加强大。
私有属性:私有的也会继承,只不过是封装性的影响不可以直接获取到,用set/get方法可以使用
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
在main函数中
s1.setAge(10);
System.out.println(s1.getAge());//输出10
(2)私有方法:
public void eat() {
System.out.println("吃饭");
sleep();
}
private void sleep() {
System.out.println("睡觉");
}
在main函数中
s1.sleep();//会报错--不可以使用,因为为私有的不可以直接为其他类调用。
s1.eat();//会输出吃饭和睡觉
2.继承性的优点
①减少了代码的冗余,提高了代码的复用性
②便于功能的扩展,如:多个类中都想添加一些功能,则可以在他们所继承的类中加上这些功能即可
③为之后的多态性的使用提供了前提
3.继承性的格式
class A extends B{}
其中:
A:子类、派生类、subclass
B:父类、超类、基类、superclass
4.Java中关于继承性的规定
①一个类可以被多个子类继承
②Java中类的单继承性:一个类只能有一个父类
③子类与父类是相对的概念,即类是可以多层继承的。A–>B–>C–>D,其中D类有ABC类的功能
④子类直接继承的父类叫直接父类(如A与B、B与C、C与D的关系),子类间接继承的父类叫间接父类(如C、D与A的关系)
⑤子类继承了父类以后,就获取了直接父类以及所有间接父类所声明的属性和方法
⑥被继承的类必须定义无参构造器,不可以只定义含参构造器,他们都可以同时含有空参构造器与带参构造器;用super(空)解释,如果父类没有空的构造器,子类中默认的super(空)就找不到空参构造器,因此会报错
⑦如果我们没有显示的声明一个类的父类的话,则此类继承java.lang.Object类
⑧所有的Java类(除Object类)都直接或间接的继承于java.lang.Object类。即所有的Java类都具有java.lang.Object类声明的功能
6.子类对象实例化的过程
①从过程上看:当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,直到调用了java.lang.Object类中空参的构造器位置。正因为加载过所有的父类结构,所以才可以看到内存中有父类的结构,子类对象才可以考虑进行调用。
②从结果上来看:子类继承父类以后,就获取了父类中声明的属性和方法。创建子类的对象,在对空间中,就会加载所有父类中生命的属性和方法
③明确:虽然创建子类对象时调用了父类的构造器,但是自始至终就创建过一个对象,即为new过的这个子类对象
7.案例
(1)虽然在ManKind中sex与salary使private类型,使用kids.age的方式不可以使用,但是可以用getAge()方法获取
public class KinsTest {
public static void main(String[] args) {
Kids kids = new Kids(12);
kids.printAge();
kids.setSalary(0);
kids.setSex(1);
System.out.println(kids.getSalary());//虽然在ManKind中sex与salary使private类型,使用kids.age的方式不可以使用,但是可以用getAge()方法获取
kids.employed();
kids.manOrWoman();
}
}
public class ManKind {
private int sex;
private int salary;
public ManKind() {
}
public ManKind(int sex, int salary) {
this.sex = sex;
this.salary = salary;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
public void manOrWoman() {
if (sex == 1) {
System.out.println("man");
} else {
System.out.println("woman");
}
}
public void employed() {
// if (salary==0){
// System.out.println("no job");
// }
// else {
// System.out.println("job");
// }
String info = (salary == 0) ? "no job" : "job";
System.out.println(info);
}
}
public class Kids extends ManKind{
private int yearsOld;
public Kids() {
}
public Kids(int yearsOld) {
this.yearsOld = yearsOld;
}
public int getYearsOld() {
return yearsOld;
}
public void setYearsOld(int yearsOld) {
this.yearsOld = yearsOld;
}
public void printAge() {
System.out.println("I am " + yearsOld);
}
}
(2)
public class Test {
public static void main(String[] args) {
Cylinder cylinder = new Cylinder();
cylinder.setLength(2.1);
cylinder.setLength(2.1);
System.out.println("圆柱的体积为:" + cylinder.findVolume() + "圆柱的底面积为:" + cylinder.findAre());
}
}
public class Circle {
private double radius;
public Circle() {
radius = 2.0;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
public double findAre() {
return Math.PI * radius * radius;
}
}
public class Cylinder extends Circle {
private double length;
public Cylinder() {
length = 1.0;
}
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
public double findVolume() {
//return Math.PI * getRadius() * getRadius() * length;
return findAre() * length;//会继承父类的方法,所以findAre可以直接用
}
}
九、方法的重写(非静态)
1.方法重写的定义
在子类中可以根据需要对父类中继承来的非静态方法进行改造或覆盖,在执行程序时,子类的方法将覆盖父类的方法,只有非静态方法可以重写。
重写以后:
(1)创建子类对象并且通过子类对象调用子父类中的同名同参的方法时,实际执行的是子类中重写的方法;
(2)创建子类对象调用子类中没有重写父类对象的方法,实际执行的仍是父类中的方法;
(2)创建父类对象调用父类的方法是(不可能调用子类的方法)执行的是父类的方法。
2.方法重写的格式与要求
方法的声明:权限修饰符 返回值类型 方法名(形参列表) throws 异常类型{
//方法体
}
子类重写的方法:重写的方法 父类中被重写的方法:被重写的方法
(1)子类重写的方法与父类被重写的方法的方法名与形参列表相同;
(2)子类重写方法的权限修饰符大于等于父类被重写方法的权限修饰符**;特殊情况:子类中不可以重写父类中private权限的方法;
(3)返回值类型:
①如果父类中被重写方法的返回值类型是void,则子类重写方法的返回值类型也只能是void;
②父类被重写方法的返回值类型是A类型,则子类重写方法的返回值类型可以是A类型或是A的子类;(A类中有Student类型,重写方法可以是A也可以是Student)
③父类被重写方法的返回值类型是基本数据类型,则子类重写方法的返回值类必须是该基本数据类型;
(4)子类重写方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
注:在开发过程中,一般不用记这么细,只需要
①把需要重写的方法从父类中直接粘贴过来,修改方法体即可
②直接打方法的的名称在提示中进行选择
③权限修饰符:父<=子,返回值:父>=子,异常:父>=子
(5)子类和父类中同名同参数的方法,要么都声明为非static(重写),要么都声明为static(不是重写),不可以父类与子类中一个加一个不加(会报错),但只有非static的才叫重写,static不能重写
public class Test {
public static void main(String[] args) {
Student s=new Student("计算机科学与技术");
s.eat();
s.study();
s.walk(10);//用s调用person中的walk,尽管walk调用了show方法,但由于show是private类型,所以不认为是子类对付类的重写,显示的仍是父类方法的人
Person p=new Person();
p.eat();
}
}
//父类Person
public class Person {
String name;
int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void eat() {
System.out.println("吃饭");
}
public void walk(int distance) {
System.out.println("走路"+distance);
show();
}
private void show(){
System.out.println("人");
}
public Object info(){
return null;
}
public double info1(){
return 1.0;
}
//子类Student
public class Student extends Person {
String major;
public Student() {
}
public Student(String major) {
this.major = major;
}
public void study(){
System.out.println("专业是"+major);
}
//对父类中的eat方法进行重写
public void eat() {//去掉public或者是换一个比public小的,则会报错,父类中eat方法的public可以去掉不会报错
System.out.println("学生应该多吃有营养的食物");
}
public void show(){//因为父类中show方法是private,所以此处不认为是对父类中show方法的重写
System.out.println("学生");
}
public Object info(){//把Object改成String也可以(Object是所有方法(除Object)的父类),即父类被重写方法的返回值类型是A类型,则子类重写方法的返回值类型可以是A类型或是A的子类;
return null;
}
public double info1(){//返回值为基本数据类型所以必须一样
return 1.0;
}
@Override
public void walk(int distance) {//直接打方法的的名称在提示中进行选择
super.walk(distance);
}
}
十、super关键字
1.uper关键字的使用范围
super理解为父类的可以用来调用:属性、方法、构造器(super(value))
2.super修饰属性和方法
①属性与方法可以在子类的方法或构造器中使用 super.属性 或 super.方法 的方式显示调用父类中声明的属性或方法,但是通常情况下会 省略"super."。当子类和父类中定义了相同的属性时,我们要想在子类中调用父类中声明的属性,则必须显示的使用super.来调用父类中的属性与方法
②super.属性 super.方法 :直接去父类中寻找该属性值,this.super:先在本子类中去找,找到了使用子类中的值,没找到再去其父类中去找
③方法:对于子类中没有重写的方法,用this与super都一样,省略也可以如:eat();
public class SuperTest {
public static void main(String[] args) {
Student student=new Student();
student.show();
student.study();
}
}
public class Student extends Person {
String major;
int id=1002;//学号,与父类的属性相同,但是属性没有覆盖一说,因此在内存中存在两个id
public Student() {
}
public Student(String major) {
this.major = major;
}
public void eat() {
System.out.println("学生多吃有营养的事物");
}
public void study() {
System.out.println("学生学习之知识");
this.eat();
super.eat();
walk();//对于子类中没有重写的方法,用this与super都一样
}
public void show() {
System.out.println("name=" + this.name + ",age=" + super.age);
System.out.println("id="+super.id);
}
}
public class Person {
String name;
int age;
int id=1001;//身份证号
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void eat(){
System.out.println("人吃饭");
}
public void walk(){
System.out.println("人走路");
}
}
3.super调用构造器
①我们可以在子类的构造器中显示的使用"super(形参列表)"的方式,调用父类中声明的指定构造器
②"super(形参列表)"的使用,必须声明在子类构造器的首行
③我们在类的构造器中,针对于”this(形参列表)“或”super(形参列表)“只能二选一,不能同时出现
④在构造器的首行没有显示的声明”this(形参列表)“或”super(形参列表)“则默认调用的是父类中空参的构造器:super(空);
public Person() {
}
public Student(String major) {//没有则默认为super();
this.major = major;
}
4.案例
public class CheckTest {
public static void main(String[] args) {
CheckAccount checkAccount = new CheckAccount(1122, 20000, 0.045, 5000);
checkAccount.withdraw(5000);
System.out.println("您的账户余额为:" + checkAccount.getBalance());
System.out.println("您的透支额度为:" + checkAccount.getOverdraft());
checkAccount.withdraw(18000);
System.out.println("您的账户余额为:" + checkAccount.getBalance());
System.out.println("您的透支额度为:" + checkAccount.getOverdraft());
checkAccount.withdraw(3000);
System.out.println("您的账户余额为:" + checkAccount.getBalance());
System.out.println("您的透支额度为:" + checkAccount.getOverdraft());
}
}
public class Account {
private int id;
private double balance;
private double annualInterestRate;
public Account(int id, double balance, double annualInterestRate) {
this.id = id;
this.balance = balance;
this.annualInterestRate = annualInterestRate;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public double getAnnualInterestRate() {
return annualInterestRate;
}
public void setAnnualInterestRate(double annualInterestRate) {
this.annualInterestRate = annualInterestRate;
}
public double getMonthlyInterest() {
return annualInterestRate / 12;
}
public void withdraw(double amount) {
if (balance >= amount) {
balance -= amount;
return;
}
System.out.println("余额不足");
}
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
}
public class CheckAccount extends Account {
private double overdraft;//可透支额度
public CheckAccount(int id, double balance, double annualInterestRate, double overdraft) {
super(id, balance, annualInterestRate);
this.overdraft = overdraft;
}
public double getOverdraft() {
return overdraft;
}
public void setOverdraft(double overdraft) {
this.overdraft = overdraft;
}
public void withdraw(double amount) {
if (getBalance() >= amount) {
//方式1 setBalance(getBalance() - amount);
super.withdraw(amount);
} else if (overdraft >= amount - getBalance()) {//透支额度+余额足够消费,要先用透支额度减去消费金额减去余额,再使余额为0,否则先让余额为0
// 后就为透支额度减去消费金额减去余额(0)
overdraft -= (amount - getBalance());
setBalance(0);
super.withdraw(getBalance());//setBalance(0);
} else {
System.out.println("超过可透支限额");
}
}
}
十一、多态性
1.多态性的定义
多态性:父类的引用指向子类的对象(子类的对象赋给父类的引用)。Person p2=new Man();左边是父类的声明,右边是子类的对象。
2.多态的使用
①虚拟方法调用:编译时只能调用父类中声明的方法,运行期实际执行的是子类重写父类的方法
②编译时看左边运行时看右边,属性全看左边:编译时看左边的方法,运行时看右边的方法,属性全看左边的-----使用父类存在的方法名调子类重写的方法
③编译时看谁的方法是看左边的,p2是Person类型不是Man类型,eat方法是Person中的(ctrl+左键会跳转至Person中的eat 方法);运行时运行的是右边子类man中对应的eat方法
Person p2 = new Man();
p2.eat(); 编译时看谁的方法是方法看左边的,p2是Person类型不是Man类型,eat方法是Person中的(ctrl+左键会跳转至Person中的eat方法)
p2.walk(); 运行时运行的是右边子类man中对应的eat方法
输出Man中重写的eat与walk方法
3.多态性的使用前提
①要有类的继承关系(没有继承性就没有多态性)
Person person = new Woman();左边与右边要有继承关系(右边与左边类型一样 或者是 右边是左边的一个子类,左大右小)
②要有方法的重写(在子类中体现)
③对象的多态性只适用于方法不适用于属性
4.多态性的体现
public class AnimalTest {
public static void main(String[] args) {
AnimalTest animalTest = new AnimalTest();
animalTest.fun(new Dog());
animalTest.fun(new Cat());
}
public void fun(Animal animal) {// 相当于多态性 Animal animal=new Dog();
animal.eat();
animal.shout();
if (animal instanceof Dog) {//需要将以下的两个单独方法删除才可以显示,否则会直接调用下面的方法
Dog d = (Dog) animal;
d.watchDoor();//狗类中特有方法
}
}
//如果没有多态性要定义以下两个方法
public void fun(Dog dog) {
dog.eat();
dog.shout();
}
public void fun(Cat cat) {
cat.eat();
cat.shout();
}
}
class Animal {
public void eat() {
System.out.println("动物进食");
}
public void shout() {
System.out.println("动物叫");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("狗吃狗粮");
}
public void shout() {
System.out.println("汪汪汪");
}
public void watchDoor(){
System.out.println("狗看门");
}
}
class Cat extends Animal {
public void eat() {
System.out.println("猫吃鱼");
}
public void shout() {
System.out.println("喵喵喵");
}
}
class Order {
public void method(Object obj) {
}
}
//多态性在与数据库连接时的体现
class Driver {
public void doData(Connection connection) {//connection = new MySQLConnection() connection =new OracelConnection
//connection.method1();
}
}
5.虚拟方法调用
(1)正常方法:定义什么方法调用什么对象 Person p=new Person();
(2)虚拟方法:子类中定义了与父类同名同参数的方法,在多数情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的,即它是一个运行时行为。------动态绑定
6.instanceof关键字
①instanceof左侧的对象是右侧类型的对象或子类的对象,instanceof左侧为null时运算结果恒为false,Student s=null; s instanceof Student;–null
②有了对象的多态性之后,内存中实际上是加载了子类特有的属性和方法,但是由于变量声明为父类的类型,导致编译时只能调用父类中声明的属性和方法,子类中特有的属性和方法不能调用。
③如何才能调用子类特有的属性和方法?
(1)使用强制类型转换符(向下转型)–使用强转是可能出现ClassCastExpection的异常,将p2转换成man,下面再想将p2转换成Woman时会报出异常。
Man man=(Man)person;—Person转为子类的Man。man指向person的空间,可以调用person中Man的结构,Man中的方法
(2)使用instanceof关键字(在向下转型之前使用)
①a instanceof A:判断对象a是否是类A的实例,如果是返回true,如果不是返回false
为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前先进性instanceof的判断,一旦返回true就会向下转型;如果返回false则不进行向下转型
②如果a instanceof A返回true,则a instanceof B也返回true,其中类B是类A的父类。new的对象(右边)是什么用该实体person(new的左边)去instanceof右边一定是true
instanceof一个子类为true,则换成它的父类(直接或间接父类)也为true
新理解:左是右的本身或子类则为true(左小于等于右,不可大于右)
Person person = new Man();//右边小左边大--向上转型(person是Man类型)
if(person instanceof Man){//返回Man为true,则返回Man的父类也都为true
Man man=(Man)person;//man是新的不是之前定义的 左边小右边(P2是Person类型)大--向下转型
man.earnMoney();//返回man中重写的earnMoney方法
System.out.println("*********Man********");
}
if (person instanceof Person){
System.out.println("person");
}
if (person instanceof Object){
System.out.println("object");
}
③多态性(向上转型)与向下转型总结:P131:例4-14
7.instanceof关键字的总结
①两种转型时必须要有继承关系
②向上转型:左父右子
③向下转型:左父(大)右子(小)(右边的大小看多态性new的),否则就会出现异常
④instance:左小于等于右返回true,左为null返回false
⑥参数可以传递形参类本身或形参的子类
⑦实际类型左,大小看右:person是Person类型,但是实际运行时看右边(Woman),大小要看右边的,因此总结中看的是右边实际运行的类型,叫法(向上转型、向下转型)看的是实际类型
Person p=new Man();//父类 a=new 子类();---多态性--a为子类的类型
Person pp =(Person)p;//父类 b=(父类)a;---转型--右边类小,左边类大
问题及分析
强转时必须要有继承关系,强转左边大(等)右边小,右边大小看new的,否则就会出现异常
问题一:编译时通过,运行时不通过
① Person person = new Woman();//new时加载了Woman以及其所有父类的方法
Man man2 = (Man) person;//转型时只加载Woman的方法,未加载其他的方法--向下转型时没有继承关系(woman与man没有继承关系,不满足左man大右woman小)
person是Person类型,但是实际运行时看右边,因此总结中看的是右边实际运行的类型,叫法(向上转型、向下转型)看的是实际类型
② Object o=new Date();
String str1=(String) o;//左右不相关,无继承关系
问题二:编译通过,运行时也通过
Object obj=new Woman();
Person pp=(Person)obj;
Person p4=new Person();
Man m4=(Man) p4;//左小右大了
问题三:编译不过,运行不过
Man mm=new Woman();//--Man与Woman没有继承关系
8.继承性与多态性的赋值
(1)继承**(编译运行都看左边**): 属性:看对象(先在对象类中找,没有再去父类中找) 方法:看对象(先在对象类中找,没有再去父类中找)
(2)多态**(编译看左边运行看右边)**:属性:父类 方法:是否可用父类,执行结果子类
即:对于多态来说
若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类:编译看左边,运行看右边
对于实例变量(属性)则不存在这样的现象,即使子类里定义了与父类完全相同的属性,这个实例变量依然不可能覆盖父类中定义的属性:编译运行都看左边
//继承:属性:看对象(先在对象类中找,没有再去父类中找) 方法:看对象(先在对象类中找,没有再去父类中找)
//多态:属性:父类 方法:是否可用父类,执行结果子类
public class FiledMtethodTest {
public static void main(String[] args) {
Sub s = new Sub();
System.out.println(s.count);//20
s.display();//20
Base b = s;//多态,s赋给base
System.out.println(b == s);//true(比较两个引用数据类型变量的地址值是否相同)
System.out.println(b.count);//10
b.display();//20
}
}
class Base {
int count = 10;
public void display() {
System.out.println(this.count);
}
}
class Sub extends Base {
int count = 20;//count2=20 s.count现在子类中找count没找到再去父类中找count找到了10
public void display() {
System.out.println(this.count);//
}
}
8.案例
练习1:
public class Test {
public static void main(String[] args) {
Test t = new Test();
t.method(new Student());//多态性
}
public void method(Person e) {
String info = e.getInfo();
System.out.println(info);//输出学生类的信息
// if (e instanceof Graduate) {
// System.out.println("a graduated student");
// System.out.println("a student");
// System.out.println("a person");
// } else if (e instanceof Student) {
// System.out.println("a student");
// System.out.println("a person");
// } else {
// System.out.println("a person");
// }
if (e instanceof Graduate)
System.out.println("a graduated student");
if (e instanceof Student)
System.out.println("a student");
if (e instanceof Person)
System.out.println("a person");
}
}
public class Person {
protected String name = "person";
protected int age = 50;
public String getInfo() {
return "Name:" + name + "\n" + "age:" + age;
}
}
class Student extends Person {
protected String school = "pku";
public String getInfo() {
return "Name:" + name + "\n" + "age:" + age + "\nschool:" + school;
}
}
class Graduate extends Student {
public String major = "IT";
public String getInfo() {
return "Name:" + name + "\nage:" + "\nschool:" + school + "\nmajor:" + major;
}
}
练习2:
class Graduate extends Student {
public String major = “IT”;
class A{
public void add(int a,int... arr){
sout("1");
}
}
class B extents A(){
public void add(int a,int[] arr){
sout("2");
}
public void add(int a,int b,int c){
sout("3");
}
}
A a=new B();
a.add(1,2,3);//2
B b=(B)A;
b.add(1,2,3);//3
练习3:多态性的使用,方法的形参的是GeometricObject,但是实参传的是它的子类new Circle
public class Test {
public static void main(String[] args) {
Test test = new Test();
Circle c1 = new Circle("blue", 2.3, 1.0);
Circle c2 = new Circle("block", 3.3, 2.0);
test.displayGeometricObject(c1);//多态性的使用,方法的形参的是GeometricObject,但是实参传的是它的子类new Circle
test.displayGeometricObject(c2);
Boolean b = test.equalArea(c1, c2);
System.out.println("c1、c2的面积是否相等:" + b);
}
//比较面积是否相同
public boolean equalArea(GeometricObject o1, GeometricObject o2) {
return o1.findArea() == o2.findArea();
}
public void displayGeometricObject(GeometricObject o) {
System.out.println("面积为:" + o.findArea());
}
}
public class GeometricObject {
protected String color;
protected double weight;
public GeometricObject(String color, double weight) {
this.color = color;
this.weight = weight;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
public double findArea() {
return 0.0;
}
}
public class Circle extends GeometricObject {
private double radius;
public Circle(String color, double weight, double radius) {
super(color, weight);
this.radius = radius;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
public double findArea() {
return 3.14 * radius * radius;
}
}
public class MyRectangle extends GeometricObject {
private double width;
private double height;
public MyRectangle(String color, double weight, double width, double height) {
super(color, weight);
this.width = width;
this.height = height;
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public double findArea() {
return width * height;
}
}
十二、Object类
1.Object类介绍
(1)Object类是所有Java类的根父类
(2)如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类
2.Object类中的功能(属性、方法)
(1)Object无属性
(2)Object只声明了一个空参的构造器
(3)equals()比较两个方法是否相等
(4)toString()c1.toString()与c1相同
(5)getClass()
(6)hashCode()
(7)clone()
(8)finalize()
(9)wait()
(10)notify()
(11)notifyAll()
3.equals()与 ==
(1)==:运算符
①可以使用在基本数据类型变量和引用数据类型变量中
②如果比较的是基本数据类型变量:比较的是两个变量保存的值是否相同(不一定要同类型,boolean除外)
③如果比较的是引用类型变量:比较的是两个对象的地址值是否相同,即两个引用是否指向同一对象实体
④==两边的类型要一样或者是可转换成一样的(boolean永远不能比,不同的引用类型不能转换为一起的直接报错)
(2)equals()方法的使用
①是一个方法,而非运算符
②只能适用于引用数据类型
③Objcet类中equalsI()方法的定义:与“==”作用相同
④像String、Date、File、包装类等都重写了Object中的equal()方法:比较的不是两个引用的地址是否相同,而是比较两个对象的“实体内容”是否相同
总结:①==:基本类型比内容,引用类型比地址。 equals:正常情况比地址,重写之后比内容
②通常情况下去,我们自定义的类如果使用equals()的话,也通常是比较两个两个对象的“内容”是否相同。那么我们就需要对Object类中的equal方法进行重写
4.equals的几个特性
①对称性:x e y=t则y e x=t
②自反性:x e x =t
③传递性:x e y=t y e z=t则z e x=t
④一致性:x e y=t则xy只要内容一直不变则永远为t
⑤x e null=f null e x异常,所以想要比较是要看前面的是否为null
⑥x e 和x不同类型的对象=f
5.equlas练习
public class java08 {
public static void main(String[] args) {
int i = 10;
int j = 10;
double d = 10.0;
System.out.println(i == j);//true
System.out.println(i == d);//true
boolean b = true;
//System.out.println(i == b);报错,都不可以和boolean玩,其他都可以互相比
char c = 10;
System.out.println(i == c);//true
char c1 = 'A';
char c2 = 65;
System.out.println(c1 == c2);//true
Customer customer = new Customer("Tom", 21);
Customer customer2 = new Customer("Tom", 21);
System.out.println(customer == customer2);
String s1 = new String("123");
String s2 = new String("123");
System.out.println(s1 == s2);//false
System.out.println(customer.equals(customer2));//false--重写equals()方法后变为true
System.out.println(s1.equals(s2));//true
Date d1 = new Date(123456);
Date d2 = new Date(123456);
System.out.println(d1 == d2);//false
System.out.println(d1.equals(d2));//true
}
}
public class Customer {
private String name;
private int age;
public Customer() {
}
public Customer(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;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Customer customer = (Customer) o;
return age == customer.age && Objects.equals(name, customer.name);
}
//重写的原则:比较两个实体的内容(即:name和age)是否相同
// public boolean equals(Object obj) {
// if (this == obj) {//指向的地址是否相同,同一个地址肯定的是true
// return true;
// }
// if (obj instanceof Customer) {//看看obj是不是Customer类或是其子类,不是就不用比了,是则转成Customer类进行各个值的比较
// Customer c = (Customer) obj;
// //比较两个对象的每个属性是否都相同
if (this.age == c.age && this.name.equals(c.name)) {
return true;
} else {
return false;
}或
// return this.age == c.age && this.name.equals(c.name);//相等为true不等为false,基本数据类型age比的是内容,String因为重写了equal
// // 方法所以比的是内容
// } else {
// return false;
// }
// }
}
public class OrderTest {
public static void main(String[] args) {
Order o1 = new Order(11, "11");
Order o2 = new Order(22, "22");
Order o3 = new Order(11, "11");//name为内容
Order o4 = new Order(11, new String("11"));//new String创建新的地址,所以不再原来的变量池中了,地址也就不一样了
System.out.println(o1.equals(o2));//f
System.out.println(o1.equals(o3));//t t
System.out.println(o1.equals(o4));//f f
String s1 = "BB";
String s2 = "BB";
s1 = "cc";
System.out.println(s2);
System.out.println(s1 == s2);//true--String的值会存储在常量池中,创建新的变量但是值一样仍会指向之前已经存储了的该值的变量池,但是一旦修改了这个变量的值,就会创建一个新的变量池放新值,以前比变量池的值不变
}
}
class Order {
private int orderId;
private String orderName;
private Customer c;//在Order中判断Customer是否相等时也需要用equals,这个equals是在Customer中进行重写的equals
public Order(int orderId, String orderName) {
this.orderId = orderId;
this.orderName = orderName;
}
public int getOrderId() {
return orderId;
}
public void setOrderId(int orderId) {
this.orderId = orderId;
}
public String getOrderName() {
return orderName;
}
public void setOrderName(String orderName) {
this.orderName = orderName;
}
public boolean equals(Object obj) {
if (this == obj) {//此处的this指当前对象
return true;
}
if (obj instanceof Object) {
Order order = (Order) obj;
return this.orderId == order.orderId && this.orderName.equals(order.orderName);
//return this.orderId == order.orderId && this.orderName==order.orderName;s1与s3比返回true。原因:变量池。如果为o4的写法则为f
}
return false;
}
}
6.toString方法
(1)当我们输出一个对象的引用时,实际上就是调用当前对象的toString()方法
(2)Object类中的toString方法:输出包名@该数据的地址值
(3)String、Data、File、包装类等都重写了Object类中的toString方法:输出实体“内容”信息
(4)自定义类也可以重写toString方法,当调动此方法时,返回对象的“实体内容”
public String toString() {//可以直接调用
return "Customer[name = " + name + ",age = " + age + "]";
}
System.out.println(customer.toString());
7.综合练习
public class CircleTest {
public static void main(String[] args) {
Circle c1 = new Circle(2.3);
Circle c2 = new Circle("white", 2.3, 2.0);
Circle c3 = new Circle(new String("white"), 2.3, 2.0);
System.out.println("颜色是否相同:" + c1.getColor().equals(c2.getColor()));//String中重写了equlas方法,比的是内容
System.out.println("半径是否相同:" + c1.equals(c2));
System.out.println(c1);//与toString相同
System.out.println(c2.toString());
}
}
public class GeometricObject {
protected String color;
protected double weight;
public GeometricObject() {
super();
this.color = "white";
this.weight = 1.0;
}
public GeometricObject(String color, double weight) {
this.color = color;
this.weight = weight;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
}
public class Circle extends GeometricObject {
private double radius;
public Circle() {
super();// color="white"; weight=1.0;
radius = 1.0;
}
public Circle(double radius) {
super();// color="white"; weight=1.0;
this.radius = radius;
}
public Circle(String color, double weight, double radius) {
super(color, weight);//三个值都由实参传入赋值
this.radius = radius;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
public double findArea() {
return 3.14 * radius * radius;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o instanceof Circle) {
Circle c = (Circle) o;
return this.radius == c.radius;
}
return false;
}
@Override
public String toString() {
return "Circle{" +
"radius=" + radius +
'}';
}
}
十三、包装类的使用
1.Java提供了八种数据类型对应的包装类,使得基本数据类型的变量具有类的特征
基本数据类型 包装类
byte Byte
short Short
int Integer
long Long
float Float
double Double
以上为数值型的,其父类为Number
boolean Boolean
char Character
2.基本数据类型、包装类、String三者之间的相互转化
(1)基本数据类型转换为包装类:调用类的构造器
public class Test {
public static void main(String[] args) {
int num1 = 10;
Integer in1 = new Integer(num1);
System.out.println(in1.toString());
//s:必须时一个数,不可以由其他字符,可以写"123"--String也可以写123--int
Integer in2 = new Integer(123);
System.out.println(in2.toString());
// Integer in3=new Integer("123a");
// System.out.println(in3.toString());
//可以写12.3--double也可以写"12.3"--String或12.3f--float
Float f1 = new Float(12.3f);
System.out.println(f1.toString());
//value:小写true或false s:或略大小写,和true一样是true,其他的都是false
Boolean b1 = new Boolean(true);
Boolean b2 = new Boolean("true");
Boolean b3 = new Boolean("true123");
System.out.println(b1.toString());
System.out.println(b2.toString());
System.out.println(b3.toString());
Order o = new Order();
System.out.println(o.isMale);//false
System.out.println(o.isFemale);//NULL,没赋初值为null
}
}
class Order {
boolean isMale;//基本数据类型boolean
Boolean isFemale;//包装类
}
为什么要将基本数据类型转换为包装类
调用method,但是参数是Object类型,int直接穿传不过去,因此需要转成包装类再传
//调用method,但是参数是Object类型,int直接穿传不过去,因此需要转成包装类再传
int i=10;
Test t=new Test();
t.method(new Integer(i));
public void method(Object obj){
System.out.println(obj.toString());
}
(2)包装类转化成基本数据类型:利用基本数据类型对应的包装类对象来调用包装类的xxxValue()
Integer in1 = new Integer("123");
int i1 = in1.intValue();
System.out.println(i1 + 1);//124
System.out.println(in1+1);//124
Float f1=new Float(12.3);
float f2=f1.floatValue();
System.out.println(f2);//12.3
float f3=(f1+1)/2;
System.out.println(f3);//6.65
为什么要将包装类转换成基本数据类型:类有时不可以做加减乘除运算,因此必要时要转为基本数据类型
int i=10;
Test t=new Test();
t.method(new Integer(i));
public void method(Object obj){
Integer i=(Integer) obj;//利用obj不可以直接算,要向下转型也可以int intScore = (int) obj;
//int in=i.intValue();由于自动拆箱的原因不用将Integer转换成int了
System.out.println(i+1);
}
(3)基本数据类型、包装类转String类型
①num+“”;
②调用String重载的valueOf(xxx xxx)方法
//基本数据类型、包装类转String类型
int num = 10;
//方式1:+""
String str = num + "";
//方式二:调用String重载的valueOf(xxx xxx)方法
float f = 12.3f;
String str1 = String.valueOf(f);//str1="12.3"
Double d = new Double(12.4);//由于自动拆箱所以可直接调用
String str2 = String.valueOf(d);
(4)String类型怎么转换成基本数据类型或包装类:调用包装类的ParseXxx()
//String类型怎么转换成基本数据类型或包装类:调用包装类的ParseXxx()
//不可以为"123a"
String s = "123";
int num1 = Integer.parseInt(s);
System.out.println(num1 + 100000000);
String s1 = "TRUE";//不是标准的true都是false
boolean b = Boolean.parseBoolean(s1);
System.out.println(b);
3.JDK5.0新特性:自动装箱与自动拆箱(基本数据类型与包装类相当于一回事)
//自动装箱
int num = 10;
Integer i = num;//替代Integer i=new Integer(num);
boolean b = true;
Boolean B = b;
//自动拆箱
int num1 = i;//直接把包装类赋值给基本数据类型
4.总结
(1)基本数据类型-----包装类:自动装拆箱 Integer i = num; int num = i;
(2)基本数据类型、包装类------String:String.valueOf(int);
(3)String------基本数据类型、包装类:包装类.paraseXxx(String);
(4)包装类的Compare方法------Double.compare(this.price, g.price);比较两个对应包装类的大小
5.面试题
(1)
//1.0:因为自动类型提升为了double
Object o = true ? new Integer(1) : new Double(2.0);
//1
Object o2;
if (true) {
o2 = new Integer(1);
} else {
o2 = new Double(2.0);
}
//false
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j);
//Integer内部定义了IntegerCache结构,IntegetCache中定义了-128~127范围的数组,如果再范围内是可以直接使用数组中的元素不用再new了,使用了也不会销毁,超过127时则再创建新的。即-128~127中不创建新的对象则地址相同,创建新的对象则地址不同,其他范围无论床不创建新的对象都会创建一个新的地址
//true:没new对象新的对象,所以比地址时一样的
Integer m = 1;
Integer n = 1;
System.out.println(m == n);
//false:new了新的对象,所以比地址不一样
Integer x = 128;
Integer y = 128;
System.out.println(x == y);
(2)
public class ScoreTest {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
Vector v = new Vector();
int maxScore = 0;
for (; ; ) {
System.out.println("请输入学生成绩,负数时结束:");
int score = s.nextInt();
if (score < 0) {
break;
}
if (score > 100) {
System.out.println("数据非法,请重新输入");
continue;
}
v.addElement(score);
if (maxScore < score) {
maxScore = score;
}
}
for (int i = 0; i < v.size(); i++) {
char level;
Object o = v.elementAt(i);
// Integer intScore = (Integer) o;拆箱也可以用下面的
int intScore = (int) o;
if (maxScore - intScore <= 10) {
level = 'A';
} else if (maxScore - intScore <= 20) {
level = 'B';
} else if (maxScore - intScore <= 30) {
level = 'C';
} else {
level = 'D';
}
System.out.println("student:" + i + " score is " + intScore + " level " + level);
}
}
}
十四、Static关键字的使用
1.static介绍
①static:静态的
②static可以用来修饰:属性、方法、代码块、内部类
2.static修饰属性
①属性按是否使用static修饰分为静态属性和非静态属性(实例变量),static修饰的变量叫做静态变量(类变量)
②实例变量:我们创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性,当修改其中一个对象中的非静态属性时,不会导致其他对象中属性值的修改。
③静态变量:我们创建了类的多个对象,多个对象都共享同一个静态变量,当修改其中一个对象中的静态属性时,会修改其他对象的静态变量。
④多个对象中,非静态变量各用各的,静态变量公用一个
⑤通过"类.静态变量"来调用静态变量
⑥静态变量随着类的加载而加载(类的加载早于对象),因此静态变量的加载早于对象的创建,所以可以通过"类.静态变量"的方式进行调用
⑦由于类只加载一次,则静态变量在内存中也只会存在一份,且存在方法区的静态域中。
Chinese.nation = "中国";
System.out.println(Chinese.nation);
⑧是否可以调用
类变量(静态变量) | 实例变量(非静态变量) | |
---|---|---|
类 | YES | NO |
对象 | YES(可以但不推荐) | YES |
⑨static不可以修饰局部变量
public class java16 {
public static void main(String[] args) {
Chinese.nation = "中国";
System.out.println(Chinese.nation);
Chinese c1 = new Chinese();
c1.name = "姚明";
c1.age = 40;
Chinese c2 = new Chinese();
c2.name = "马龙";
c2.age = 30;
c1.nation = "CHN";
c2.nation = "CHINA";
System.out.println(c2.nation);//CHINA
System.out.println(c1.nation);//CHN
}
}
class Chinese {
String name;
int age;
static String nation;
}
3.static修饰方法
①随着类的加载而加载,可以通过"类.静态方法"的方式进行调用–用于不想创建对象但是想调用方法的情况
②是否可调用
静态方法 | 非静态方法 | |
---|---|---|
类 | YES | NO |
属性 | YES | YES |
③由于生命周期的原因,在静态的方法中只能调用静态的方法或属性;非静态方法中既可以调用非静态的方法或属性也可以调用静态的方法和属性
晚出生(非静态)的可以调早出生(静态)的;早出生的不可以调晚出生的,需要在静态方法中创建该类的对象来调用非静态方法
public static void show() {
System.out.println("我是一个" + nation + "人");//nation省略的时Chinese.nation
walk();
}
public static void walk() {
System.out.println("走路");
}
④在静态的方法中,不能使用this关键字、super关键字,因为在静态方法加载时对象还没有创建
4.static修饰代码块
在伴随着类的加载而加载且只执行一次
5.总结
(1)开发中如何确定一个属性是否要声明为static的
①属性被多个对象所共享,不会随着对象的不同而不同的。
②类中的常量也常常声明为static
(2)开发中如何确定一个方法是否要声明为static的
①操作静态属性的方法通常设置为静态的
②工具类中的方法,习惯上声明为static的
6.案例
(1)设置一个static的常量用于自增编号,在构造器中每创建一个对象就让该常量自增1并赋值给id实现id的自增
public class CircleTest {
public static void main(String[] args) {
Circle c1 = new Circle();
System.out.println("c1的id:" + c1.getId());
Circle c2 = new Circle();
System.out.println("c2的id:" + c2.getId());
Circle c3 = new Circle(3.4);
System.out.println("c3的id:" + c3.getId());
System.out.println("创建圆的个数为:" + Circle.getTotal());
}
}
class Circle {
private double radius;
private int id;
private static int total;
private static int init = 1001;
public Circle() {
id = init++;//static声明的init被多个对象共享,编号每次+1
total++;
}
public Circle(double radius) {
this();
this.radius = radius;
// id = init++;利用this代替
// total++;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
public static int getTotal() {
return total;
}
public int getId() {
return id;
}
public double findArea() {
return 3.14 * radius * radius;
}
}
(2)
public class Test {
public static void main(String[] args) {
Account acct1 = new Account();
Account acct2 = new Account("123456", 123);
Account.setInterestRate(0.012);
Account.setMinMoney(0);
System.out.println(acct1);
System.out.println(acct2);
System.out.println(Account.getInterestRate());
System.out.println(Account.getMinMoney());
}
}
public class Account {
private int id;
private String password = "000000";
private double banance = 0.0;
private static double interestRate;
private static double minMoney = 1.0;
private static int init = 1001;//用于自动生成id
public Account() {
id = init++;
}
public Account(String password, double banance) {
id = init++;
this.password = password;
this.banance = banance;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public static double getInterestRate() {
return interestRate;
}
public static void setInterestRate(double interestRate) {
Account.interestRate = interestRate;
}
public static double getMinMoney() {
return minMoney;
}
public static void setMinMoney(double minMoney) {
Account.minMoney = minMoney;
}
public int getId() {
return id;
}
public double getBanance() {
return banance;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", password='" + password + '\'' +
", banance=" + banance +
'}';
}
}
十五、设计模式
1.单例设计模式
单例设计模式,就是采取一定的方法保证在整个的软件系统中,对 某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
优点
由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
2.如何实现单例设计模式
如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构 造器的访问权限设置为private,这样,就不能用new操作符在类的外部产生 类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无 法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象, 静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象 的变量也必须定义成静态的。
(1)饿汉式单例设计模式实现步骤:
①私有化构造器
②内部创建该类的静态对象
③提供公共的静态方法返回类的对象
public class Test {
public static void main(String[] args) {
Bank b1 = Bank.getInstance();
Bank b2 = Bank.getInstance();
System.out.println(b1 == b2);//b1和b2是一个对象
}
}
class Bank {
//1.私有化构造器
private Bank() {
}
//2.内部创建类的对象
//4.要求创建的内部对象也是静态的
private static Bank bank = new Bank();
//3.提供公共的静态方法返回类的对象
public static Bank getInstance() {
return bank;//静态的方法不可以调非静态的结构,所以对象bank也应该是静态的
}
}
(2)懒汉式单例设计模式实现步骤
①私有化类的构造器
②声明当前类的静态的对象,不初始化
③声明公开的、静态的返回类对象的方法
④判断对象为空再造对象
public class Test {
public static void main(String[] args) {
Order order1 = Order.getOrder();
Order order2 = Order.getOrder();
System.out.println(order1 == order2);
}
}
class Order {
//1.私有化类的构造器
private Order() {
}
//2.声明当前类的静态的对象,不初始化
private static Order order = null;
//3.声明公开的、静态的返回类对象的方法
public static Order getOrder() {
if (order == null) {
order = new Order();
}
return order;
}
}
3.区分饿汉式和懒汉式
饿汉式:
坏处:对象加载时间过长
好处:饿汉式是线程安全的
懒汉式:
好处:延迟对象的创建
坏处:目前写法线程不安全,多线程时需要修改
4.应用场景
①网站的计数器,一般也是单例模式实现,否则难以同步。
②应用程序的日志应用,一般都使用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
③数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。
④项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置 文件数据,都生成一个对象去读取。
⑤Application 也是单例的典型应用
⑥Windows的Task Manager (任务管理器)就是很典型的单例模式
⑦Windows的Recycle Bin (回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例
5.模板方法的设计模式
(1)定义
当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以 把不确定的部分暴露出去,让子类去实现。换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用, 这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽 象出来,供不同子类实现。这就是一种模板模式。
(2)总结为:确定顶方法就写好,不确定的交给子类
public class java30 {
public static void main(String[] args) {
SubTemplate s = new SubTemplate();
s.spendTime();
//匿名子类非匿名对象,把父类中code方法重写这个,并且进行方法的调用,所有调用时涉及到的code方法在不做其他修改的情况下都会调用匿名子类中重写的方法
Template t = new Template() {
@Override
public void code() {
for (int i = 1; i <= 10; i++) {
System.out.println(i);
}
}
};
t.spendTime();
}
}
abstract class Template {
public void spendTime() {
//代码执行的当前时间
long start = System.currentTimeMillis();
code();
long end = System.currentTimeMillis();
System.out.println("花费的时间为:" + (end - start));
}
public abstract void code();
}
class SubTemplate extends Template {
@Override
public void code() {
for (int i = 2; i <= 1000; i++) {
boolean flag = true;
for (int j = 2; i < Math.sqrt(i); j++) {
if (i % j == 0) {
flag = false;
break;
}
}
if (flag == true) {
System.out.println(i + " ");
}
}
}
}
6.代理模式
(1)定义
代理模式是Java开发中使用较多的一种设计模式。代理设计就是为其他对象提供一种代理以控制对这个对象的访问
(2)静态代理模式的使用
-
1.创建接口,接口中含有需要代理类与类代理类进行实现的方法
-
2.创建代理类,实现接口,方法中写功能
-
3.创建被代理类实现接口,定义接口类型的属性并设置该属性的构造器,设置需要的方法,在对接口中的方法进行完善
-
4.创建代理类与被代理类的对象,被代理类作为构造器的参数,代理类调用实现的方法,实现完善之后的方法的调用
public class NetWorkTest { public static void main(String[] args) { //创建代理类和被代理类,被代理类做参数 Server server = new Server(); //server作为参数即把server属性利用构造器赋给work ProxyServer proxyServer = new ProxyServer(server); //因为work即为server所以在browse中work.browse相当于server.browse即调用server中的browse方法 proxyServer.browse(); //联网之前的检查工作 //真实的服务器访问网络 } } interface NetWork { void browse(); } //被代理类(想要做的事务) class Server implements NetWork { @Override public void browse() { System.out.println("真实的服务器访问网络"); } } //代理类(调用代理类中的方法实现被代理类方法中的事务) class ProxyServer implements NetWork { private NetWork work; public ProxyServer(NetWork work) { this.work = work; } @Override public void browse() { check(); work.browse(); } public void check() { System.out.println("联网之前的检查工作"); } }
(3)
应用场景: 安全代理:屏蔽对真实角色的直接访问。 远程代理:通过代理类处理远程方法调用(RMI) 延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象 比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有 100MB,在打开文件时,不可能将所有的图片都显示出来,这样就可以使用代理 模式,当需要查看图片时,用proxy来进行大图片的打开。
分类 静态代理(静态定义代理类) 动态代理(动态生成代理类) JDK自带的动态代理,需要反射等知
十六、理解main方法的语法
1.main()方法的使用说明
(1)main()方法作为程序的入口
(2)main()方法也是一个普通的静态方法
public class java21 {
public static void main(String[] args) {//入口
Main.main(new String[100]);
show();
}
public static void show() {//静态的方法只能调用静态的方法,如果该方法不是静态的就需要创建对象,让对象来调用非静态方法
System.out.println("show");
}
}
class Main {
public static void main(String[] args) {
args = new String[100];
for (int i = 0; i < args.length; i++) {
args[i] = "args_" + i;
System.out.println(args[i]);
}
}
}
(3)main()方法也可以作为我们与控制台交互的方式(之前使用Scanner)
十七、代码块
1.代码块的作用
①用来初始化类或对象
②类中属性不会出现未定义就使用的错误,因为类中与方法体不同,先后定义也无所谓。但一般先定义属性再写代码块
2.代码块的分类
一个类中代码块若有修饰符,则只能被static修饰,称为静态代码块,没有使用static修饰的,为非静态代码块。
静态代码块的执行优先于非静态代码块的执行,与先后顺序无关
(1)静态代码块
①内部可以有输出语句
②随着类的加载而执行,且只执行一次
③可以定义多个,若有多个动态代码块,那么按照从上到下的顺序依次执行。
④静态代码块中不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法。
作用
①初始化静态属性
(2)动态代码块
①内部可以有输出语句
②随着对象的创建而执行,每创建一个对象就会执行一次非静态代码块
③可以定义多个,若有多个动态代码块,那么按照从上到下的顺序依次执行。
④可以调用静态结构与非静态的结构
作用
可以在创建对象时,对象的属性等进行初始化
对属性可以默认赋值的位置
①默认初始化
②显示初始化
③在代码块中进行赋值(与②看谁写在前面谁就先执行,不会出现未定义就是用的错误,因为类中与方法体不同,先后定义也无所谓)
④构造器中初始化
⑤有了对象以后,使用"对象.属性"或"对象.方法"的方式进行赋值
public class BlockTest {
public static void main(String[] args) {
String desc = Person.desc;//使用此类让此类的静态结构加载
String desc1 = Person.desc;
System.out.println(desc);
Person.info();
Person person = new Person();
Person person1 = new Person();
System.out.println(person.age);
}
}
class Person {
String name;
int age;
static String desc = "我是一个人";
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
//代码块
{
System.out.println("hellow block");
age = 1;//初始非化静态属性
info();//动态代码块可以调用静态结构与非静态结构
eat();//动态代码块可以调用静态结构与非静态结构
}
static {
System.out.println("hello,static block-1");
desc = "我是一个爱学习的人";//初始化静态属性
info();//静态代码块仅可以调用静态结构
}
static {
System.out.println("hello,static block-2");
desc = "我是一个爱学习的人";//初始化静态属性
}
public void eat() {
System.out.println("人吃饭");
}
public static void info() {
System.out.println("我是一个快乐的人");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
3.执行顺序
由父及子,静态先行;类内部(静态代码块、动态代码块、无参构造器、有参构造器)
class Root {
//1
static {
System.out.println("Root的静态初始化块");
}
//4.代码块优先于构造器先执行
{
System.out.println("Root的普通初始化块");
}
public Root() {
//5
//省略了super();
System.out.println("Root的无参数的构造器");
}
}
class Mid extends Root {
//2
static {
System.out.println("Mid的静态初始化块");
}
//6
{
System.out.println("Mid的普通初始化块");
}
public Mid() {
//省略了super();
//7
System.out.println("Mid的无参数的构造器");
}
public Mid(String msg) {
//通过this调用同一类中重载的构造器
this();
//8
System.out.println("Mid的带参数构造器,其参数值:"
+ msg);
}
}
class Leaf extends Mid {
//3
static {
System.out.println("Leaf的静态初始化块");
}
//9
{
System.out.println("Leaf的普通初始化块");
}
public Leaf() {
//通过super调用父类中有一个字符串参数的构造器
//10
super("尚硅谷");
System.out.println("Leaf的构造器");
}
}
public class LeafTest {
public static void main(String[] args) {
new Leaf();
new Leaf();//不再加载静态的代码块
}
}
一个类中只要碰到了一条语句就会加载静态结构,由父及子,静态先行
class Father {
static {
System.out.println("11111111111");
}
{
System.out.println("22222222222");
}
public Father() {
System.out.println("33333333333");
}
}
public class Son extends Father {
static {
System.out.println("44444444444");
}
{
System.out.println("55555555555");
}
public Son() {
System.out.println("66666666666");
}
public static void main(String[] args) { // 由父及子 静态先行
System.out.println("77777777777");
System.out.println("************************");
new Son();
System.out.println("************************");
new Son();
System.out.println("************************");
new Father();
}
}
4.赋值顺序
①默认初始化
②显示初始化
③在代码块中进行赋值(与②看谁写在前面谁就先执行,不会出现未定义就是用的错误,因为类中与方法体不同,先后定义也无所谓)
④构造器中初始化
⑤有了对象以后,使用"对象.属性"或"对象.方法"的方式进行赋值
public class Test {
public static void main(String[] args) {
Order o = new Order();
System.out.println(o.id);//3
Orders os = new Order();
System.out.println(os.id);//4
}
}
class Order {
{
id = 4;
}
int id = 3;
}
class Orders {
int id = 3;
{
id = 4;
}
}
十八、final关键字
1.final关键字的使用位置
①final(最终的)可以用来修饰的结构:类、变量(成员变量和局部变量)、方法、
②修饰类:此类不能有子类(不可以被其他类所继承),如:String、System、StringBuffer
③修饰方法:此方法不可以被子类重写,如:Object类中的getClass();
④修饰变量:此时的“变量”只可以被赋值或初始化一次,不可以再变,即称作一个常量。未进行显示赋值时需要在代码块、构造器中为其赋值,否则会报错
可以考虑赋值的位置有
(1)默认初始化:
默认初始化时,必须要在代码块中赋个值,其他不行,否则会报错。
final int LEFT;
{
LEFT = 1;
}
(2)显示初始化(每个对象的值都一样选用此方法)
final int I = 1;
(3)在代码块中进行赋值(需要抛异常、调方法等)
{
final int RIGHT=1;
}
(4)构造器中初始化(每个对象的值不一样选用此方法)
final int R;
public FinalA(){
R=1;
}
public FinalA(int n){
R=n;//必须再将Final修饰的属性赋值
}
(5)有了对象以后,使用"对象.属性"或"对象.方法"的方式进行赋值:不可以使用,不可以在set/get方法中使用final
⑤修饰局部变量:方法内变量、形参
方法体内变量:常量
形参:尽可以在调用时对该形参变量赋值一个实参,之后仅可以在此方法体内使用,不可以赋值,为一个常量
FinalA finalA=new FinalA();
finalA.show(10);
public void show(final int num){
num=20;//报错,只可使用不可改变
}
2.static final修饰属性和方法
①属性:全局常量(常量一般大写)
②方法:被类调且不能被重写
3.练习
(1)
public class Something {
public int addOne(final int x) {
return ++x;//不可以
return x + 1;//可以
}
}
(2)对该对象施加final并不是该对象的成员
public class Something {
public static void main(String[] args) {
Other o = new Other();
new Something().addOne(o);
}
public void addOne(final Other o) {//定义一个final的Other类型的变量(o指向另一个对象),o不可以在变即创建新的对象,o的属性i可以变
o = new Other();//加上这句就不可以了
o.i++;
}
}
class Other {
public int i;
}
十九、抽象类与抽象方法
1.abstract关键字的使用位置
abstract(抽象的)可以用来修饰的结构:类、方法
(1)abstract修饰类
①此类不能实例化(造对象)。虽然不能造对象了,但是也需要构造器,因为要给子类用。
②因此开发中都会提供抽象类的子类,让子类实例化
public class java27 {
public static void main(String[] args) {
Person p = new Person();//报错:类被abstract修饰,则不可以实例化
p.eat();
}
}
abstract class Person {
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void eat() {
System.out.println("吃饭");
}
}
(2)abstract修饰方法
public abstract void show();//抽象方法:只有方法的声明没有方法体
①抽象方法必须在抽象类中(抽象方法所在的类一定不可以造对象),抽象类中可以没有抽象方法
②抽象方法只有方法声明,没有方法体
③若子类重写了所有父类中的抽象方法以后,此子类方可实例化;若子类没有重写所有父类中的所有抽象方法,则此子类也是抽象类即也需要用abstract修饰子类
abstract class Person {
String name;
int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void eat() {
System.out.println("吃饭");
}
public abstract void show();//抽象方法:只有方法的声明没有方法体
}
abstract class Student extends Person {
public Student() {
super();
}
public void show(){}//重写show方法;或该类也得是抽象类,否则报错
}
2.abstract使用上的注意点
①abstract不能用来修饰:属性、方法块、构造器
②abstract不能修饰私有方法:因为被abstract修饰的方法需要子类重写,但私有方法只可以在此类中用,不可以被重写,因此没有意义
③abstract不能用来修饰静态方法:被重写的方法需要是非静态的
④abstract不能用来修饰被final修饰的方法:因为被final修饰的方法不可以被重写
⑤abstract不能用来修饰被final修饰的类:因为被final修饰的方法不可以被继承
3.练习
(1)
public class EmployeeTest {
public static void main(String[] args) {
Manager manager = new Manager("库克", 1001, 5000, 5000);
//可以利用多态
Employee employee = new CommonEmployee();
employee.work();
manager.work();
}
}
abstract public class Employee {
private String name;
private int id;
private double salary;
public Employee() {
}
public Employee(String name, int id, double salary) {
this.name = name;
this.id = id;
this.salary = salary;
}
public abstract void work();
}
public class Manager extends Employee {
private double bonus;
public Manager(String name, int id, double salary, double bonus) {
super(name, id, salary);
this.bonus = bonus;
}
@Override
public void work() {
System.out.println("管理员工提高效率");
}
}
public class CommonEmployee extends Employee {
@Override
public void work() {
System.out.println("员工在一线生产产品");
}
}
二十、抽象类的匿名子类
1.使用
相当于一个对象p
2.习题
匿名子类:在创建对象是对父类方法进行重写
public class Test {
public static void main(String[] args) {
method(new Student());//匿名对象(因为没有把对象赋给一个变量,所以叫做匿名的)
Work work = new Work();
method1(work);//非匿名类,非匿名对象
method1(new Work());//非匿名类,匿名对象
//创建了匿名子类的对象p,用此格式在{重写需要用到的方法},匿名子类非匿名对象,把父类中eat方法重写这个,并且进行方法的调用,所有调用时涉及到的eat方法在不做其他修改的情况下都会调用匿名子类中重写的方法
Person p=new Person(){
@Override
public void eat() {
System.out.println("人吃饭重写");
}
@Override
public void show(){
System.out.println("展示重写");
}
};
method1(p);
//创建匿名子类的匿名对象。匿名子类:在创建对象是对父类方法进行重写
method1(new Person(){
@Override
public void eat() {
System.out.println("人吃饭重写2");
}
});
}
public static void method(Student student) {
}
public static void method1(Person person) {
person.eat();;
person.show();
}
}
abstract class Person {
public void eat() {
System.out.println("人吃饭");
}
public void show(){
System.out.println("展示");
}
}
class Student {
}
class Work extends Person {
}
3.练习(抽象类、toString)
编写工资系统,实现不同类型员工(多态)的按月发放工资。如果当月出现某个 Employee对象的生日,则将该雇员的工资增加100元。 实验说明: (1)定义一个Employee类,该类包含: private成员变量name,number,birthday,其中birthday 为MyDate类的对象; abstract方法earnings(); toString()方法输出对象的name,number和birthday。 (2)MyDate类包含: private成员变量year,month,day ; toDateString()方法返回日期对应的字符串:xxxx年xx月xx日 (3)定义SalariedEmployee类继承Employee类,实现按月计算工资的员工处 理。该类包括:private成员变量monthlySalary; 实现父类的抽象方法earnings(),该方法返回monthlySalary值;toString()方法输 出员工类型信息及员工的name,number,birthday。 练习3 (4)参照SalariedEmployee类定义HourlyEmployee类,实现按小时计算工资的 员工处理。该类包括: private成员变量wage和hour; 实现父类的抽象方法earnings(),该方法返回wage*hour值; toString()方法输出员工类型信息及员工的name,number,birthday。 (5)定义PayrollSystem类,创建Employee变量数组并初始化,该数组存放各 类雇员对象的引用。利用循环结构遍历数组元素,输出各个对象的类 型,name,number,birthday,以及该对象生日。当键盘输入本月月份值时,如果本 月是某个Employee对象的生日,还要输出增加工资信息。
/**
* @author shkstart
* @create 2022-04-28 18:20
* (5)定义PayrollSystem类,创建Employee变量数组并初始化,该数组存放各
* 类雇员对象的引用。利用循环结构遍历数组元素,输出各个对象的类
* 型,name,number,birthday,以及该对象生日。当键盘输入本月月份值时,如果本
* 月是某个Employee对象的生日,还要输出增加工资信息。
*/
public class PayrollSystem {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
int cm = calendar.get(Calendar.MONTH);//使用Calendar获取当前月份,一月份为0以此类推
Employee[] employees = new Employee[2];//只是开辟了空间,并指向含有两个employees的空间,每个employee没有new对象,因此不违背抽象类
employees[0] = new SalariedEmployee("马森", 1001, new MyDate(1999, 2, 18), 10000);
employees[1] = new HourlyEmployee("马劳", 1002, new MyDate(1999, 1, 22), 60, 240);
for (int i = 0; i < employees.length; i++) {
System.out.println(employees[i]);//输出toString方法
double salary = employees[i].earnings();
if (cm + 1 != employees[i].getBirthday().getMonth()) {
System.out.println("月工资为:" + salary);
} else {
salary = salary + 100;
System.out.println("月工资为:" + salary);
}
}
}
}
/**
* @author shkstart
* @create 2022-04-28 16:46
* (1)定义一个Employee类,该类包含:
* private成员变量name,number,birthday,其中birthday 为MyDate类的对象;
* abstract方法earnings();
* toString()方法输出对象的name,number和birthday。
*/
public abstract class Employee {
private String name;
private int number;
private MyDate birthday;
public abstract double earnings();
public Employee(String name, int number, MyDate birthday) {
this.name = name;
this.number = number;
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public MyDate getBirthday() {
return birthday;
}
public void setBirthday(MyDate birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
//birthday要改成调用MyDate类的toDateString,因为MyDate类中是toDateString的格式输出
return "name='" + name + '\'' +
", number=" + number +
", birthday=" + birthday.toDateString() +
'}';
}
}
/**
* @author shkstart
* @create 2022-04-28 17:10
* (2)MyDate类包含:
* private成员变量year,month,day ;
* toDateString()方法返回日期对应的字符串:xxxx年xx月xx日
*/
public class MyDate {
private int year;
private int month;
private int day;
public MyDate(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
public String toDateString() {
return year + "年" + month + "月" + day + "日";
}
}
/**
* @author shkstart
* @create 2022-04-28 17:21
* (3)定义SalariedEmployee类继承Employee类,实现按月计算工资的员工处
* 理。该类包括:private成员变量monthlySalary;
* 实现父类的抽象方法earnings(),该方法返回monthlySalary值;toString()方法输
* 出员工类型信息及员工的name,number,birthday
*/
public class SalariedEmployee extends Employee {
private int monthlySalary;
public SalariedEmployee(String name, int number, MyDate birthday) {
super(name, number, birthday);
}
public SalariedEmployee(String name, int number, MyDate birthday, int monthlySalary) {
super(name, number, birthday);
this.monthlySalary = monthlySalary;
}
public int getMonthlySalary() {
return monthlySalary;
}
public void setMonthlySalary(int monthlySalary) {
this.monthlySalary = monthlySalary;
}
@Override
public double earnings() {
return monthlySalary;
}
@Override
public String toString() {
return "SalariedEmployee[ " + super.toString() + "]";
}
}
/**
* @author shkstart
* @create 2022-04-28 17:54
* (4)参照SalariedEmployee类定义HourlyEmployee类,实现按小时计算工资的
* 员工处理。该类包括:
* private成员变量wage和hour;
* 实现父类的抽象方法earnings(),该方法返回wage*hour值;
*/
public class HourlyEmployee extends Employee {
private int wage;//每小时的工资
private int hour;//月工作的小时数
public HourlyEmployee(String name, int number, MyDate birthday) {
super(name, number, birthday);
}
public HourlyEmployee(String name, int number, MyDate birthday, int wage, int hour) {
super(name, number, birthday);
this.wage = wage;
this.hour = hour;
}
@Override
public double earnings() {
return wage * hour;
}
@Override
public String toString() {
return "HourlyEmployee[ " + super.toString() + "]";
}
}
二十一、接口(interface)
1.接口的概述
①有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。–解决了单继承的局限性
②有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有继承的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打 印机、扫描仪、摄像头、充电器、MP3机、手机、数码相机、移动硬盘等都 支持USB连接。也比如:飞机、子弹、风筝、热气球都聚有飞的功能,子弹也具有攻击性。
③Java中,接口和类是并列的两个结构
2.接口的使用
(1)接口的定义
interface Flyable{
}
(2)接口的结构
JDK7以前:只能定义全局常量和抽象方法(不可以定义其他的,尤其是构造器(意味着接口不可以实例化))
①创建接口(仅可创建属性和方法)
②创建类实现接口{实现接口中部分(此类加abstract)或全部方法}
③对实现类进行实例化
①全局常量:public static final的
1.定义全局常量 public static final删了也没事,因为接口中默认定义为此形式
2.调用全局常量 Flyable.MAX_SPEED=1;不可以赋值
public class java32 {
public static void main(String[] args) {
System.out.println(Flyable.MAX_SPEED);
//Flyable.MAX_SPEED=1;不可以赋值
}
}
//全局常量 public static final删了也没事,因为接口中默认定义为此形式
public static final int MAX_SPEED = 7900;
int MIN_SPEED = 1;
②抽象方法:public abstract的
1.抽象方法 public abstract 可以省略
interface Flyable {
//抽象方法 public abstract 可以省略
public abstract void Fly();
void Stop();
}
③Java开发中,接口不可以实例化,通常通过让类去实现(implements)的方式来使用,即类实现接口;如果实现类实现了(就是继承中的重写)接口中的所有抽象方法,则此实现类可以实例化。如果实现类没有实现接口中所有的抽象方法,则需要给此类加上abstract
public class java32 {
public static void main(String[] args) {
System.out.println(Flyable.MAX_SPEED);
//Flyable.MAX_SPEED=1;不可以赋值
Plane plane = new Plane();
plane.fly();
}
}
如果实现类实现了(就是继承中的重写)接口中的所有抽象方法,则此实现类可以实例化。
class Plane implements Flyable {
@Override
public void fly() {
System.out.println("飞机通过引擎起飞");
}
@Override
public void stop() {
System.out.println("驾驶员减速停止");
}
}
如果实现类没有实现接口中所有的抽象方法,则需要给此类加上abstract
abstract class Kite implements Flyable{
@Override
public void fly() {
System.out.println("风筝飞");
}
}
④Java类可以实现多个接口,弥补了Java单继承的局限性(一个类可以实现多个接口)
继承以及多个实现的形式class CB extends CA implements IA,IB
class Bullet extends Object implements Flyable,Attackable{
@Override
public void attack() {
}
@Override
public void fly() {
}
@Override
public void stop() {
}
}
⑤接口与接口之间可以继承且可以多继承
public class java32 {
public static void main(String[] args) {
System.out.println(Flyable.MAX_SPEED);
//Flyable.MAX_SPEED=1;不可以赋值
Plane plane = new Plane();
plane.fly();
}
}
interface Attackable {
void attack();
}
interface Flyable {
//全局常量 public static final删了也没事,因为接口中默认定义为此形式
public static final int MAX_SPEED = 7900;
int MIN_SPEED = 1;
//抽象方法 public abstract 可以省略掉
public abstract void fly();
void stop();
}
class Plane implements Flyable {
@Override
public void fly() {
System.out.println("飞机通过引擎起飞");
}
@Override
public void stop() {
System.out.println("驾驶员减速停止");
}
}
abstract class Kite implements Flyable {
@Override
public void fly() {
System.out.println("风筝飞");
}
}
class Bullet extends Object implements Flyable, Attackable, CC {
@Override
public void attack() {
}
@Override
public void fly() {
}
@Override
public void stop() {
}
@Override
public void method1() {
}
@Override
public void method2() {
}
}
interface AA {
void method1();
}
interface BB {
void method2();
}
interface CC extends AA, BB {
}
⑥接口的具体使用体现多态性
⑦接口实际上可以看作是一种规范
//接口的使用也满足多态性
public class USBTest {
public static void main(String[] args) {
Computer computer = new Computer();
Flash flash = new Flash();
computer.transferData(flash);
}
}
//要和脑进行数据传输的规范,谁要和电脑传输数据都要调用这个接口把接口实现
interface USB {
void start();
void stop();
}
class Computer {
public void transferData(USB usb) {//USB usb=new Flash();
usb.start();
System.out.println("具体传输细节");
usb.stop();
}
}
class Flash implements USB {
@Override
public void start() {
System.out.println("U盘开启工作");
}
@Override
public void stop() {
System.out.println("U盘结束工作");
}
}
class printer implements USB {
@Override
public void start() {
System.out.println("打印机开启工作");
}
@Override
public void stop() {
System.out.println("打印机结束工作");
}
⑧体现面向接口编程
(3)JDK8新特性
JDK8:除了可以定义全局常量和抽象方法之外,还可以定义静态方法、默认方法
①定义静态方法(不用考虑重写)public static 类型 方法名(){},可以不写public,不可以不写static.接口中定义的静态方法只能够定义它的接口调用,实现类与对象都不可以调用,静态方法在实现类中的名字相同时不叫重写
②默认方法public default 类型 方法名(){},可以不写public,不可以不写default。通过实现类的对象可以调用默认方法,默认方法可以在实现类中重写
public class SubClassTest {
public static void main(String[] args) {
SubClass s = new SubClass();
ComparA.method1();//接口中定义的静态方法只能够定义它的接口调用
s.method1();//并不是调用重写接口中的方法,而是在实现类中新定义的方法
//s.method1();接口中定义的静态方法只能够定义它的接口调用,实现类与对象都不可以调用
s.method2();
}
}
class SubClass implements ComparA {
//静态方法不可以重写
public void method1() {
System.out.println("1");
}
@Override
public void method2() {
//默认方法可以在实现类中重写
System.out.println("Subclass:上海");
}
}
class SubClass implements ComparA {
}
public interface ComparA {
//定义静态方法(不用考虑重写)public static 类型 方法名(){},可以不写public,不可以不写static.接口中定义的静态方法只能够定义它的接口调用,实现类与对象都不可以调用
static void method1() {
System.out.println("北京");
}
//默认方法public default 类型 方法名(){},可以不写public,不可以不写default
public default void method2() {
System.out.println("上海");
}
}
③如果子类(实现类)继承的父类和实现的接口中声明了同名同参数的方法,则子类在没有重写此方法的情况下,默认调用父类中的同名同参的方法(类优先原则);重写后调用重写的方法
public interface ComparA {
default void method() {
System.out.println("上海");
}
}
public class SuperCLASS {
public void method(){
System.out.println("杭州");
}
}
class SubClass extents SuperCLASS implements ComparA, ComparB {
@Override
public void method() {
//默认方法可以在实现类中重写
System.out.println("Subclass:上海");//输出Subclass:上海,若没有在此重写则输出杭州
}
}
④实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么在实现类没有重写此方法的情况下会报错(接口冲突),如果想要实现此方法必须在抽象类中重写此方法
public interface ComparA {
default void method() {
System.out.println("上海A");
}
}
public interface ComparB {
default void method(){
System.out.println("上海B");
}
}
class SubClass implements ComparA, ComparB {//没有重写method()方法则会报错
}
⑤如何在子类(实现类)的方法中调用父类(父抽象类)中声明的方法与属性:父类接口.super.父类方法(); 父类接口.属性
class SubClass extends SuperCLASS implements ComparA, ComparB {
//重写父类的方法
@Override
public void method2() {
//默认方法可以在实现类中重写
System.out.println("Subclass:上海");
}
public void myMethod(){
//如何在子类(实现类)的方法中调用父类(父抽象类)中声明的方法
//调用自己重写的方法
method2();
//调用父类的方法
super.method2();
//调用父类接口中声明的方法
ComparA.super.method2();
ComparB.super.method2();
}
}
//例二
public class Test {
public static void main(String[] args) {
D d = new D();
d.show();
}
}
interface A {
int a = 1;
default void s() {
System.out.println("123");
}
}
interface B {
int a = 2;
default void ss() {
System.out.println("456");
}
}
class C implements A, B {
public void show() {
System.out.println(A.a);
A.super.s();//123
B.super.ss();//456
}
public void s1() {
B.super.ss();//456,想要在D中调用B父类中的ss,而不是C中重写后的SS可以再创建一个方法里面存放C父类中的SS,然后再D中对该方法进行调用
}
@Override
public void ss() {
System.out.println("456123");
}
}
class D extends C {
@Override
public void show() {
System.out.println(A.a);//1
super.ss();//456123
super.s1();//456
}
}
(4)JDK7+JDK8
//定义全局常量
int F = 1;
//定义抽象方法
void method();
//定义静态方法(不用考虑重写)public static 类型 方法名(){},可以不写public,不可以不写static
static void method1() {
System.out.println("北京");
}
//默认方法public default 类型 方法名(){},可以不写public,不可以不写default
public default void method2() {
System.out.println("上海");
}
3.匿名实现接口以及匿名对象
创建对象了就叫非匿名对象,这个对象不管是调用的还是当作参数的,但是优先看参数,如果方法无参再看调用
1.创建了接口的非匿名实现类的非匿名对象(确实有这个子接口,并且创建了该接口的对象)
2.创建接口的非匿名实现类的匿名对象(有这个接口,但是没有创建这个接口的对象)
3.匿名实现类的非匿名对象(没有这个类(接口)或不知道具体用哪个接口但是创建了这个接口的对象,在使用时进行重写,即没有定义新的接口或类去继承接口或类),本来下面没有phone类或接口,自然无法创建phone对象,使用匿名实现类的非匿名对象的方法创建了一个临时的仅可以用一次的类接口或类重写USB中的方法,并可以作为实参传值
4.匿名实现类的匿名对象(没有这个接口或不知道具体用哪个接口在使用时去重写类中的方法,且也没有创建这个接口的对象)
//接口的使用也满足多态性
public class USBTest {
public static void main(String[] args) {
//1.创建了接口的非匿名实现类的非匿名对象(确实有这个子接口,并且创建了该接口的对象)
Computer computer = new Computer();
Flash flash = new Flash();
computer.transferData(flash);
//2.创建接口的非匿名实现类的匿名对象(有这个接口,但是没有创建这个接口的对象)
computer.transferData(new printer());
//3.匿名实现类的非匿名对象(没有这个接口或不知道具体用哪个接口但是创建了这个接口的对象),本来下面没有phone类或接口,自然无法创建phone对象,使用匿名实现类的非匿名对象的方法创建了一个临时的仅可以用一次的类接口或类重写USB中的方法,并可以作为实参传值
USB phone=new USB() {//创建对象了就叫非匿名对象
@Override//没有定义实现类所以要在这里进行父类方法的重写
public void start() {
System.out.println("手机开始工作");
}
@Override
public void stop() {
System.out.println("手机停止工作");
}
};
computer.transferData(phone);
//4.匿名实现类的匿名对象(没有这个接口或不知道具体用哪个接口,且也没有创建这个接口的对象)
computer.transferData(new USB() {
@Override
public void start() {
System.out.println("XBOX开始工作");
}
@Override
public void stop() {
System.out.println("XBOX结束工作");
}
});
}
}
//要和脑进行数据传输的规范,谁要和电脑传输数据都要调用这个接口把接口实现
interface USB {
void start();
void stop();
}
class Computer {
public void transferData(USB usb) {//USB usb=new Flash();
usb.start();
System.out.println("具体传输细节");
usb.stop();
}
}
class Flash implements USB {
@Override
public void start() {
System.out.println("U盘开启工作");
}
@Override
public void stop() {
System.out.println("U盘结束工作");
}
}
class printer implements USB {
@Override
public void start() {
System.out.println("打印机开启工作");
}
@Override
public void stop() {
System.out.println("打印机结束工作");
}
}
4.面试题
(1)
interface A {
int x = 0;
int x1 = 3;
}
class B {
int x = 1;
int x4 = 4;
}
class C extends B implements A {
public void pX() {
System.out.println(x);//报错,父类和接口同名
System.out.println(super.x);//1,
System.out.println(A.x);//0,不能super.super
System.out.println(x1+x4);//3,4
}
public static void main(String[] args) {
new C().pX();
}
}
(2)
interface Playable {
void play();
}
interface Bounceable {
void play();
}
interface Rollable extends Playable,
Bounceable {
Ball ball = new Ball("PingPang");//常量
}
class Ball implements Rollable {
private String name;
public String getName() {
return name;
}
public Ball(String name) {
this.name = name;
}
public void play() {//重写了两个接口中的play方法
ball = new Ball("Football");//接口中定义的变量为常量,不可以改变
System.out.println(ball.getName());
}
}
5.练习
定义一个接口用来实现两个对象的比较。 interface CompareObject{ public int compareTo(Object o); //若返回值是 0 , 代表相等; 若为正数,代表当 前对象大;负数代表当前对象小 } 定义一个Circle类,声明redius属性,提供getter和setter方法 定义一个ComparableCircle类,继承Circle类并且实现CompareObject接口。在 ComparableCircle类中给出接口中方法compareTo的实现体,用来比较两个圆的半 径大小。 定义一个测试类InterfaceTest,创建两个ComparableCircle对象,调用compareTo 方法比较两个类的半径大小。
public class CompareCircleTest {
public static void main(String[] args) {
CompareCircle c1 = new CompareCircle(3.1);
CompareCircle c2 = new CompareCircle(3.2);
int flag = c1.compareTo(c2);
if (flag > 0) {
System.out.println("C1大!");
} else if (flag < 0) {
System.out.println("C2大");
} else if (flag == 0) {
System.out.println("C1与C2一样大");
}
int flag1=c1.compareTo(new String("AA"));//抛出异常
}
}
public interface CompareObject {
//若返回值是 0 , 代表相等; 若为正数,代表当前对象大;负数代表当前对象小
int compareTo(Object o);
}
public class Circle {
private double radius;//改为Double可利用包装类的方式比较大小
public Circle() {
}
public Circle(double radius) {//改为Double可利用包装类的方式比较大小
this.radius = radius;
}
public double getRadius() { return radius;//改为Double可利用包装类的方式比较大小
}
public void setRadius(double radius) {//改为Double可利用包装类的方式比较大小
this.radius = radius;
}
}
public class CompareCircle extends Circle implements CompareObject {
public CompareCircle(double radius) {
super(radius);
}
@Override
public int compareTo(Object o) {
if (this == o) {
return 0;
}
if (o instanceof CompareCircle) {
CompareCircle c = (CompareCircle) o;
//利用包装类的compareTo()方法直接比较大小
return this.getRadius().compareTo(c.getRadius());
// if (this.getRadius() > c.getRadius()) {
// return 1;
// } else if (this.getRadius() < c.getRadius()) {
// return -1;
// } else {
// return 0;
// }
} else {
throw new RuntimeException("传入的数据类型不匹配");
}
}
}
二十二、内部类
1.定义
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。
在Java中,允许一个类A的定义位于另一个类B的内部,前者类A称为内部类,后者类B称为外部类
2.分类:成员内部类(静态成员内部类和非静态成员内部类) vs 局部内部类(方法内、代码块内、构造器内)、匿名内部
class Person {
//静态成员内部类
static class AA {
}
//非静态成员内部类
class FF{}
//局部内部类
{
class BB {
}
}
public void method() {
class CC {
}
}
public Person() {
class DD {
}
}
}
(1)成员内部类
一方面,作为外部类的成员
①可以调用外部类的结构(属性、方法等)
②可以被static修饰
③可以被四种权限修饰符修饰(private、缺省、protect、public)
另一方面,作为一个类
①类内可以定义属性、方法、构造器、代码块、在定义内部类等
②可以被final修饰表示此类不能被继承,若不使用final也可以被继承
③可以被abstract修饰,则类不可以被实例化
class Person {
String name;
int age;
public void eat() {
System.out.println("eat");
}
//静态成员内部类
static abstract class AA {
String name;
public AA(String name) {
this.name = name;
}
public void show() {
System.out.println("show");
}
}
//非静态成员内部类
final class FF {
String name;
public FF(String name) {
this.name = name;
}
public void sing() {
System.out.println("sing");
eat();//省略了Person.this.eat();
}
}
3.关注如下三个问题
(1)如何实例化成员内部类的对象
①在静态内部类中只能使用外部类中的静态成员
②在非静态内部类中不可以创建静态成员
//创建AA实例(静态成员内部类)--外部类名.内部类名 变量名=new 外部类名.内部类名();
Person.AA a = new Person.AA();
a.show();
//创建FF实例(非静态员内部类)--外部类名.内部类名 变量名=new 外部类名().new 内部类名();
Person p = new Person();
Person.FF f = p.new FF();
//或Person.FF f=new Person().new FF();
f.sing();
(2)如何在成员内部类中区分调用外部类的接口
①调用形参name–name
②调用内部类中的属性name–this.name
③调用外部类中的属性name–外部类名.this.name
class Person {
String name = "小明";
final class FF {
String name = "FF";
public FF() {
}
public FF(String name) {
this.name = name;
}
public void sing() {
System.out.println("sing");
eat();//省略了Person.this.eat();
}
public void display(String name) {
//调用形参name
System.out.println(name);//fx
//调用FF类中的name
System.out.println(this.name);//FF
//调用Person类中的name
System.out.println(Person.this.name);//小明
//name唯一,没有重复只有一个name可直接用name
}
}
}
(3)开发中局部内部类的使用
public class Test {
//很少见的局部内部类
public void method() {
//局部内部类
class AA {
}
}
//常见的局部内部类:返回一个Comparable接口的实现类,并造一个对象对象
public Comparable getComparable() {
//创建一个实现了Comparable接口的类:局部内部类
// class MyComparable implements Comparable {//Compareable是一个已经写好的类,Compareable是一个接口,无法实例化,所以建立一个MyComparable进行实例化
//
// @Override
// public int compareTo(Object o) {
// return 0;
// }
// }
// return new MyComparable();new一个MyComparable()对象,则会重写CompateTo方法,并返回执行结果
//方式二 创建了一个实现Comparable接口的匿名实现类的匿名对象
return new Comparable() {
@Override
public int compareTo(Object o) {
return 0;
}
};
}
}
(2)局部内部类
①使用
public class Test {
public static void main(String[] args) {
Outer outer=new Outer();
outer.test();
}
}
class Outer{
private int num=4;
public void test(){
class Inner{
public void show(){
System.out.println("num="+num);
}
}
Inner inner=new Inner();
inner.show();
}
}
②在局部内部类的方法中,想要调用局部内部类所声明的方法中的局部变量的话,要求此局部变量声明为final的(JKD7之前要求手动添加final)
public class Test {
public void method(){//局部内部方法
int num=10;//默认为final
class AA{//局部内部类
public void show(){//局部内部类中的方法
//num=20;final修饰的变量不可以修改,加上会报错
System.out.println(num);
}
}
}
}
③内部类也可以实现外面的接口
二十三、异常处理
1.异常
(1)定义
在Java语言中,将程序执行中发生的不正常情况称为“异常” 。 (开发过程中的语法错误和逻辑错误不是异常)
(2)分类
Java程序在执行过程中所发生的异常事件可分为两类:
①Error:Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源 耗尽等严重情况。比如:StackOverflowError和OOM。一般不编写针对性的代码进行处理。
public class Test {
public static void main(String[] args) {
//错误:栈溢出
main(args);
//错误:堆溢出OOM
Integer[] arr = new Integer[1024 * 1024 * 1024];
}
}
②Exception: 其它因编程错误或偶然的外在因素导致的一般性问题,可以使 用针对性的代码进行处理。例如: 空指针访问 、试图读取不存在的文件、网络连接中断、数组角标越
对于这些错误,一般有两种解决方法:一是遇到错误就终止程序 的运行。另一种方法是由程序员在编写程序时,就考虑到错误的检测、错误消息的提示,以及错误的处理。
捕获(处理)错误最理想的是在编译期间,但有的错误只有在运行时才会发生。 比如:除数为0,数组下标越界等
分类:编译时异常和运行时异常
①运行时异常(非受检异常):指编译器不要求强制处置的异常。一般是指编程时的逻辑错误,是程序员应该积极避免其出现的异常。java.lang.RuntimeException类及它的子类都是运行时异常。对于这类异常,可以不作处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响。
②编译时异常(受检异常):是指编译器要求必须处置的异常。即程序在运行时由于外界因素造成的一 般性异常。编译器要求Java程序必须捕获或声明所有编译时异常。对于这类异常,如果程序不处理,可能会带来意想不到的结果。
(3)异常体系结构
java.lang.Throwable
①java.lang.Erroe:一般不编写代码进行处理
②java.lang.Exception:可以进行异常处理。
编译时异常(checked):IOException–FileNotFoundException
//编译时异常
File file = new File("hello.txt");
FileInputStream fis = new FileInputStream(file);
int data = fis.read();
while (data != -1) {
System.out.println((char) data);
data = fis.read();
}
fis.close();
运行时异常(unchecked):NullPointerExceptuon
ArrayIndexOutOfBoundsException
ClassCastException
NumberFormatException
public static void main(String[] args) {
//运行时异常
//空指针异常NullPointerException
int[] a = null;
System.out.println(a[3]);
String s = null;
System.out.println(s.charAt(0));
//数组脚边越界ArrayIndexOutOfBoundsException
int[] a1 = new int[10];
System.out.println(a[10]);
//字符串脚边越界StringIndexOutOfBoundsException
String s1 = "abc";
System.out.println(s1.charAt(3));
//类型转换越界ClassCastException
Object o = new Date();
String s2 = (String) o;
//数字转换异常NumberFormatException(是字符串是数字可以转,但是给此字符串赋值带有非数字就不可以转了)
String s3="123";
s3="abc";
int num=Integer.parseInt(s3);
//输入不匹配InputMismatchException(nextInt输入整形但是用户输入的是其他的类型)
Scanner scanner=new Scanner(System.in);
int score=scanner.nextInt();
System.out.println(score);
//算数型异常ArithmeticException(比如0不可以做除数)
int m=10;
int n=0;
System.out.println(m/n);
}
2.异常处理机制
抓抛模型
过程一 ”抛“:程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象。并将此对象抛出给程序的调用者。一旦抛出对象以后,其后的代码不再执行。
关于异常对象的产生:①系统自动生成的异常对象
②手动的生成一个异常对象,并抛出(throw)
②手动抛一个异常
public class Test {
public static void main(String[] args) {
Student s = new Student();
//throws后在此进行处理
try {
s.regist(-1001);
} catch (Exception e) {
// e.printStackTrace();可以不用这个,用getMessage,获得下面throw new Exception(message)中的message
System.out.println(e.getMessage());
}
}
}
class Student {
private int id;
public void regist(int id) throws Exception {
if (id > 0) {
this.id = id;
} else {
//手动创建一个对象,从Exception(需要在编译时进行处理,可以选择在throw处进行try-catch进行处理也可以使用throws往上抛在其上面调用处进行处理)或RuntimeException(运行时异常,不用处理,运行时会报错)中选择
//throw new RuntimeException("您输入的数据非法!");()中的属性是message属性,相当于给message赋值,不用加其他的内容进行处理
throw new Exception("您输入的数据非法");//可以选择try-catch在此进行处理,也可以选择throws网上抛
}
}
}
过程二 ”抓“:可以理解为异常的处理方式:①try-catch-finally ②throws(一种处理异常的方式)
(1)try-catch-finally
try/catch实际上是把异常处理掉了,会继续执行下面的内容,因此两个try/catch都会执行
①使用try将可能出现异常的代码包装起来,在执行过程中一旦出现异常,就会生成一个对应的异常类对象,并根据此对象的类型,去catch中进行匹配
②try中的异常对象匹配到满足此异常的catch时(可能有多个异常,则会分别匹配相应的catch),就会进入catch中进行异常处理,一旦处理完成,在没有final的情况下就跳出try-catch结构,继续执行结构后的代码
③catch中的异常类型,如果没有字符类关系则声明的顺序无所谓;若catch中的异常类型,满足子父类关系,则要求子类声明在父类的上面,否则报错
try{
//可能出现异常的代码
}catch (异常(必须选对异常) 变量名){
//处理异常的方式1
}catch (Exception e){
//处理异常的方式2
}
finally {
//一定会执行的代码
}
④常用的异常对象处理的方式
①System.out.println(e.getMessage());
②e.printStackTrace();//常用
⑤在try结构中声明的变量在出了try结构以后便不可再用。可以将这个变量声明到try结构之前,并初始化,如果没有初始化编译器会考虑到有可能没有异常,因为在try中赋值,则相当于此变量没有初始化,因此必须先初始化一个默认值
package java42;
import javax.imageio.IIOException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* @author shkstart
* @create 2022-05-02 10:50
*/
public class java42 {
public static void main(String[] args) {
String s3 = "123";
s3 = "abc";
int num = 0;//声明在外面要初始化
try {
//把异常的那个语句或那些语句写入try中,自动生成异常对象抛给e
num = Integer.parseInt(s3);
} catch (NumberFormatException e) {//选择对应的catch中的异常并执行相应catch中的语句
System.out.println(e.getMessage());
//二选一
e.printStackTrace();
} catch (NullPointerException e) {
System.out.println("出现了空指针异常,不要着急");
} catch (Exception e) {
e.printStackTrace();
}
try {
File file = new File("hello.txt");
FileInputStream fis = new FileInputStream(file);
int data = fis.read();
while (data != -1) {
System.out.println((char) data);
data = fis.read();
}
fis.close();
} catch (FileNotFoundException e) {//解决了一部分
e.printStackTrace();
} catch (IOException e) {//①解决了另一部分 ②两个合起来解决了全部异常 ③FileNotFoundException是IOException的子类,所以顺序不可以颠倒
e.printStackTrace();
}
}
}
⑥使用try-catch-finally处理编译时异常,在编译时不再报错,但是运行时仍可能报错。相当于我们使用try-catch-finally结构将一个编译时可能出现的异常延迟到运行时出现了;开发中由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了,针对编译时异常,一定要考虑异常处理
⑦try-catch结构可以嵌套
⑧finally的使用:
1.是一个可选的。
2.finally中声明的是一定会被执行的代码,即使catch中又出现异常了,try中有return语句,catch中有return语句等情况,也一定会执行finally中的代码。
public int method() {
try {
int[] arr = new int[10];
System.out.println(arr[10]);//执行到此出异常了跳转到catch,如过该句数组没有越界即没有异常则先执行finally再return1
return 1;
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
return 2;//跳转后该返回2了,但是先执行finally在返回2
} finally {
System.out.println("我一定会被执行");
return 3;//跳转到finally后返回return3而不是返回return1或2
}
}
3.哪些操作放入finally中:像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己手动的进行资源的释放。此时资源释放就需要声明在finally中,因为资源的释放一定要被执行。
public class java44 {
public static void main(String[] args) {
FileInputStream fis = null;
try {
File file = new File("hello.txt");
fis = new FileInputStream(file);
int data = fis.read();
while (data != -1) {
System.out.print((char) data);
data = fis.read();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (fis != null) {//如果是null说明资源都没创建好,也就不用关闭了
fis.close();//关闭时也有异常,使用异常的嵌套
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
(2)throws+异常类型 处理方式(重写时:父类抛出的异常大于等于子类抛出的异常;父类的方法中没有抛异常,子类也不可以抛异常)
①throws+异常类型 写在方法的声明处,指明此方法执行时,可能会抛出的异常;一旦当方法体执行时出现异常,仍会在异常代码代码处生成一个异常类的对象,此对象满足throw后的异常类型是就会被抛出。异常代码后面的代码不再执行。
public class java45 {
public static void main(String[] args) {//抛到main方法就不要再抛了,使用try-catch-finally解决它
try {
method2();
} catch (IOException e) {
e.printStackTrace();
}
method3();//因为method3中进行了异常处理,所以在此调用时不会出现异常
}
public static void method3() {
try {
method2();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void method2() throws IOException {
method();
}
public static void method() throws FileNotFoundException, IOException {
File file = new File("hello.txt");
FileInputStream fis = new FileInputStream(file);
int data = fis.read();
while (data != -1) {
System.out.println((char) data);
data = fis.read();
}
fis.close();
System.out.println("哈哈哈");//不会执行
}
}
(3)总结:
①try-catch-final:真正的将异常给处理掉了
②throws+异常类型:只是将异常抛给了方法的调用者,没有真正将异常处理掉
③重写时:父类抛出的异常大于等于子类抛出的异常;父类的方法中没有抛异常,子类也不可以抛异常)
public class Test {
public static void main(String[] args) {
Test t = new Test();
t.display(new SubClass());//重写时:父类抛出的异常大于等于子类抛出的异常。因为多态性的原因可能传入一个子类参数,如果子类的异常小于等于父类的异常,那么在display中设置的try
// -catch可以将解决;如果子类的异常大于父类的异常则display的try-catch可能罩不住
}
public void display(SuperClass s) {
try {
s.method();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class SuperClass {
public void method() throws IOException, FileNotFoundException {
}
}
class SubClass extends SuperClass {
@Override
public void method() throws IOException, FileNotFoundException {
}
}
(4)开发中这两种异常处理方式如何选择(一般选择一个就可以)
①如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能throws。意味着子类重写的方法有异常,必须使用try-catch-finally方式处理
②执行的方法A中,先后又调用了另外的几个方法,这几个方法时递进关系(下一个的执行与上一个的执行结果有关),我们建议这几个方法使用throws的方式全部抛到最上面的方法A进行处理,而执行的方法A可以考虑使用try-catch-finally方式进行处理
3.用户自定义异常类
(1)如何自定义异常类
①创建自定义异常类并继承RuntimeException或Exception
②提供serialVersionUID的全局常量,唯一标识此类
③提供重载的构造器
//创建自定义异常类继承RuntimeException或Exception
public class MyException extends RuntimeException {
static final long serialVersionUID = -7034897190745766939L;
public MyException() {
}
public MyException(String msg) {
super(msg);
}
}
class Student {
private int id;
//可以抛MyException也可以是它的父类如Exception
public void regist(int id) throws MyException {
if (id > 0) {
this.id = id;
} else {
throw new MyException("不能输入负数");//可以选择try-catch在此进行处理,也可以选择throws网上抛
}
}
}
class Test {
public static void main(String[] args) {
Student s = new Student();
try {
s.regist(-1001);
} catch (MyException e) {
System.out.println(e.getMessage());
}
}
}
4.练习
(1)
public class ReturnExceptionDemo {
static void methodA() {
try {
System.out.println("进入方法A");//1
throw new RuntimeException("制造异常");//抛出异常即抛出message信息(设置message信息,使用getmessage调用时显示用的),并不是输出,抛出之后的代码不再执行,但是抛出前要先执行finally,在getmessage打印 3
}finally {
System.out.println("用A方法的finally");//2
}
}
static void methodB() {
try {
System.out.println("进入方法B");//4
return;
} finally {
System.out.println("调用B方法的finally");//5
}
}
public static void main(String[] args) {
try {
methodA();
} catch (Exception e) {
System.out.println(e.getMessage());//执行完2后输出message也就是制造异常
}
methodB();
}
}
(2)
编写应用程序EcmDef.java,接收命令行的两个参数,要求不能输入负数,计算两数相除。
对数据类型不一致(NumberFormatException) 、缺少命令行参数 (ArrayIndexOutOfBoundsException、除0(ArithmeticException)及输入负数(EcDef 自定义的异常)进行异常处理。
提示:
(1)在主类(EcmDef)中定义异常方法(ecm)完成两数相除功能。
(2)在main()方法中使用异常处理语句进行异常处理。
(3)在程序中,自定义对应输入负数的异常类(EcDef)。
(4)运行时接受参数 java EcmDef 20 10 //args[0]=“20” args[1]=“10” (5)Interger类的static方法parseInt(String s)将s转换成对应的int值。 如:int a=Interger.parseInt(“314”); //a=314;
public class EcmDef {
public static void main(String[] args) {
try {
int i = Integer.parseInt(args[0]);
int j = Integer.parseInt(args[1]);
int result = ecm(i, j);
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
} catch (ArithmeticException e) {
e.printStackTrace();
} catch (EcDef e) {
System.out.println(e.getMessage());
}
}
public static int ecm(int i, int j) throws EcDef {
if (i < 0 || j < 0) {
throw new EcDef("分子或分母小于0");
}
return i / j;
}
}
public class EcDef extends Exception {
static final long serialVersonUID = -33875164229948L;
public EcDef() {
}
public EcDef(String msg) {
super(msg);
}
}