java初级笔记
文章目录
第一章 Java技术简介
一、Java的性质
1. 简单性
2.*可移植性
3.*面向对象
4.分布式
5.*安全性
6.体系结构中立
7.健壮性
8.解释型
9.高性能
10.*多线程
11.*动态性
面向对象
面向对象,木匠做椅子,先关注的是所制作的椅子而不是工具,非面向对象的,木匠主要先考虑的是所用的工具。
两个基本概念:类,对象
三大特性:封装性,继承性,多态性
可移植性
数据具有固定字节或是格式,移植到其他操作系统时不会发生改变,可在多操作系统上使用。
Java语言是运行在Java虚拟机上(JVM)的,所以在不同操作系统当中,Java在JVM上运行。对于不同的平台有不同的JVM。
动态性
可为正在运行的程序增加代码,不会影响运行中程序的使用,相比于C和C++而言,更加具有动态性,,可以很容易的实现运行程序的演进。
二、关于对Java常见误解的解释
1.Java是HTML的拓展
Java是一种程序设计语言,HTML是一种描述网页结构的方式,二者没有任何共同之处
2.Java是专用的,应避免使用
Java语言是闭源的,但是可以查看,可以很好的使用,在未来不久,Java语言专利到期后,会转为开源,
3.JavaScript是Java的简易版
JavaScript是一种网页中使用的脚本语言,只有名字有些相像,其他并无任何关系
第二章 Java程序设计环境
安装Java开发工具包
1.下载jdk
下载Java开发工具包,可以访问Oracle公司的网站:www.oracle.com/technetwork/java/javase/downloads进行下载,在得到所需软件之前需要明白大量专业术语,例如编写Java程序的程序员使用的软件Java Developmen Kit(jdk),这就是我们所需要下载的软件专业术语
2.设置JDK
这个可以百度自行搜索
3.设置环境变量
4.测试JDK能否正常使用
1.win+R,输入cmd2.输入javac.exe 出现如下界面
3.输入java -version 注意中间有空格
(如果输入java -version无反应,可以输入java.exe -version)
出现如下界面
安装成功后,即可使用集成开发软件编写代码了,如果出现错误请重新安装jdk,或者安装低版本jdk
5.使用集成开发环境
我们可以使用一些免费的集成开发软件,例如Eclipse,IntelliJ IDEA,NetBeans等
6.简单的命令行指令应用
dir:列出当前目录下的文件以及文件夹
md:创建目录
rd:删除目录
cd:进入指定目录
cd…:返回至上一级目录
cd\:返回至根目录
del:删除文件
exit:退出dos命令
第三章 Java的基本程序设计结构
一、一个简单Java应用程序应具有的结构
public class Test{
public static void main(String[] args){
System.out.println("Hello world");
}
}
例如以上简单Java程序,此程序虽然简单,但是所有java程序都应具备这种结构
Java程序结构
1.Java程序中是区分大小写的,如果出现大小写拼写错误,程序将无法运行
2.关键字public成为访问修饰符,这些修饰符用于控制程序的其他部分对于这段代码的访问级别。
3.关键字class表明Java程序中的全部内容包含在类中,class后紧跟类名
4.类名问题:Java中定义类名的规则很宽松,名字必须以字母开头,其后可以添加符号与字母的任意组合,但是不可以使用Java保留字,类名规范为大写字母开头的名词,例如:class Sample,如果类名由多个名词构成,那么应以每个单词的首字母大写定义,例如:class FristSample.
5. 定义Java程序名字后切记不要添加(.class)拓展名,程序执行后会自动添加
6.在以上例子中我们使用了System.out对象并调用了println()方法,注意,点号(.)用于调用方法,Java中使用调用方法的通用语法是object.method(parameters参数)
7.代码中的大括号({ }),Java中的任何方法都用 ‘{’开始,用 ’ } ’ 结束。
8.在Java中,每个句子必须用分号(;)结束
9. 执行Java程序是,Java虚拟机总是从指定类中的main方法开始执行,所以为了代码能够执行,必须在类的源文件中必须包含一个main方法,并在main方法中调用这些方法(main方法必须声明为public)
10.在一个java源文件中可可以声明多个class,但是,最多有一个类声明为public的,而且要求声明public的类的类名必须与源文件名相同
11.编译源文件(.java)后或出现一个或多个字节码文件(.class),字节码文件的名称为类名
使用Dos命令运行一个简单的Java程序,输出HelloWorld
Java文件的建立及编译
Java程序是通过将代码编写到.java文件(源文件)中,再由javac.exe进行编译的,编译完成后生成.class文件(字节码文件),最后通过java.exe对生成的.class文件进行运行的。(注:.class文件的命名是在对源文件编译完成后,将源文件中的类名用作于.class文件的名字)。
编译,运行,输出HelloWorld
建立一个.txt文件,命名为Helloworld,更改文件类型为.java类型,这边建立一个Helloworld.java文件,使用记事本打开,如下图输入
打开Dos命令,进入.java文件路径,并输入javac HelloWorld.java进行编译,生成HelloChina.class文件(取名为类名)
输入java HelloChina.class进行运行,最后输出HelloWorld
二、注释
//注释方法以及使用
// 1.单行注释:符号// 在输入//符号后 符号后同行内容不会影响代码运行
//2.多行注释:符号 /* */ 在符号/* */之间的内容会被注释,不影响代码的运行
public class Notes {
public static void main(String[] args) {
System.out.println("hellowrld");//输出helloworld
/* System.out.println("helloworld");
System.out.println("helloworld"); 在符号中间的内容被注释
System.out.println("helloworld");
*/
}
}
三、数据类型
//
//数据类型:整型(byte,int,short,long) 浮点型(float,double) 布尔型(boolean) 字符型(char),引用型数据-字符串(String)
//
public class DateType {
public static void main(String[] args) {
//整型 long>int>short>byte
int a=1;
short b=2;
long c=1000000000000000l; // 长整型之后要加l(L),输出结果不带L,不加l会默认为int类型,编译不报错
//浮点型 double>float
float n=0.3f;// float类型之后要加 f或F 否则 则会被默认为是double类型,不加f会默认为double类型
double m=0.34;// double类型之后可以选择 加d或D或不添加
//布尔型 (整型值与布尔值不能相互转换)
boolean one=true;
boolean two=false;//布尔型只有两种值
//字符型
char s='a';// 定义一个字符型 符号是'',内容可以是一个字符,转义字符,Unicode的值
//char s=97的值和char s='a'的值相同,因为java语言会自动把97转义为小写字母a
char car='\n';
/* 转义可使用转义序列(例如:\b )或者使用Unicode值转义
(例如:\u0008),这两样都表示退格的意思 Unicode值由 \u加上4个16进制数组成*/
/*!!! 在注释中使用小心使用Unicode值,在注释中,打出\u会提示转义错误,在Java中,即使是在注释中,也依旧会自动转义Unicode值,无意中在注释打出\u,或者包含\u的句子会自动转义,最后导致出现错误*/
System.out.println("注意Unicode转义在注释后定义转义符号\u等会提示转义错误");//输出结果:转义出错
//字符串
String ni="wode"; //定义一个字符串 符号是""
System.out.println(ni);
}
}
四、变量和常量
1.声明变量
//变量(Variable)
//声明一个变量的名字可以定义很多,范围有数字.字母.符号(_,$)
//符号不可单独做一个变量名
//不可使用保留字作为变量名
public class Variable {
public static void main(String[] args) {
//定义一个变量
int a;
//定义多个变量
int d,e,f;
//给变量赋值的方法
int b;
b=1;
//或者
int c=1;
}
}
变量的作用域,其定义所在的一对{ }中,变量只有在其作用域内有效,同一作用域中,不能重复定义变量的名。
public class Variable{
public static void main(String[] args){
int num=1;
System.out.println(num);
Variable2 v=new Variable2();// v 为引用Variable类的对象
System.out.println(v.num);//在这里调用variable2类中的num变量
//两个相同的变量名,但是其作用域不一样,所以可以使用
}
}
class Variable2{
int num=2;
}
2.变量的初始化
定义变量的格式:数据类型 变量名 = 变量值;
在Java语言中,声明一个变量要对其进行初始化,如果声明完,没有对其赋值直接进行使用,会出现错误,例如以下
int a;
System.out.println(a);//没有对该变量进行初始化,直接使用会出现错误
声明完变量后,我们应对其进行初始化,如下
//对变量进行初始化的方法有两种
//可以在声明变量的同时直接对变量进行初始化
int a=10;
//也可以在声明变量后进行变量的初始化
int a;
a=10;
变量要先声明并初始化,然后在进行使用,变量的初始化尽量靠变量第一次使用的地方近一些
3.常量
//常量(final)
//定义一个常量用关键字 final
//关键字final表示这个变量只能被赋值一次,赋值后不能更改
//类常量,定义在main方法的外部,类常量可以在一个类的多个方法使用
public class Final {
public static final int a=1;//使用static final定义一个类常量,在此类中可以被多个方法使用,添加public可以被其他类使用
public static void main(String[] args) {
final int b=2;//定义一个常量,此常量的值不可被改变
b++;//当尝试改变b的值时会报错
System.out.println(b);
}
}
4.运算符
//运算符
//算术运算符 + - * /(除) %(求余) (也叫二元运算符)
//赋值 和运算符
//自增与自减
//关系运算符
//位运算符
public class Operator{
//运算符 + - * / % 前++ --、 后++ --
public static void main(String[] args){
//除号:
int num1=12;
int num2=5;
int result1=num1 / num2;
System.out.println(result1); //2
int result2 = num1 / num2 * num2;
System.out.println(result2); //10
double result3 =num1 / num2;
System.out.println(result3); //2.0
double result4 = num1 / num2 + 0.0; //2.0
double result5 = num1 / (num2 + 0.0); // 2.4
System.out.println(result5);
double result6 = (double)num1 / num2; //2.4
double result7 = (double)num1 /(double) num2; //2.4
double result8 = (double)(num1 / num2); //2.0
//取模(求余)
//结果的符号与被模数的符号相同
//开发中,经常使用%来判断能否被除尽的情况,结果为0被除尽
int m1 = 12;
int n1 = 5;
System.out.println("m1 % n1 ="+m1%n1); //2
int m2 = -12;
int n2 = 5;
System.out.println("m2 % n2 ="+m2%n2); // -2
int m3 = 12;
int n3 = -5;
System.out.println("m3 % n3 ="+m3%n3); //2
int m4 = -12;
int n4 = -5;
System.out.println("m4 % n4 ="+m4%n4); // -2
}
public class Calculation {
public static void main(String[] args) {
//赋值 和运算 符
int x=1;
x=x+4;//或者x+=4 其他算数运算符也是如此
x+=3.5;//如果这种写法的两边类型不一致时,就会发生强制类型转换 将其计算结果类型转换成与左边的一致
System.out.println("x的值:"+x);
int num=15;
num %= 5;//num = num % 5
System.out.println(num);//0
num +=2;//num=2 这种写法不改变数值的类型
//自增与自减
int z=1,h=2;
z=z*h++;//这样会先计算z*h,在计算h=h+1
z=z*++h;//这样会先计算h++,再计算z*h
//减号与加号同样
//注意点:
short s1 = 10;
//s1 = s1 +1; 编译失败,1为int类型
//s1=(short)(s1+1);//正确的
s1++;//自增一不会改变本身的数据类型
System.out.println(s1);
//问题:
byte bb1=127;
bb1++;
System.out.println("bb1:"+bb1);//输出为 -128,加一导致溢出,符号位由零变成一,最后的值为-128
//关系运算和boolean,关系运算符返回值是布尔型
//检测相等性 可用== 3==7 结果为flase
//检测不相等 可用!= 3!=7 结果为true
//注意!!! 要区分=和 == 的区别
boolean b1=true;
boolean b1=false;
System.out.println(b2 == b1); // false b2与b1不相同
System.out.println(b2 = b1); //true 将b1的值赋给b2并输出
//还有经常用的 > < >= <= 等
// &(判断符号两边的表达式,全真则真) |(判断两边表达式,一真则真)
// &&(与 一假则假,遇到假就为假) || (或 一真则真)
/*三元操作符 条件表达式 ?表达式1:表达式2 例 x<y?x:y
//最后用变量来接受结果
//int x =(m>n)?m:n;,
int x1= 1,y1=2;
String max=x1>y1?x1:"y大"; //编译错误,x1为int类型
//!!!注意,结果表达式的类型要为同一类型
逻辑为 如果条件表达式的值为真 则返回表达式1 假则返回表达式2
*/
//位运算符
//括号与与运算符级别
// a&&b||c
//&&优先级高于|| 等价于 (a&&b)||c
//a+=b+=c +=是右结合运算符 所以等价于a+=(b+=c)先计算b+=c 再用 b+c的值加a
}
}
其他位运算符
5.数值类型之间的转换
//类型转换
int n=123456789;
float f=n;
//在数据类型转换时,高精度向低精度转换会丢失精度
System.out.println(f);//输出结果为1.23456792E8
int num1=3;
float num2=4.56f;
System.out.println(num1+num2);
/*当二元运算符链接两个值时会先先将两个操作数转换为同一种类型在进行计算
以num1+num2为例
如果两个操作数中有一个是double类型,另一个就会转变为double类型
否则,如果其中一个操作数是float类型,另一个就会转变为float类型
否则,如果其中一个操作数是long类型,另一个就会转变为long类型
否则,两个操作数都会转变为int类型
//当byte,char,short三种变量类型做运算时,结果都为int类型,
//float 类
*/
//强制类型转换
double xy=9.997; //将double类型强制转换为int类型
int nx=(int) xy;//丢失精度 变为整型 nx=9
int next=3;
float nex=(float)next;//将int类型强制转换为float类型
System.out.println(nex);//输出结果为3.0
6.字符串
字符串的定义:
(1)用String定义字符串,(2)给字符串初始化赋值时使用( " ")
String a = "hello";//定义一个字符串,用""
String b = "study";
String c = "Java";
String d="Hello";
String e=123;//编译错误
子串:
//子串 用substring方法可以从字符串中提取子串
String a = "hello";
String s = a.substring(0, 3); //substring从0开始计数,到3为止,但不包括3
System.out.println(s);//输出结果为hel
字符串的拼接:
//字符串拼接
String a="hello";
String b="我今年";
String c="world";
String sum = a + c;//字符串拼接中间没空格,+会按照顺序将两个字符串拼接起来
System.out.println(sum);//输出结果helloworld
int age = 13;
String ra = b + age;//当字符串与非字符串类型拼接时,会自动将其转换为字符串类型
System.out.println(ra);//输出结果为 我今年13
字符串的修改:
由于String类中并没有提供修改字符串中某个字符的方法,所以想要修改字符串,只可以使用方法进行拼接替换
//字符串的修改 字符串是不可修改的,所以想要修改字符串只有替换字符串,使用substring方法提取字符串
String h ="hello";
String New=h.substring(0,3)+"p!";
System.out.println(New);//输出结构为help!
字符串的输出
//直接输出字符串
System.out.println("This is my name : 昭浅");//
//如果想将多个字符串输出,并且使用一个界定符号分开,可以使用静态join方法
String all=String.join("/","s","m","l"); //输出结果为 s/m/l
//重复输出字符串的方法repeat
String repeat="java".repeat(3); //将java重复输3遍
System.out.println(repeat);//输出结果为 javajavajava
检测字符串是否相等
// 用equals方法检测字符串是否相等 相同为true 不相同为false
String a="me";
String d="your";
System.out.println(a.equals(d)); //直接使用连个变量相比较
System.out.println("hello".equals(a)); //用字符串与变量相比较
System.out.println(a.equalsIgnoreCase(d)); //两个变量间不分大小写的比较
/**!!! 不可以用==来比较字符串*/
空串与Null串
NULL:代表声明了一个空对象,不是一个字符串,可以赋给任何对象。 空字符:代表声明了一个对象实例,这个对象实例的值是一个长度为0的空字符串
String a="";//a为空串,长度为0,内容为空的字符串
String b=null;//声明一个空对象
五、进制
1.二进制
二进制的整数有如下三种形式
原码:直接将一个数值换成二进制数,最高位是符号位
负数的反码:是对原码按位取反,只是最高位(符号位)确定为1
负数的补码:其反码加1
计算机底层的存储方式:所有的数字在计算机底层都以二进制形式存在。
二进制数据的存储方式:所有的数值,无论正负,底层都以补码的方式存储。
用14 的二进制数来举例说明
这是14的二进制形式:
0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 |
---|---|---|---|---|---|---|---|
最高位为符号位,0:正数,1:负数 | |||||||
这是-14的原码: |
1 | 0 | 0 | 0 | 1 | 1 | 1 | 0 |
---|---|---|---|---|---|---|---|
这是-14的反码: | |||||||
1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 |
– | – | – | – | – | – | – | – |
这是-14的补码 负数的补码:其反码加1 | |||||||
1 | 1 | 1 | 1 | 0 | 0 | 1 | 0 |
– | – | – | – | – | – | – | – |
在计算机的底层都以补码的方式来存储数据(无论正负)!
正数的原码,反码,补码是相同的,所以正数可以直接当做数据存储,而负数的补码需要通过负数的原码推至反码,再到补码进行存储(原码→反码→反码)
以此,我们反向思考一下,给了一个二进制的补码,我们应该怎么算出它的原码呢
以下随机用一个二进制数补码来推出原码
这是一个随机二进制数的补码
由此补码减1,我们可得到这个随机补码的反码
再进行除符号位外,取反,我们可得到这个随机补码的原码
最后我们得到这个随机二进制补码的原码:
1符号位 | 1 | 0 | 0 | 1 | 0 | 0 | 1 |
---|---|---|---|---|---|---|---|
这个二进制数的结果为-73 |
2.二进制转换十进制
举例说明,随机写一个二进制数
转换计算过程为
最终得出结果为14
3.十进制转换二进制
举例说明,随机写一个十进制数 17
将十进制数除二,最后除尽(结果为1)为止,如上图,最后结果取逆,得出结果:17的二进制数为10001
4.二进制与八进制,十六进制之间的转换
在本图中由二进制转换为八进制,可以理解为八是二的三次幂,在所以在本图中可以把三位数当做一个二进制数来处理,推算出从低到高(从右至左),得出三个数字1,5,3
,所以该二进制数转换为八进制数为 0 3 5 1
同理,由二进制转换为十六进制,十六是二的四次幂,在本图中,可把四位数当做一个二进制数处理,最后得到结果分别是 9,E(二进制1 1 1 1是十进制的15,也就是16进制的F)
5.进制之间的转换
以上,我们讲解了二进制到十进制,八进制,十六进制之间的转换,所以,想要四种进制之间的互相转换只需要中间的纽带(二进制)就可以
八进制→二进制:举例八进制数 0357
所以可得该八进制转换为二进制为 011101111
十六进制→ 二进制:举例十六进制数 0x3AF
所以可得该十六进制数转换为二进制为 001110101111
到这里,我们学完了二进制到所有进制之间的转换,也学完了所有进制到二进制之间的转换,通过二进制为纽带,我们可以做到所有进制之间的相互转换。
六.关键字与保留字
1.关键字
定义:被Java语言赋予了特殊含义,用作专门用途的字符串(单词)
特点:关键字中所有的字母都为小写
2.保留字
定义:现有java版本尚未使用,但以后版本可能会作为关键字使用,自己命名标识符时要避免使用这些保留字
具体保留字: goto、const
3.标识符
定义:Java对各种变量、方法和类等要素命名时使用的字符序列成为标识符
技巧:凡是自己可以起名的地方都叫标识符
涉及到的结构:类名、接口名、变量名、方法名、常量名
定义合法标识符规则:必须要遵守,否则编译不通过
(1)由26个英文字母大小写,0-9,_或$组成
(2)数字不可以开头
(3)不可以使用关键字和保留字,但可以包括关键字和保留字
(4)Java中严格区分大小写,长度无限制
(5)标识符不能包含空格
Java中的名称命名规范:
(1)包名:多单词组成时所有字母都小写:xxxyyyzzz
(2)类名、接口名:多单词组成是,所有单词的首字母大写:XxxYyyZzz
(3)变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写:xxxYyyZzz
(4)常量名:所有字母都大写,多单词时每个单词用下划线链接:XXX_YYY_ZZZ
注意:在起名字时,为了提高阅读性,要尽量有意义,见名知意
Java采用unicode字符集,因此标识符也可以使用汉字声明,但是不建议使用
七.输入与输出
如何从键盘中获取不同类型的变量,需要使用Scanner类
具体实现步骤:
1.导包:import java.util.Scanner;
2.Scanner的实例化:Scanner scan = new Scanner(System.in);
3.调用Scanner类的相关方法,来获取指定类型的变量
注意:需要根据相应的方法,来输入指定类型的值,如果输入的数据类型与要求的类型不匹配时,会报异常:InoutMisMatchException,,导致程序终止
import java.util.Scanner;
class ScannerTest{
public static void main(String[] args){
Scanner scan = new Scanner(System.in); //声明一个输入对象scan
int num = scan.nextInt(); //调用nextInt()方法,获取键盘中输入的int类型值
System.out.println(num); //输出获取的值
}
}
!!! 在Sacnner类中 没有提供获取char类型的获取方法,只能获取一个字符串
System.out.println("请输入你的姓名:");
String name = scan.next(); //输入字符型
System.out.println(name);
System.out.println("请输入你的年龄:");
int age = scan.nextInt(); //输入int类型
System.out.println(age);
System.out.println("请输入你的体重:");
double weight = scan.nextDouble(); //输入浮点型
System.out.println(weight);
System.out.println("你是否是男生?(true/false)");
boolean sex = scan.nextBoolean();
System.out.println(sex); //输入布尔型
八.程序流程控制
流程控制语句是用来控制程序中语句执行顺序的语句,可以把语句组合成能完成一定功能的小逻辑模块
其流程控制方式采用结构程序设计中规定的三种基本流程结构,即
顺序结构 :程序从上到下逐行的执行,中间没有任何判断和跳转
分支结构:根据条件,选择性的执行某段代码
循环结构:根据循环条件,重复性的执行某段代码
分支结构
1.if-else 结构
if语句的三种格式:
if(x>2)
if(x<10)
System.out.print(true);
else System.out.print(false); //注意,就近原则,与if(x<10)配对
当if else 结构中不加{}时,注意上图,else会遵循就近原则,他会与if(x<10)配对,而不是if(x>2),小心翻车
2.switch-case结构
switch(表达式){
case 常量1 :
语句1;
break;
case 常量2 :
语句2;
break;
case 常量3 :
语句3;
break;
case 常量 4:
语句4;
break;
default: //defalut语句:当以上case语句都不与表达式相匹配时,就会执行default语句
语句;
break;
}
switch语句将从与表达式值相匹配的case标签开始执行,直到遇到break语句,或者执行到switch语句的结束处为止,如果没有相匹配的case标签,而有default语句,就执行这个语句
警告:当case语句中没有break语句时,可能会触发多个case语句,在遇到与表达式相同的case语句,程序会从这条case语句执行到以下所有的case语句,这种情况常常会引发错误,如下
switch(num) {
case 1 :
System.out.println("这是1");
case 2 :
System.out.println("这是2");
case 3 :
System.out.println("这是3");
case 4 :
System.out.println("这是4");
case 5 :
System.out.println("这是5");
}
//不加break,如类似以上代码,会在遇到相匹配的case语句时,一直执行到代码结束
//输出结果为 这是3
//这是4
//这是5
//!!!但break不是必加的
case标签可以是:
① char , int , byte , short常量表达式
② 枚举类型
③ 字符串常量
case标签必须是常量表达式,是不可写范围的(例如(x > 2))
1.凡是可以使用switch-case的结构,都可以转换为if-else,反之,不成立。
2.我们写分支结构时,当发现既可以使用switch-case(同时,switch中表达式的取值情况不太多), 又可以是有if-else时 ,我们优先选择使用switch-case,原因 :switch-case执行效率高
循环结构
1.for循环结构
for循环的4个要素
一、循环结构的4个要素
1.初始化条件
2.循环条件 -->是boolean类型
3.循环体
4.迭代条件
二、for循环的结构
for(1;2;4){
3
}
执行过程 1->2->3->4->2->3->4-.....
int num = 1;
for(System.out.print('a');num <=3;System.out.print('c'),num++){
//迭代条件为两个System.out.print('c')和num++,中间用,(逗号)隔开
System.out.print('b');
}
//输出结果为abcbcbc,由此看出执行过程
2.while循环结构
while循环的4个要素
一、循环结构的4个要素
1.初始化条件
2.循环条件 -->是boolean类型
3.循环体
4.迭代条件
二、while循环的结构:
1;
while(2){
3;
4;
}
执行过程: 1->2->3->4->2->3->4-.....
注意:写while循环千万小心不要丢掉迭代条件,否则会造成死循环,循环算法要有有限性
for循环和while循环可以相互转换,不同点:for循环和while循环初始化条件的作用域不一样
3.do-while循环结构
一、循环结构的4个要素
1.初始化条件
2.循环条件 -->是boolean类型
3.循环体
4.迭代条件
二、do-while循环结构
1;
do{
3;
4;
}
while(2);
执行过程: 1-->3-->4-->2-->3-->4-->.....
注意:do-while至少会执行一次循环体
说明: 1.不在循环条件部分限制次数的结构:for(;;)或while(true) 2.结束循环有几种方式? 方式一:循环条件部分返回false 方式二:在循环体中,执行break
4.嵌套循环
1.嵌套循环: 抢一个循环结构A声明在另一个循环结构B的循环体中,就构成了嵌套循环
2.
外层循环:循环结构B
内层循环:循环结构A
说明:
①内层循环结构遍历一遍,只相当于外层循环循环体执行了一次
②假设外层循环需要执行m次,内层循环需要执行n次,此时内层循环的循 环体一共执行了m*n次
③外层控制行数,内层控制列数
经典练习题: 输出九九乘法表
for(int i=1;i<=9;i++) {
for(int j=1;j<=i;j++) {
System.out.print(i+"*"+j+"="+i*j+" ");;
}
System.out.println();
}
九、数组
数组的概述
1、数组的理解
数组(Array),是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理
2、数组相关的概念
>数组名
>元素
>索引(角标)
>数组的长度:元素的个数
3、数组的特点
①数组是有序排列的
②数组属于引用数据类型的变量,数组的元素既可以是基本数据类型,也可以是引用数据类型
③常见数组对象会在内存 开辟一整块连续的空间
④数组的长度一旦确定,就不能改变
4、数组的分类
①按照维数:一维数组、二维数组、…
②按照数组元素的类型,基本数据类型元素的数组、引用数据类型元素的数组
一维数组的使用
1、一维数组的声明和初始化
//声明并定义一个一维数组,数组的长度为三
//
//静态初始化:数组的初始化和数组元素的赋值操作同时进行
int[] array= new int[] {1,2,3};
//动态初始化:数组的初始化和数组元素的赋值操作分开进行
int[] array = new int[3];
// 定义数组的不同方式
int array[] = new int[3];
总结:数组一但定义完成,其长度就确定了
2、数组元素的调用
//定义数组names并为其赋值 数组名[索引]=赋值
String[] names = new String[4];
names[0]="decade";
names[1]="kuga";
names[2]="kubuto";
names[3]="w";
System.out.println(name[1]);//调用并输出一维数组names中的元素
3、如何遍历数组元素
System.out.println(names.length);//获取数组names的长度
//遍历数组names并输出数组元素
for(int i=0;i<names.length;i++) {
System.out.println(names[i]);
4、数组元素的默认初始化值
>数组元素是整形:0
>数组元素是浮点型:0.0
>数组元素是char型:0或‘\u0000’,而非‘0’
>数组元素是boolean型:false
>数组元素是引用数据类型:null
多维数组的使用
1、二维数组的声明和初始化
对于二维数组的理解,我们可以看成是一维数组array1又作为另一个一维数组array2的元素存在,其实从数组底层的运行机制来看,没有多维数组
//声明并定义一个二维数组
//静态初始化
int[][] arr = new int[][]{{1,2,3},{4,5},{6,7,8}};
//动态初始化1
int[][] arr1 = new int[3][4];
//动态初始化2
int[][] arr2 = new int[3][];//声明一个行长度为3的二维数组
arr2[0] = new int[4];//为二维数组arr2的第0行定义列数为4
2、二维数组元素的调用
int[][] arr1=new int[][]{{1,2,3},{4,5},{6,7,8}};
int[][] arr2 = new int[3][4];
System.out.println(arr1[0][1]);//2 调用并输出二维数组的元素
System.out.println(arr2[0][1]);// 0、null 未负值的数组在使用时的值为0或是null 看数组的类型而定
3、如何遍历二维数组元素
int[][] arr1=new int[][]{{1,2,3},{4,5},{6,7,8}};
System.out.println(arr1.length);//获取二维数组行的长度
System.out.println(arr1[0].length);//获取二维数组第0行的列数
System.out.println(arr1[1].length);//获取二维数组第1行的列数
for(int i=0;i<arr1.length;i++){
for(int j=0;j<arr1[i].length;j++){
System.out.println(arr1[i][j]);
}
}
4、二维数组的使用
//规定:二维数组分为外层数组的元素,内层数组的元素
int[] arr = new int[3][4];
//外层元素 : arr[0],arr[1]等
//内层元素 : arr[0][0],arr[1][2]等
System.out.println(arr[0]); //[I@59906517 输出的结果为第0个外层元素中的值,
//外层元素中的值为内层元素的地址
System.out.println(arr[0][0]);//0 输出的结果为0
//是第0个外层元素中第0个内层元素的值
System.out.println(arr); //[[I@5bfbf16f 输出的结果为外层元素的地址
//方括号([)的个数代表存储的是几维数组 I为存储数组的数组类型
//特别注意!!
int[][] arr1 = new int[3][];
System.out.println(arr1[1]);//null 输出结果为null
//声明arr1数组时并未声明内层数组的长度,所以并未为其分配空间,
//所以外层数组中存的值为null,表示空的地址值
System.out.println(arr1[1][1]);//报错,并未为声明内存元素,空指针异常
5、二维数组元素的默认初始化值
针对初始化方式一:例如 int[] arr = new int[3][4];
外层元素的初始化值为:地址值
内层元素的初始化值为:与一维数组初始化情况相同
针对初始化方式二:例如 int[][] arr1 = new int[3][];
外层元素的初始化值为:null
内层元素的初始化值为:不能调用,否则报错
数组中涉及的常见算法
1、数组元素的赋值(杨辉三角、回型数)
杨辉三角
//杨辉三角
public class ArrayTest2 {
public static void main(String[] args) {
int[][] arr= new int[10][];//定义一个外层数组为10
for(int i=0;i<arr.length;i++) {
arr[i]=new int[i+1];
for(int k=1;k<=arr.length-i-1;k++) {
System.out.print(" ");
}
for(int j=0;j<=i;j++){
if(j==0 || j==i) {
arr[i][j]=1;
}
else {
arr[i][j]=arr[i-1][j-1]+arr[i-1][j];
}
System.out.print(arr[i][j]+" ");
}
System.out.println();
}
}
}
回型数
package Question;
import java.util.Scanner;
//回型数
public class Array1 {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int num=scan.nextInt();
int[][] arr = new int[num][num];
int i=0,j=0,sum=1;
int min=0,max=num-1;
while(i<arr.length && j<arr[i].length) {
if(i==0 && j==0) {
arr[i][j]=sum;
if(num==1) {
arr[i][j]=sum;
break;
}
}
sum++;
if(j==max) {
if(i!=max) {
i++;
}
}else {
if(i==min) {
j++;
}
}
if(i==max) {
arr[i][j]=sum;
if(j==min) {
i--;
}
if(j>min)
j--;
}else {
arr[i][j]=sum;
if(j==min && i>min) {
i--;
}
}
if(i==min+1 && j==min) {
sum++;
arr[i][j]=sum;
min++;
max--;
}
if(sum==num*num) {
break;
}
}
for(int m=0;m<arr.length;m++) {
for(int n=0;n<arr[m].length;n++) {
System.out.print(arr[m][n]+"\t");
}
System.out.println();
}
}
}
2、求数值型数组中元素的最大值、最小值、平均数、总和等
import java.util.Random;
public class ArrayTest3 {
public static void main(String[] args) {
Random random = new Random();
int[] arr = new int[10];
for(int i=0;i<arr.length;i++) {
arr[i]=random.nextInt(90)+10;
System.out.print(arr[i]+" ");
}
System.out.println();
int max=0,min=100,sum=0;
for(int i=0;i<arr.length;i++) {
if(max<arr[i]) {
max=arr[i];
}
if(min>arr[i]) {
min=arr[i];
}
sum+=arr[i];
}
System.out.println("最大值:"+max);
System.out.println("最小值:"+min);
System.out.println("平均值:"+sum/arr.length);
System.out.println("总和:"+sum);
}
}
3、数组的复制、反转、查找(线性查找、二分法查找)
使用简单数组 数组复制
①创建一个名为ArrayTest的类,在main()方法中声明array1和array2两个变量,他们是int[]类型的数据
②使用大括号{}把array1初始化为8个素数:2,3,5,7,11,13,17,19
③显示array1的内容
④赋值array2变量等于array1,修改array2中的偶索引元素,使其等于索引值,如(array[0]=0,array[2]=2)打印出array1
思考: array1和array2是什么关系?
答:array1和array2地址值相同,都指向堆空间的唯一的一个数组实体
拓展:怎么实现array2对array1的复制
array2=new int[array1.length];
for(int i=0;i<array2.length;i++){
array2[i]=array1[i];
}
int[] array1,array2;//①
array1=new int[] {2,3,5,7,13,17,19};//②
for(int i=0;i<array1.length;i++) {//③
System.out.print(array1[i]+" ");
}
System.out.println();
array2=array1;//④ 此时赋值是将array1的地址赋值为array2,并没有将array1的值复制。所以操作array2也会改变array1,不能叫做数组的复制
for(int i=0;i<array2.length;i++) {
if(i%2==0) {
array2[i]=i;
}
System.out.print(array2[i]+" ");
}
System.out.println();
for(int i=0;i<array1.length;i++) {//③
System.out.print(array1[i]+" ");
}
System.out.println();
数组的反转
//数组的练习
//数组的反转练习
public class Array2 {
public static void main(String[] args) {
//数组的反转
String[] arr=new String[] {"a","b","c","d","e","f"};
String temp="";
int length=arr.length-1;
for(int i=0;i<arr.length;i++) {
temp=arr[i];
arr[i]=arr[length];
arr[length]=temp;
length--;
if(i==length || i>length) {
break;
}
}
for(int i=0;i<arr.length;i++) {
System.out.println(arr[i]);
}
//优化
for(int i=0;i<arr.length/2;i++) {
String temp1=arr[i];
arr[i]=arr[arr.length-1-i];
arr[arr.length-1-i]=temp1;
}
for(int i=0;i<arr.length;i++) {
System.out.println(arr[i]);
}
}
}
查找(线性查找、二分法查找)
遍历查找(线性查找)
String[] arr=new String[]{"a","b","c","d","e"};
String find="c";
boolean isFlag=false;
for(int i=0;i<arr.length;i++){
if(find.equals(arr[i])){
System.out.println("找到了,位置在"+i);
isFlag=true
}
}
if(isFlag){
System.out.println("没有找到");
}
二分法查找:(注意二分法查找,所查找的数组必须是有序!!!)
int[] arr1=new int[] {1,2,3,4,5,6,7};
int find1=2;
int head=0;
int end=arr1.length-1;
int middle=0;
boolean Flag=true;
while(head<=end) {
middle=(head+end)/2;
if(find1==arr1[middle]) {
System.out.println("找到了,位置在"+middle);
Flag=false;
break;
}else if(find1>arr1[middle]) {
head=middle+1;
}else {
end=middle-1;
}
}
if(Flag) {
System.out.println("没有找到");
}
4、数组元素的排序算法
十大排序算法:
选择排序:
▷ 直接选择排序、堆排序
交换排序:
▷ 冒泡排序、快速排序
插入排序:
▷ 直接插入排序、折半插入排序、Shell排序
归并排序
桶式排序
基数排序
冒泡排序:
public class BubbleTest {
public static void main(String[] args) {
int[] arr = new int[] {10,30,20,40,70,60,50,80};
for(int i=0;i<arr.length;i++) {
for(int j=0;j<arr.length-i-1;j++) {
if(arr[j]>arr[j+1]) {
int temp=arr[j+1];
arr[j+1]=arr[j];
arr[j]=temp;
}
}
}
for(int i=0;i<arr.length;i++) {
System.out.println(arr[i]);
}
}
}
快速排序:(类似于二分法查找的排序方法,将数组对半分进行排序)
Arrays工具类的使用
1 | boolean equals(int[] a,int[] b) | 判断两个数组是否相等 |
---|---|---|
2 | String toString(int[] a) | 输出数组信息 |
3 | void fill(int[] a , int vel) | 将指定值填充到数组之中 |
4 | void sort(int[] a) | 对数组进行排序 |
5 | int binarySearch(int[] a,int key) | 对排序后的数组进行二分法查找指定的值 |
int[] arr1=new int[] {1,3,5,6,3,6};
int[] arr2=new int[] {3,21,4,5,24,3};
//boolean equals.(int[] a,int[] b) 判断两个数组是否相等 返回结果为布尔型
boolean equal=Arrays.equals(arr1, arr2);
System.out.println(equal);
//String toString(int[] a) 输出数组信息
System.out.println(Arrays.toString(arr1));
//void fill(int[] a,int val) 将指定值val填充到数组中
Arrays.fill(arr1,10);
System.out.println(Arrays.toString(arr1));
//void sort(int[] a) 对数组进行排序
Arrays.sort(arr2);
System.out.println(Arrays.toString(arr2));
//int binarySearch(int[] a, int key)对排序后的数组进行二分法查找指定的值
//如果未找到指定的值,返回值为负数
int index=Arrays.binarySearch(arr2, 4);
System.out.println(index);
数组种常见的异常
1、数组角标越界的异常(ArrayIndexOutOfBoundsExcetion)
角标(索引)超出数组索引的范围导致的异常
int[] arr = new int[] {1,2,3,4,5,6};
System.out.println(arr[8]);
2、空指针异常(NullPointerException )
由于数组为空导致的指针找不到地址,指针为空的异常
情况一
int[] arr = new int[] {1,2,3,4,5,6,7};
arr=null;//将数组定义为空,导致没有指针,出现异常
System.out.println(arr[1]);
情况二
int[][] arr = new int[3][];//外层数组中存储的内层数组的地址为空,导致指针为空异常
System.out.println(arr[1]);//输出null 外层数组中存储的内层数组的地址为空
System.out.println(arr[1][1]);//外层数组中存储的内层数组的地址为空,导致指针为空异常
情况三
int[] arr = new int[]{1,2,4,5,6};
arr[1]=null;
System.out.println(arr[1].toString());//由于指针找不到数组中的索引1的元素,指针为空,null调用方法会显示空指针异常
第四章、面向对象(上)
面向对象的含义
🔸面向过程与面向对象:
面向过程: 强调的是功能行为,以函数为最小单位,考虑怎么做
面向对象: 强调具备了功能的对象,以类/对象为最小单位,考虑谁来做
🔹举例:人把大象装进冰箱
▷面向过程:强调的是功能行为,以函数为最小单位,考虑怎么做
人把大象装进冰箱:
①把冰箱打开
②抬起大象,塞进冰箱
③把冰箱门关闭
▷面向对象:强调具备了功能的对象,以类/对象为最小单位,考虑谁来做
人{
打开(冰箱){
冰箱.打开();
}
抬起(大象){
大象.进入(冰箱);
}
关闭(冰箱){
冰箱.闭合();
}
}
冰箱{
开开(){}
闭合(){}
}
大象{
进入(){}
}
程序员从面向过程的执行者转化成了面向对象的指挥者
我要开车去丽江:我、车、丽江、可以看做类(名词),开车、去可以看做方法(动词)
一、Java类及类的成员
(属性、方法、构造器、代码块、内部类)
类: 对一类事物的描述,是抽象的、概念上的定义
对象: 是实际存在的该类事物的每个个体,因而也被称为实例
举例:
家电(类)
电视(对象) 电脑(对象) 空调(对象)
▷面向对象程序设计的重点是类的设计
▷设计类:就是设计类的成员
设计类:就是设计类的成员
类及对象的使用
属性:对应类中的成员变量
行为:对应类中的成员方法
属性=成员变量=Filed=域、字段
方法=成员方法=函数=method
创建类的对象= 类的实例化 = 实例化类
public class PersonTest{
public static void main(String[] args){
Person p1 = new Person();//创建Person类的对象
//调用对象的结构:属性、方法
//调用属性:对象.属性;
p1.name="Tom";
p1.isMale=true;
System.out..print(p1.name);//输出 Tom
//调用方法
p1.eat();
p1.sleep();
p1.talk("汉语");
}
}
class Person{
//属性
String name;
int age;
boolean isMale;//判断是否为男性
//方法
public void eat(){
Sysout.out.print("人可以吃饭");
}
public void sleep(){
Sysout.out.println("人可以睡觉");
}
public void talk(String language){
Sysout..out..println("人可以说"+language);
}
}
类和对象的使用(面向对象思想落地的实现)
1.创建类
2.创建类的对象
3.通过“对象.属性”或“对象.方法”调用对象的结构
注意:
如果创建了一个类的多个对象,则每个对象都独立拥有一套类的属性。(非static的)意味着如果我们修改一个对象的属性值,则不影响其他对象的属性值
对以上类及对象的创建
新建对象时
Person p2 = new Person();
System.out.println("p2.name");//输出null
System.out.println("p2.isMale");//输出false
//将p1变量保存的对象地址值赋给p3,导致p1和p3指向堆空间中的同一个对象实体(与数组类似)
Person p3 =p1;
p3.name;//输出Tom
p3.age=10;
Sysout.out.println(p1.age); // 输出10 p1和p3共用同一属性值
类中属性的使用
属性(成员变量) vs 局部变量
1.相同点:
①定义变量的格式:数据类型 变量名 = 变量值
②先声明,后使用
③变量都有其对应的作用域
2.不同点:
①在类中声明的位置不同:
属性:直接定义在类的一对{}中
局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量
②关于权限修饰符的不同
属性:可以再声明属性时,指明其权限,使用权限修饰符
常用的权限修饰符:private、public、缺省(默认)、protected -->封装性
局部变量:不可以使用权限修饰符
③默认初始化的情况:
属性:类的属性,根据其类型,都有默认初始值
整型(byte、short、int、long):0
浮点型(float、double):0.0
字符型(char):0(或'\u0000')
布尔型(boolean):false
引用数据类型(类、数组、接口、):null
局部变量:没有默认初始值
意味着,我们在调用局部变量之前,一定要显式赋值
特别的,形参在调用时,我们赋值即可
④在内存中加载的位置:
属性:加载到堆空间中(非static)
局部变量:加载到栈空间中
public class UserTest{
public static void main(String[] args){
User u1 = new User();
System.out.print(u1.name);//null
Sysout.out.print(u1.age);//0
System.out.print(u1.isMale);//false
u1.talk("汉语");//调用方法时给形参赋值
}
}
class User{
//属性(成员变量)
String name;
int age;
boolean isMalel;
public void talk(String language){ // language:方法形参
Sysout.out.println("我们使用"+language+"进行交流");
}
public void eat(){
String food = "烙饼"; //局部变量
System.out.println("北方人喜欢吃:"+food);
}
}
类中方法的使用
类中方法的声明和使用
方法:描述类应该具有的功能
比如:Math类:sqrt()、random()....
Scanner类:nextXxx()...
Arrays类:sort()排序、binarySearch()二分法查找、toString()、equals()...
举例:
public void eat(){}//无形参方法 void:表示没有返回值
public void sleep(int hour){}
public String getName(){}
public String getNation(String nation){}//有形参方法 有返回值 用return返回
方法的声明:权限修饰符 返回值类型 方法名(形参列表(可多个形参)){
方法体
}
说明:
1、关于权限修饰符,默认方法的权限修饰符先都是用public
Java规定的4种权限修饰符:private、public、缺省(默认)、projrcted
-->封装性中讲明
2、返回值类型:有返回值 vs 无返回值
①如果方法有返回值,则必须在方法声明时,指定返回值的类型,同时
方法中需要使用return关键字来返回指定类型的变量或常量
②如果方法没有返回值,则方法声明时,使用void来表示,通常,没有
返回值的方法中,就不使用return,但是如果使用的话,只能return;来表示结束
此方法的意思,
3、方法名:属于标识符,尊徐标识符的规则和规范,见名知意
4、形参列表:方法可以声明0个,1个,或多个形参,参数之间用逗号隔开
格式:数据类型1 形参1,数据类型2,形参2...
return关键字的使用:
1、使用范围:使用在方法体中
2、作用:①结束方法
②针对于有返回值类型的方法,使用"return 数据" 方法返回所要的数据
3、注意点:return关键字后面不可以声明执行语句
关于方法的使用,可以在方法中当前类的属性和方法,特殊的,方法A可以调用方法A:递归方法
但是,方法中不可以定义方法
public class CustomerTest{
}
//客户类
class Customer{
//属性
String name;
int age;
boolean isMale;
//方法
public void eat(){
System.out.println("客户吃饭");
}
public void sleep(int hour){
System.out.println("休息了"+hour+"个小时");
}
public String getName(){
return name;
}
public String getNation(String nation){
String info = = "我的国籍是"+nation;
return info;
}
}
完成一个项目的思路:
根据问题需要,选择问题所针对的显示世界中的实体
从实体中寻找解决问题相关的属性和功能,这些属性和功能就形成了概念世界中的类
把抽象的实体用计算机语言进行描述,形成计算机世界中类的定义,即借助某种程序语言,
把类构造成计算机能够识别和处理的数据结构
将类实例化成计算机世界中的对象,对象是计算机世界中解决问题的工具
JVM内存结构
编译完源程序以后,生成一个或多个字节码文件
我们使用JVM中的类的加载器和解释器对生成的字节码文件进行解释运行,
意味着,需要将字节码文件对应的类加载到内存中,涉及到内存解析
匿名对象的使用
package Object;
//匿名对象的使用
//理解:我们创建的对象,没有显式的赋给一个变量名,即为匿名对象
//特征:匿名对象只能调用一次,再次调用就是另外的对象了
public class Anonymous {
public static void main(String[] args) {
Phone p=new Phone();//创建Phone对象p
// p=null;
System.out.println(p);
p.sendEmail();
p.playGame();
//匿名
new Phone().sendEmail();//匿名使用方法Phone类中的方法
new Phone().playGame();
//两次匿名中调用的并不是同一对象
new Phone().price=1999;
new Phone().showPrice();//输出0.0
//匿名对象在开发中的使用
PhoneMall mall = new PhoneMall();
mall.show(new Phone());
//在调用方法时,需要传递一个Phone对象变量,
//此时使用匿名对象,即可直接调用方法,无需建立实例对象
}
}
class PhoneMall{//创建类
public void show(Phone phone) {//创建show方法,需要提供一个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);
}
}
方法的深入使用
方法的重载(overload)
1.定义:在同一个类中,允许存在一个以上的同名方法,只要他们的参数个数或参数类型不同即可
"两同一不同":同一个类,相同方法名
参数列表不同,参数个数不同,参数类型不同
判断是否为方法重载:
跟方法的权限修饰符、返回值类型(有无返回值)、形参变量名、方法体都没有关系
在通过对象调用方法时,如何确定某一个指定的方法:
方法名--->参数列表
以下情况构成重载:
public class OverLoadTest{
public void getSum(String i,int j){
}
public void getSum(int j,String i){//参数列表不同
}
public void getSum(int i){//参数个数不同
}
public void getSum(double i,double j){//参数类型不同
}
}
2.举例:Arrays类中重载的sort() / binarySearch()等方法
可变个数形参的方法
package Method;
//可变参数形参个数的方法:
// 1.jdk 5.0的新增内容
// 2.具体使用:
// 2.1 可变个属性采纳的格式:数据类型 ... 变量名
// 2.2 当调用可变个数形参的方法时,传入的参数个数可以是:0个,1个,2个...
// 2.3 可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载
// 2.4 可变个数形参的方法与本类中方法名相同,形参类型也相同的数组之间不构成重载,换句话说,二者不能共存
// 2.5 可变个数形参在方法的形参中,必须声明在末尾
// 2.6 可变个数形参在方法的形参中,最多只能声明一个可变形参
// 2.7 如果出现同名的重载方法,method(确定个数形参)和method(可变个数形参),则会优先调用确定了形参个数的方法
// 2.8 可变个数形参的方法在子类或父类中遇到同名且数据类型相同的形参数组时,会构成重载
public class VariableArgs {
public static void main(String[] args) {
VariableArgs test = new VariableArgs();
test.show(0);
test.show("A");
test.show("a","b","c");//调用可变个数形参方法
}
public void show(int i) {
System.out.println("整形");
}
public void show(String s) {
System.out.println("字符串");
}
public void show(String ... strs) {//可变个数形参的方法,与上列方法也构成重载
System.out.println("可变个数形参类型方法");
}
// public void show(String[] strs) {//用数组传入多个相同类型参数的方式,与可变个数形参的方法相同,不构成重载,会报错
//
// }
public void show(int i,String ... strs) {//可变形参要定义在其他形参后边
System.out.println("可变个数形参类型方法");
}
}
方法中值传递的机制
1.方法的形参的传递机制:值传递
形参:定义方法时,声明的小括号内的参数
实参:方法调用时,实际传递给形参的数据
2.值传递机制:
基本数据类型传递:数据值
引用数据类型传递:地址值(含变量的数据类型)
//引用数据类型传递值
Person p1 = new Person();
User u1 = p1;//报错 ,传递地址值时会含有数据类型,两者的数据类型不相同
3.规则:
▷如果参数是基本数据类型,此时实参赋给形参的是实参真实存储的数据值
▷如果参数是引用数据类型,此时实参赋给形参的是实参存储数据的地址值
▷java所有的参数传递都是原变量的副本,能否改变原变量的数据,要看该方法能否改变原变量的内存空间地址保存的数据
递归方法
1.递归方法:一个方法中调用它自身
2.方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无需循环控制,递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环
3.递归方法一般要向参数缩减的方向递归(个人想法)
package Method;
public class Recursion {
public static void main(String[] args) {
//例一:计算1-100的和
//方式一
int sum=0;
for(int i =1;i<=100;i++) {
sum+=i;
}
System.out.println(sum);
//方式二
Recursion test = new Recursion();
System.out.println( test.getSum(100));
//例二:已知有一个数列: f(0)=1,f(1)=4,f(n+2)=2*f(n+1)+f(n)
//其中n是大于0的整数,求f(0)的值
int value = test.f(10);
System.out.println(value);
}
public int getSum(int n) {
if(n==1) {
return 1;
}else {
return n+getSum(n-1);
}
}
public int f(int n) {
if(n==0) {
return 1;
}else if(n==1) {
return 4;
}else {
return 2*f(n-1)+f(n-2);
}
}
}
二、面向对象的三大特征
(封装性、继承性、多态性、抽象性*)
1.封装与隐藏(封装性)
我们程序设计追求"高内聚,低耦合"。
高内聚:类的内部数据操作细节自己完成,不允许外部干涉
低耦合:仅对外暴露少量的方法用于使用
封装性的体现
package Encapsulation;
封装性的体现
一.问题的引入
当我们创建一个类的对象以后,我们可以通过"对象.属性"的方式,对对象的属性进行赋值,这里,赋值操作要受
到属性的数据类型和存储范围的制约,除此之外,没有其他制约条件,但是,在实际问题中,我们往往需要给变量
属性赋值加入额外的限制条件,这个条件就不能在属性声明时体现,我们只能通过方法进行限制条件的添加(比如:setLegs())
同时,我们需要避免用户再使用"对象.属性"对属性进行赋值,则需要将属性声明为私有的(private)
-->此时,针对于属性就体现了封装性
二.封装性的体现
我们将类的属性xxx私有化(private),同时,提供公共的(public)方法来获取(getXxx)和设置(setXxx)此属性的值
拓展:封装性的的体现:如上、不对外暴露的私有的方法、单例模式
三.封装性的体现:需要权限修饰符来配合
1.java规定的4种权限(从小到大):private、缺省(default)、protected、public
2.4种权限可以用来修饰类及类的内部结构:属性、方法、构造器、内部类
3.具体的,4种权限都可以用修饰类的内部结构:属性、方法、构造器、内部类
修饰类的话,只能使用:缺省、public
4.总结:Java提供了4种权限修饰符来修饰类及类的内部结构。体现类及类的内部结构在被调用时的可见性的大小
public class AnimalTest {
private int i=1;//私有成员变量只能在本类中使用,在其他类中声明的对象并不能调用成员变量
public static void main(String[] args) {
AnimalTest t= new AnimalTest();
t.i=2;
Animal test = new Animal();
test.name="蜘蛛";
test.age=18;
test.setLegs(1);
test.showInfo();
}
}
class Animal{
String name;
int age;
//int legs;//腿的个数
private int legs;
public void setLegs(int l) {//此时,我们为legs的赋值加以限制,完成封装
if(l>=0 && l%2==0) {
legs=l;
}else {
legs=0;//如果不符合规定,则赋值为0或抛出异常
}
}
public void showInfo() {
System.out.println("name:"+name+" age:"+age+" legs:"+legs);
}
}
权限修饰符
封装性的体现:需要权限修饰符来配合
1.java规定的4种权限(从小到大):private、缺省(default)、protected、public
2.4种权限可以用来修饰类及类的内部结构:属性、方法、构造器、内部类
3.具体的,4种权限都可以用修饰类的内部结构:属性、方法、构造器、内部类
修饰类的话,只能使用:缺省、public
构造器的使用
package Object;
类的结构之三,构造器(或构造方法、constructor)的使用
一、构造器的作用
1.创建对象
2.初始化对象的属性
二、说明
1.如果没有显式的定义类的构造器的话,则系统默认提供一个空参的构造器
2.定义构造器的格式:权限修饰符 类型(形参列表){}
3.一个类中定义的多个构造器,彼此构成重载
4.一旦我们显式的定义了类的构造器之后,系统就不再提供默认的空参构造器
5.一个类中,至少会有一个构造器
public class Construct {
public static void main(String[] args) {
ConstructTest test = new ConstructTest();
test.eat();
ConstructTest test2 = new ConstructTest("tom");
System.out.println(test2.name);
}
}
class ConstructTest{
//属性
String name;
int age;
//构造器
public ConstructTest() {
System.out.println("构造器。。。");
}
public ConstructTest(String n) {//可写多个形参
name=n;
}
//方法
public void eat() {
System.out.println("人吃饭");
}
}
总结:属性赋值的先后顺序
①默认初始化
②显式初始化
③构造器初始化
④通过"对象.方法"或"对象.属性"的方式复制
以上操作的先后顺序 :1-2-3-4
JavaBean的使用
JavaBean是一种Java语言写成的可重用组件。
所谓JavaBean,是指符合如下标准的Java类
>类是公共的
>有一个无参的公共的构造器
>有属性,且有对应的get,set方法
拓展知识:UML类图
2.其他关键字
(this、super、static、final、abstract、interface、package 、import)
关键字—this
package Mark;
//this关键字的使用:
//1.this可以用来修饰:属性、方法、构造器
//
//2.this修饰属性和方法:
// this理解为:当前对象
//
// 在类的方法中,我们可以使用"this.属性"或"this.方法"的方式,调用当前对象的属性或方法,但是
// 通常情况下,我们都选择省略"this.",特殊情况下,如果方法的形参和类的属性同名时,我们必
// 须显示的使用"this.变量"的方式,表明此变量是属性,而非形参
//3.this修饰构造器:
//this理解为:当前正在创建的对象 的
//
//在类的构造器中,我们可以使用"this.属性"或"this.方法"的方式,调用当前正在创建的对象的属性或方法,但是
//通常情况下,我们都选择省略"this.",特殊情况下,如果方法的形参和类的属性同名时,我们必
//须显示的使用"this.变量"的方式,表明此变量是属性,而非形参
//4.this调用构造器
// ①我们在类的构造器中,可以显式的使用"this(形参列表)"方式,调用本类中的其他构造器
// ②构造器中不能通过"this(形参列表)"方式调用自己
// ③如果一个类中有n个构造器,则最多有n-1个构造器使用了"this.(形参列表)"
// ④规定:"this(形参列表)"必须声明在构造器的首行,否则报错
// ⑤构造器内部:最多只能声明一个"this(形参列表)"用来调用其他构造器
public class ThisTest {
public static void main(String[] args) {
Person p=new Person();
p.setAge(10);
p.setName("Tonm");
System.out.println(p.getName()+" "+p.getAge());
}
}
class Person{
//属性
private String name;
private int age;
//构造器
public Person() {
System.out.println("当我们声明一个构造器时,需要在构造器中执行n行代码");
System.out.println("当代码过多,则每个构造器都需要写大量代码,会发生冗余,重复多次");
}
public Person(int age) {
this();//我们使用"this(形参列表)"来调用指定构造器
this.age=age;
}
//方法
public void setName(String name) {
//在声明方法或构造器的时候,形参的定义要见名知意,有时会与
//属性名相同,所以,我们要使用this关键字
//this.代表这是该对象的属性或方法,而非形参
this.name=name;
}
public void setAge(int age) {
this.age=age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
关键字—package、import
package Keyword;//包
import java.util.Arrays;//导入包中指定的Arrays类
import Question.Customer;
import Question2.Bank;//导入自定义的包中的类
import static java.lang.Math.*;//导入Math类的静态结构,注意!!!,要写全
一.package关键字的使用
1.为了更好的实现项目中的类的管理,提供包的概念
2.使用package声明类或接口所属的包,声明在源文件的首行
3.包,属于标识符,命名要遵循标识符的命名规范(xxxyyyzzz),见名知意
4.每"."一次就代表一次文件目录。例如src.Face
5.补充:同一包下,不能命名同命的接口、类
不同的包下,可以命名同名的接口、类
二.import关键字的使用(作用:导入)
1.在源文件中显式的使用ipmort结构导入指定包下的的类、接口
2.声明在包的声明和类的声明之间
3.如果需要导入多个结构,,则并列写出即可
4.可以使用"xxx.*"的方式,表示可以导入xxx包下的所有结构
5.如果使用的类或接口是定义在java.lang包下定义的,则可以省略import结构
6.如果使用的类或接口是本包下定义的,则可以省略import结构
7.如果在源文件中,使用不同包中同名的类,则至少有一个类使用全类名的方式显式
8.使用"xxx.*"方式表示可以调用xxx包下的所有结构,但是如果使用的xxx子包下的结构,则还
是需要显式导入
9.import static:导入指定类或接口的静态结构
public class PackageImport {
public static void main(String[] args) {
String info = Arrays.toString(new int[] {1,2,3,4});
System.out.println(info);
Bank bank = new Bank();//导入自定义的包中的类
Customer c = new Customer("Tom","J");
//不同包下的同名类使用时不可全部使用import,则最少一个使用全类名的方式导入类
Question2.Customer c1 = new Question2.Customer("Tom","K");
//在导入静态结构之前,我们需要在使用Math类round方法的时候写全方法的类名
long num = Math.round(2.1);
//在导入静态结构之后,可以省略方法的类名,如下
long num1 = round(2.1);
}
}
3. 继承性
package Inherit;
/*
* 面向对象的特征二:继承性
*
* 一。继承性的好处:
* 1.减少了代码的冗余
* 2.便于功能的拓展
* 3。为之后的多态性,提供了前提
*
*
* 二。继承性的格式: class A extends b{}
* A:子类,派生类、subclass
* B:父类、基类、superclass
*
* 体现:一旦子类A继承父类B以后,子类A中就获取了父类B声明的所有的属性、方法
*
*
* 特别的,父类中声明私有的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构
* 只是因为封装性的影响,使得子类不能直接调用父类的私有结构,要使用get、set方法
* 子类继承父类后,还可以声明自己的属性和方法,实现功能的拓展
*
* 子类和父类的关系,不同于集合和子集的关系,子类的功能可以拓展
*
*
* extends:延展、拓展
*
* 三、Java中对于继承性的规定:
* 1.一个类可以被多个子类继承
* 2.Java类的单继承性:一个类只能有一个父类
* 3.子父类是相对的概念,多层基层
* 4.子类直接继承的父类,称为:直接父类,间接继承的父类称为:间接父类
* 5.子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法
*
* 四、1.如果我们没有显式的声明一个父类的话,则此类继承于java.lang.Object类
* 2.所有的java类(除java.lang.Object类之外)都直接或间接的继承于java.lang.Object类
* 3.意味着,所有的java类具有java.lang.Object类声明的功能
*
*
*
*/
public class Student extends Person {//extends 加类名
// String name;
// int age;
String major;
public static void main(String[] args) {
Student student = new Student();
student.name = "Tom";
System.out.println(student.name);
student.age = 13;
System.out.println(student.age);
student.eat();
student.sleep();
}
}
package Inherit;
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 sleep() {
System.out.println("睡觉");
}
}
4.方法的重写(override / overwrite)
package Inherit;
/*
* 方法的重写(override,overwrite)
*
* 1.重写:子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作
*
* 2.应用,重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时
* 实际执行的是子类重写父类的方法
*
* 3.重写的规定:
* 方法的声明:权限修饰符 返回值类型 方法名(形参列表){
* //方法体
* }
* 约定俗称:子类中的叫重写的方法,父类中的叫被重写的方法
* ① 子类重写的方法的方法名和参数列表与父类被重写的方法名和形参列表相同
* ② 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
* >特殊情况:子类不能重写父类中声明为private权限的方法
* ③返回值类型:
* >父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void
* >父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以使A类或是Ad的子类
* >父类被重写的方法的返回值类型是基本数据类型(比如:double),则子类重写的方法的返回值类型必须是相同的基本数据类型(必须也是double)
* ④异常方面:子类重写的方法,抛出的异常类型,不大于父类被重写的方法的异常类型,具体看异常处理
* **********************************************************************
*
* 子类和父类中的同名同参数的方法要么都声明为非static(才能重写),要么都声明为static 的(不是重写)
*
* 面试题:区分方法的重载和重写
*
* 注意:当子类中声明了一个与父类同名同类型的变量时,该变量不会重写(覆盖)父类的变量
*/
public class Child extends Person{
public static void main(String[] args) {
new Child().eat();//eat方法未重写之前,调用的Person类中eat方法
//输出吃饭
}
//方法的重写
public void eat() {//重写后调用新方法
System.out.println("儿童要吃有营养的东西");
}
public String info() {//String 是 Object类的子类,重写了info方法,
return "hello world";
}
//出去八种基本数据类型,剩下的都还是Object类的子类
}
package Inherit;
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 sleep() {
System.out.println("睡觉");
}
public Object info() {//声明info方法,返回值为Object类型
return null;
}
}
5.super关键字
package Keyword;
/*
* super关键字的使用
* 1.super理解为:父类的
* 2.super可以用来调用:属性、方法、构造器
*
* 3.super的使用
*
* 3.1 我们可以在子类的方法或构造器中,通过使用"super.方法"的方式,显式的调用
* 父类中声明的属性或方法,但是,通常情况下,我们习惯省略"super"
* 3.2 特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性
* 则必须显式的使用"super.属性"的方式,表明调用的是父类中声明的属性
* 3.3 特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,
* 则必须显式的使用"super.方法"的方式,表明调用的是父类中被重写的方法
*
* 4.super调用构造器
* 4.1 我们可以再子类的构造器中显示的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器
* 4.2 "super(形参列表)"的使用,必须声明在子类构造器的首行
* 4.3 我们在类的构造器中,针对于"this(形参列表)"或"super(形参列表)"只能二选一,不能同时出现
* 4.4 在构造器的首行,没有显式的声明"this(形参列表)"或"super(形参列表)",则默认调用的是父类中空参的构造器:super()
* 4.5 在类的多个构造器中,至少有一个类的构造器中使用了"super(形参列表)",调用父类的构造器
*
*
*
*/
public class SuperTest extends Super{
String major;
int id=1002;//学号
public SuperTest() {
}
public SuperTest(String name,int age,String major) {
super(name,age);
this.major=major;
}
public void eat() {
System.out.println("学生需要吃有营养的食物");
}
public void study() {
super.eat();//调用的是父类中的eat方法
}
public void show() {
System.out.println("id:"+id);//调用的是子类中自己定义的属性
System.out.println("id:"+super.id);//调用的是父类中的id
}
public void info() {
System.out.println("name:"+name+" age:"+age+" major:"+major);
}
public static void main(String[] args) {
SuperTest t= new SuperTest();
t.eat();//调用的是被重写后的方法
t.study();
t.show();
SuperTest t1= new SuperTest("Tom",13,"IT");
t1.info();
}
}
package Keyword;
public class Super {
String name;
int age;
int id=1001;//身份证号
public Super() {
}
public Super(String name,int age) {
this.name=name;
this.age=age;
}
public void eat() {
System.out.println("吃饭");
}
}
子类对象实例化的全过程
子类对象实例化的全过程
1.从结果上来看:(继承性)
子类继承父类以后,就获取了父类中声明的属性或方法
创建子类的对象,在堆空间中,就会加载所有父类中声明的属性
2.从过程上来看:
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的构造
器,知道调用了java.lang.Object类中空参的构造器为止,正因为加载过所有的父类的结构,所以才可以看到内
存中有父类的结构,子类对象可以考虑进行调用
明确:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象
6.多态性
package duotai;
/*
* 面向对象特征之三:多态性
*
* 1.理解多态性:可以理解为一个事物的多种形态
* 2.何为多态性:
* 对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
*
* 3.多态的使用:虚拟方法调用
* 有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期
* 我们实际执行的是子类重写父类的方法
* 总结:编译,看左边 运行 看右边
*
* 4.多态的使用前提: ①类的继承关系,②方法的重写
*
* 5.对象的多态性只适用于方法,不适用于属性(编译和运行都看左边)
*/
public class Student extends Person{
String major;
public void walk() {
System.out.println("学生坐公交上学");
}
public void eat() {
System.out.println("学生要吃的好一点");
}
public void study() {
System.out.println("学生要学习");
}
public static void main(String[] args) {
//多态性:父亲的引用指向子类的对象
Person p=new Student();
//多态性的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法---虚拟方法调用
p.eat();//调用的是子类中重写后的方法
p.walk();
p.s();
// p.study();//只能调用父类中自己定义的方法,当子类中存在同名同参数的方法时,才会调用重写后的方法
}
}
instanceof关键字的使用
package duotai;
public class PersonTest {
public static void main(String[] args) {
//多态性:父亲的引用指向子类的对象
Person p=new Student();
//多态性的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法---虚拟方法调用
p.eat();//调用的是子类中重写后的方法
p.walk();
p.s();
// p.study();//只能调用父类中自己定义的方法,当子类中存在同名同参数的方法时,才会调用重写后的方法
System.out.println("****************************");
Person p1 = new Student();
p1.name="Tom";
//不能调用子类所特有的方法、属性,p1是Person类型
// p1.study();
// p1.major;
//有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于
//变量声明了父类类型,导致编译时只能调用父类中声明的属性和方法,子类特有的属性
//和方法不能调用
//如何才能调用子类特有的属性和方法
Student stu = (Student)p1;
stu.major="";
stu.study();
// stu.name="tom";
//使用强转时,可能会出现classCastException的异常
// Teacher teach= (Teacher)p1;//错误 赋给p1的对象为new Student,,无法转换为Teacher
// teach.major="老师";//错误
//
/*
* instanceof关键字的使用,
*
*
* a instanceof A :判断对象a是否类A的实例,如果是,返回true,如果不是,返回false
*
* 使用情景:为了避免在向下转型时出现classCastException的异常,我们在向下转型之前,先
* 进行instanceof的判断,一旦返回true,就进行向下转型,如果返回false,不进行向下转型
*
* 如果 a instanceof A 返回true ,则 a instanceof B 也返回true
* 其中,类B是类A的父类
*/
if(p1 instanceof Teacher) {
Teacher teach1 = (Teacher)p1;
System.out.println("*******************");
}
}
}
多态性的练习
package duotai;
/*
* 练习:
* 1.若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法
* 系统将不有可能把父类的方法转移到子类中,编译看左边,运行看右边
*
* 2.对于实例变量则不存在这样的现象,即使子类定义了与父类完全相同的实例变量
* 这个实例变量依然不可能覆盖父类中定义的实例变量,编译运行都看左边
*/
public class FieldMethodTest {
public static void main(String[] args) {
Sub s=new Sub();
System.out.println(s.count);
s.display();
Base b = s;//多态性的使用
//== 对于引用数据类型来讲,比较的是两个引用数据变量的地址值是否相同
System.out.println(b==s);
System.out.println(b.count);//属性不适用多态性,还是Base类中的count
b.display();//调用了Sub类中重写的方法
}
}
class Sub extends Base{
int count = 20;
public void display() {
System.out.println(this.count);
}
}
class Base {
int count=10;
public void display() {
System.out.print(this.count);
}
}
7.Object类的使用
==和equals()的区别
package ObjectClass;
import java.util.Date;
import java.util.Objects;
/*
*
* 面试题: ==和equals()的区别
*
* 一、==的使用
* ==:运算符
* 1.可以使用在基本数据类型变量和引用数据类型变量中
* 2.如果比较的是基本数据类型变量,比较两个变量保存的数据是否相等(不一定要类型相同),boolean类型除外,无法比较
* 如果比较的是引用数据类型变量,比较两个对象的地址值是否相同,即两个引用是否指向同一个对象实体
*
* 二、equals()方法的使用
* 1.是一个方法,而非运算符
* 2.方法是由对象调用,所以基本数据类型不可以调用,因为不是对象,
* 3.equals()方法是适用于引用数据类型
* 4.Object类中equals()的定义
* public boolean equals(Object obj){
* return (this==obj);
* }
* 说明:Object类中定义的equals()方法和==的作用是相同的,比较两个对象的地址值是否相同,即两个引用是否指向
* 5.像String、Date、File、包装类等都重写了Object类中的equals()方法,重写以后,比较的不是
* 两个的引用的地址是否相同,而是比较两个对象的实体内容是否相同
*
* 6.通常情况下,我们自定义的类如果使用equals()方法的话,也通常是比较两个对象的“实体内容”是否相同,那么,
* 就需要对Object类中的equals()方法进行重写
* 重写的原则,比较两个对象的实体内容是否相同
*/
public class EqualsTest {
public static void main(String[] args) {
int i=10 ;
int j=10;
int k=65;
double d =10.0;
System.out.println(i==j);//true
System.out.println(i==d);//true
boolean b=true;
// System.out.println(i==b);//无法比较
char c1=10;
char c2 ='A';
char c3=65;
System.out.println(c3);
System.out.println(i==c1);//true
System.out.println(k==c2);//true
System.out.println(c1 == c2);//false
Test t1= new Test("tom",11);
Test t2= new Test("tom",11);
//==在比较引用数据类型时比较的是对象的地址值
System.out.println(t1==t2);//false
//String类型也是引用数据类型,==比较的是地址值
String str1=new String("tom");
String str2=new String("tom");
System.out.println(str1==str2);//false
System.out.println(str1);
System.out.println("***********************");
//equals()方法
//Test类的父类是Object类,Object类的equals方法比较的还是地址值。所以返回false
System.out.println(t1.equals(t2));//false
//equals方法在Test类中被重写后,返回的值是true
System.out.println("重写后的equals方法");
System.out.println(t1.equals(t2));//false-->true
//此时的equals方法在被调用时,被String类重写,所以比较的是引用数据对象的内容而不是地址
System.out.println(str1.equals(str2));//true
Date date1 = new Date(123213L);
Date date2 = new Date(123213L);
System.out.println(date1.equals(date2));//true
}
}
class Test {
private String name;
private int age;
public Test(String name, int age) {
super();
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;
}
//重写的原则:比较两个对象的实体内容(即:name和age)是否相同
//自动创建equals()方法
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Test other = (Test) obj;
return age == other.age && Objects.equals(name, other.name);
}
//手写equals()方法
// @Override
// public boolean equals(Object obj) {
// if (this == obj) {
// return true;
// }
// if(obj instanceof Test) {
// Test t1 = (Test)obj
Java中的JUnit单元测试
package ObjectClass;
import org.junit.Test;
/*
* Java中的JUnit单元测试
*
* 步骤:
* 1.选中当前工程-右键-选择:bulid path - add libraries - JUnit 4 - 下一步
* 2.创建Java类,进行单元测试
* 此时的Java类要求:①此类是public的,②此类提供公共的无参的构造器
* 3.此类中声明单元测试方法
* 此时的单元测试方法,方法的权限是public,没有返回值,没有形参
*
* 4.此单元测试方法上需要声明注释:@Test 并在单元测试类中导入 import org.junit.Test;
*
* 5.声明好单元测试方法以后,就可以在方法体内测试相关的代码。
* 6.写完代码以后,左键双击单元测试方法名, 右键-run as - JUnit Test
*
* 说明:
* 1.如果执行结果没有任何异常。绿条
* 2.如果执行结果出现异常,红条
*/
public class JUnitTest {
@Test
public void test() {
Order o1 = new Order(1,"wang");
Order o2 = new Order(1,"wang");
System.out.println(o1.equals(o2));
System.out.println(o1==o2);
}
}
第五章、面向对象(下)
一.包装类的使用
package WrapperClass;
import org.junit.Test;
/*
*
* 包装类的使用
* 1.Java提供了8种基本数据类型,使得基本数据类型的变量具有类的特征
*
* 2.掌握的,基本数据类型、包装类、String三者之间的转换
*
*/
public class WrapperClassTest {
//基本数据类型--->包装类,调用包装类的构造器
@Test
public void test1() {
int num1= 10;
// System.out.println(num1.toString());编译错误
Integer in1 = new Integer(num1);
System.out.println(in1.toString());
Integer in2 = new Integer("123");
System.out.println(in2);
//报异常
// Integer ine = new Integer("123abc");//必须是数字
// System.out.println(in3);
Float f1 = new Float(12.3);
System.out.println(f1);
System.out.println(f1.toString());
Float f2 = new Float("11");
System.out.println(f2);
System.out.println(f2.toString());
Float f3 = new Float("11F");
System.out.println(f3);
Boolean b1 = new Boolean(true);
System.out.println(b1);
Boolean b2 = new Boolean("true");
System.out.println(b2);
//布尔型包装类赋值时,只要值不是true,一律返回false,无论大小写
Boolean b3 = new Boolean("true23");
System.out.println(b3);
Order o1 = new Order();
System.out.println(o1.isMale);
//isFemale的类型是类(包装类),所以返回的默认值是空
Order o2 = new Order();
System.out.println(o2.isFemale);//null
}
//包装类-->基本数据类型:调用包装类Xxx的xxxValue()
@Test
public void test2() {
Integer in1 = new Integer(12);
//包装类的对象是不能做基本运算的,所以要转换为基本数据类型
int i1 =in1.intValue();
System.out.println(i1+1);
Float f1 = new Float(12.3);
float f2 =f1.floatValue();
System.out.println(f2+1);
}
//jdk5.0新特性,自动装箱与自动拆箱
@Test
public void test3() {
int num1=10;
//当方法的形参是对象时,我们要传入一个数值,这是我们需要把
//基本数据类型转换为包装类,并传递值
// method(num1);//直接传入数值是不行的
//自动装箱:基本数据类型-->包装类
int num2=10;
Integer in1=num2;//自动装箱
boolean b1 =true;
Boolean b2 = b1;//自动装箱
//自动拆箱
System.out.println(in1.toString());
int num3 = in1;//自动拆箱
Integer int1 = new Integer(10);
int1=int1+1;
//在进行此代码时,并非是包装类对象实现了基本运算
//而是int1在运算时实现了自动拆箱完成数值运算,又执行自动装箱赋值给了int1
System.out.println(int1);//11,这里实现了int1+1
}
public void method(Object obj) {
System.out.println();
}
//基本数据类型-->String类型:调用String重载的valueOf(Xxx xxx)
@Test
public void test4() {
int num1=10;
//方式1:连接运算
String str1 = num1+"";//字符串拼接
//方式2:valueOf()
float f1 = 12.3f;
String str2 = String.valueOf(f1);
Double d1 = new Double(12.4);
String str3=String.valueOf(d1);
System.out.println(str2);
}
//String类型--->基本数据类型、包装类:调用包装类的parseXxx(String s)
@Test
public void test5() {
String str1="123";
//错误的写法:
// int num1=(int)str1;
// Integer in1 = (Integer)str1;
//可能 会报NumberFormatException
int num2 = Integer.parseInt(str1);
System.out.println(num2);
String str2="true";
boolean b1 = Boolean.parseBoolean(str2);
System.out.println(b1);
String str3 = "true1";
boolean b2 =Boolean.parseBoolean(str3);
System.out.println(b2);//返回false
}
}
class Order{
boolean isMale;
Boolean isFemale;
}
二.static关键字的使用
package Keyword;
/*
* static关键字的使用
*
* 1.static:静态的
* 2.static 可以用来修饰:属性、方法、代码块、内部类
*
* 3.使用static修饰属性:静态变量 (或类变量)
* 3.1属性:按是否使用了static修饰:又分为:静态属性 vs 非静态属性(实例变量)
* 实例变量:我们创建了类的多个对象,每个对象都独立拥有一套类中的非静态属性,当修改其中一个对象中的
* 非静态属性时,不会导致其他对象中同样的属性值的修改
* 静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量,当通过某一个对象修改静态变量时,会导致
* 其他对象调用此静态变量时,是修改过了的
*
* 3.2 static 修饰属性的其他说明
* ① 静态变量随着类的加载而加载,可以通过"类.静态变量"的方式进行调用
* ② 静态变量的加载要早于对象的创建
* ③ 由于类只会加载一次,则静态变量在内存中也只会存在一份,存在方法区的静态域中
*
* ④ 类变量 vs 实例变量
* 类 yes no
* 对象 yse yes
*
* 4.使用static修饰方法:静态方法
* ① 随着类的加载而加载,可以通过"类.静态方法"的方式进行调用
* ② 静态方法 非静态方法
* 类 yes no
* 对象 yes yes
*
* 5.static注意点:
* 5.1 在静态的方法内,不能使用this关键字、super关键字
* 5.2 关于静态属性和静态方法的使用,大家都从生命周期的角度去理解
*
* 6. 开发中,如何确定一个属性是否要声明为static的
* > 属性是可以被多个对象所共享的,不会随着对象的不同而不同的
*
* 开发中,如何确定一个方法是否要声明为static的
* > 操作静态属性的方法,通常设置为static
* > 工具类中的方法,习惯上声明为static的,比如:Math、Arrays、Collection
*
*/
public class StaticTest {
String name;
int age;
static String country;
public static void main(String[] args) {
//随着类的加载,静态变量也会加载出来,可以使用类来调用静态变量
StaticTest.country="中国";
StaticTest p1= new StaticTest();
p1.name="李";
p1.age=10;
p1.country="China";
System.out.println("name:"+p1.name+" age:"+p1.age);
System.out.println("From:"+p1.country);
StaticTest p2= new StaticTest();
p2.name="王";
p2.age=10;
p2.country="CHN";
System.out.println("name:"+p2.name+" age:"+p2.age);
System.out.println("From:"+p2.country);
//类的多个对象使用的country指向的都是同一个静态变量country,所以当一个对象更改静态变量的值
//其他对象调用此静态变量时,是修改过了的
System.out.println("From:"+p1.country);
//编译不通过
// StaticTest.name="zhang";
StaticTest.method();
}
public static void method() {
System.out.println("静态方法");
// info();//静态方法中不可以调用非静态方法和非静态变量
}
public void info() {
//非静态方法中,可以任意调用静态方法及属性
method();//此时method前省略的是"StaticTest."
System.out.println("name:"+name);
}
}
}
}
三.单例设计模式
package StaticTest;
/*
*单例设计模式
*1、所谓类的单例设计模式,就是采取一定的方法保证在整个的系统中,对某个类只能存在一个对象实例
*
*2.如何实现?
* ① 饿汉式 ② 懒汉式
*
* 3.区分饿汉式和懒汉式
* 饿汉式:
* 坏处:对象加载时间过长
* 好处:饿汉式是线程安全的
* 懒汉式
* 好处:延迟对象的创建
* 目前的写法坏处:线程不安全--->到多线程内容时,会在修改
*
*/
public class SingletonMode {
public static void main(String[] args) {
// Bank bank = new Bank();//不可直接建立对象
//饿汉式
Bank bank1 = Bank.getInstance();
Bank bank2 = Bank.getInstance();
System.out.println(bank1==bank2);//true
//两个对象指向的是同一个地址
//懒汉式
Order o1 = Order.getIntance();
Order o2 = Order.getIntance();
System.out.println(o1==o2);//true
}
}
//饿汉式
class Bank{
//1.私有化类的构造器
private Bank() {
}
//2.内部创建类的对象
//4.要求此对象也必须声明为静态的
private static Bank intance = new Bank();
//3.提供公共的方法,返回类的对象
public static Bank getInstance() {
return intance;
}
}
//懒汉式
class Order{
//1.私有化类的构造器
private Order() {
}
//2.声明当前类的对象,没有初始化
//4.要求此对象也必须声明为静态的
private static Order intance = null;
//3.提供公共的方法,返回类的对象
public static Order getIntance() {
if(intance==null) {
intance = new Order();
}
return intance;
}
}
四.main()方法的使用说明
package Method;
/*
* main()方法的使用说明:
* 1.main()方法作为程序的入口
* 2.main()方法也是一个普通的静态方法
* 3.main()方法也可以作为我们与控制台交互的方式。(之前使用的Scanner)
*/
public class MainTest {
public static void main(String[] args) {//入口
Main.main(new String[100]);
show();//相当于静态方法调用静态方法,前面省略了"MainTest."
}
public static void show() {
}
}
class Main{
public static void main(String[] args) {
for(int i=0;i<args.length;i++) {
args[i] = "args_"+i;
System.out.println(args[i]);
}
}
}
main()方法的控制台交互
package Method;
/*
* main()方法控制台交互测试
*
* 右键代码窗体 -> Run as->Run conflgurations-> 选定类->选择Argumens填入参数
*
*
*/
public class MainDemo {
public static void main(String[] args) {
for(int i=0;i<args.length;i++) {
System.out.println("***"+args[i]);//用String接收
int num = Integer.parseInt(args[i]);
System.out.println("###"+num);
}
}
}
如何利用cmd命令行填写参数
五、代码块使用说明
**package classTest;
/*
* 类的成员之四:代码块(或初始化块)
*
* 1. 代码块的作用:用来初始化类、对象
* 2. 代码块如果具有修饰的话,只能使用static
* 3. 分类: 静态代码块 vs 非静态代码快
*
* 4. 静态代码块
* >内部可以有输出语句
* >随着类的加载而执行,而且只执行一次
* >作用:初始化类的信息
* >如果一个类中定义了多个静态代码块,则按声明的先后顺序依次执行
* >静态代码块的执行要优先于非静态代码块的执行
* >静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构
*
*
* 5. 非静态代码块
* >内部可以有输出语句
* >随着对象的创建而执行
* >每创建一个对象,就执行一次非静态代码块
* >作用:可以在创建对象时,对对象的属性等进行初始化
* >如果第一个类中定义了多个非静态代码块,则按照声明的先后顺序依次执行
* >非静态代码块内可以调用静态的属性、静态的方法、或非静态的属性、非静态的方法
*
*
*
*
*
*/
public class BlockTest {
public static void main(String[] args) {
Person.info1();
Person p = new Person();
Person.info1();
}
}
class Person{
String name;
int age;
static String desc="Person";
//static代码块
static {
System.out.println("这是静态代码块1");
}
static {
System.out.println("这是静态代码块2");
}
// private static Person p1 = new Person();
// public static Person getP1() {
// return p1;
// }
//非静态代码块
{
System.out.println("这是非静态代码块1");
}
{
System.out.println("这是非静态代码块2");
}
public static void info1() {
System.out.println("this is test1");
}
public void info2() {
System.out.println("this is test2");
}
}
对属性可以赋值的位置及执行顺序
package classTest;
/*
* 对属性可以赋值的位置
*
* ①默认初始化
* ②显式初始化
* ③构造器中初始化
* ④有了对象以后,可以通过"对象.属性"或"对象.方法"的方式,进行赋值
* ⑤在代码块中赋值
*
* 执行的先后顺序 ①-②/⑤(看声明的先后顺序)-③-④
*
*/
public class Attribute {
public static void main(String[] args) {
Attribute test = new Attribute();
System.out.println(test.num);
}
int num=1;
{
num=2;
}
//关于显式赋值和代码块中赋值的先后顺序,要看他们在类中声明的顺序而定
//先声明的就先执行
// {
// num=2;
// }
// int num=1;
}
六、final关键字的使用
package Keyword;
/*
* final:最终的
*
* 1.final可以用来装饰的结构:类、方法、变量
*
* 2.final用来修饰一个类:此类不能被其他类所继承
* 比如:String类、System类、StringBuffer类
*
* 3. final 用来修饰方法:表明此方法不能被重写
* 比如:Object类中的getClass();
*
* 4.final 用来修饰变量:此时的"变量"就称为是一个常量
* 4.1final修饰属性:可以考虑赋值的位置有:显式初始化,代码块中初始化,构造器中初始化
* 4.2final修饰局部变量:
* 尤其是使用final修饰形参时,表明此形参是一个常量,当我们调用此方法时,给常量形参赋一个实参
* 一旦赋值以后,就只能在方法体内使用此形参,但不能进行重新赋值
*
*
* static final 用来修饰属性:全局常量
*
*/
public class Final {
public void show() {
final int NUM=10;
// NUM+=10;//final修饰的局部变量不可被重新赋值
}
public void show1(final int num) {
System.out.println(num);
// num+=10;//final 修饰的形参在被传入参数后,不可重新赋值修改
}
}
class A{
public final void info() {
System.out.println(1);
}
}
class B extends A{
//final修饰的方法不能被子类重写
// public final void info() {
//
// }
}
final class AA{
int i=1;
}
//final修饰的类不能被继承
//class BB extends AA{
//
//}
七、抽象类与抽象方法的使用
package AbstractTest;
/*
* abstract关键字的使用
*
* 1.abstract:抽象的
*
* 2.abstract:可以用来修饰的结构:类、方法
*
* 3.abstract修饰类:抽象类
* >此类不能被实例化
* >抽象类一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)
* >开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作
*
* 4.abstract修饰方法:抽象方法
* >抽象方法只有方法的声明:没有方法体
* >包含抽象方法的类:一定是一个抽象类,反之,抽象类中可以没有抽象方法
* >若子类重写了父类中的所有的抽象方法后,此子类方可实例化
* 若子类没有重写父类中的所有的抽象方法,则子类也是一个抽象类,需要使用abstract修饰
*
* abstract使用上的注意点:
* 1.abstract不能用来修饰:属性、构造器等结构
*
* 2.abstract不能用来修饰私有方法、静态方法、final的方法、final的类
*
* 3.普通类中可以有的,抽象类都可以有
*
* 4.抽象类不能通过new 构造方法()的方式直接实例化,但是可以通过子类对象实例化间接调用父类构造方法实例化父类对象,
* 而且子类对象给抽象父类变量赋值(向上转型)。
*
* 5.抽象类的子类可以是抽象类,也可以是普通类。如果子类是普通类必须重写抽象父类中的全部方法;如果子类是抽象类,可以重写,也可以不重写。
*/
public class AbstractTest {
public static void main(String[] args) {
// Person1 p = new Person1();//抽象类不能被是实例化
}
}
abstract class Person1 {
String name;
int age;
public Person1() {
}
public Person1(String name,int age) {
this.name=name;
this.age=age;
}
//不是抽象方法
// public void eat() {
//
// }
public abstract void eat();//抽象方法
public void walk() {
System.out.println("人走路");
}
}
class Student1 extends Person1{
public Student1(String name,int age) {
super(name,age);
}
@Override
public void eat() {
System.out.println("学生吃法");
}
}
八、匿名子类的使用
package AbstractTest;
/*
* 匿名子类的使用
*/
public class PersonTest {
public static void main(String[] args) {
new Student().eat();//匿名对象
method(new Student());//匿名对象
Teacher teach = new Teacher();
method1(teach);//非匿名的类非匿名的对象
method1(new Teacher());//非匿名的类匿名的对象
//创建了一匿名子类的对象:p
Person p =new Person() {
@Override
public void eat() {
System.out.println("我要吃饭饭阿发");
}
};
method1(p);
//创建一个匿名子类的匿名对象
method1(new Person() {
public void eat() {
System.out.println("我要吃饭饭阿发");
}
});
}
public static void method1(Person p) {
p.eat();
}
public static void method(Student s) {
s.eat();
}
}
class Teacher extends Person{
public void eat() {
System.out.println("老师吃饭");
}
}
九、模板方法的设计模式
package AbstractTest;
/*
* 模板方法的设计模式
*
* 当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以
* 把不确定的部分暴露出去,让子类去实现。
*
* 换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,
* 这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽
* 象出来,供不同子类实现。这就是一种模板模式。
*/
public class TemplateTest{
public static void main(String[] args) {
TemplateMethod t = new TemplateMethod();
t.getTime();
}
}
abstract class Template{
public final void getTime() {
long start = System.currentTimeMillis();
code();//易变因素,留给子类去实现
long end =System.currentTimeMillis();
System.out.println("执行时间为:"+(end-start));
}
public abstract void code();
}
class TemplateMethod extends Template{
public void code() {
for(int i=0;i<=500;i++) {
System.out.println(i);
}
}
}
十、接口的使用(interface)
package InterfaceTest;
/*
* 接口的使用
*
* 1.接口使用interface来定义 用implements实现
* 2.Java中,接口和类是并列的两个结构
* 3.如何定义接口,定义接口中的成员
*
* 3.1 JDK7及以前,只能定义全局变量和抽象方法
* >全局变量:public static final的,但是书写时,可以省略不写
* >抽象方法:public abstract的
*
* 3.2 JDK8及以后,除了定义全局变量和抽象方法之外,还可以定义静态方法。默认方法(略)
*
* 4.接口中不能定义构造器,意味着接口不可以实例化
*
* 5.Java开发中,接口通过让类去实现(implements)的方式来使用,
* 如果实现类覆盖了接口中的所有抽象方法,则实现类就可以实例化
* 如果实现类没有覆盖接口中的所有抽象方法,则实现类仍为一个抽象类
*
* 6. Java类可以实现多个接口 --->弥补了Java单继承的局限性
* 格式: class AA extents BB implements CC,DD,EE
*
* 7. 接口与接口之间可以继承,而且可以多继承
*
* ********************************
* 8.接口的具体使用:体现多态性
*
* 9.接口:实际上是一种规范
* 10.体会面向接口编程
*/
public class InterfaceTest {
public static void main(String[] args) {
System.out.println(Flyable.MAX_SPEED);
System.out.println(Flyable.MIN_SPEED);
Plane p = new Plane();
p.fly();
p.stop();
p.atct();
}
}
interface Flyable{
public static final int MAX_SPEED=1000;
int MIN_SPEED=1;
public abstract void fly();
void stop();
}
interface Attact{
void atct();
}
class Plane implements Flyable,Attact,CC{
public void fly() {
System.out.println("飞机在天上飞");
}
public void stop() {
System.out.println("驾驶员减速停机");
}
public void atct() {
System.out.println("可以发射导弹");
}
@Override
public void method1() {
// TODO Auto-generated method stub
}
@Override
public void method2() {
// TODO Auto-generated method stub
}
}
//******************************************
interface AA{
void method1();
}
interface BB{
void method2();
}
interface CC extends AA,BB{//接口CC继承了AA,BB
}
接口的匿名实现类的使用
package InterfaceTest;
//8.接口的具体使用:体现多态性
//*
//* 9.接口:实际上是一种规范
/*
* 接口的匿名实现类的使用
*/
public class UsbTest {
public static void main(String[] args) {
Computer com = new Computer();
//创建接口的非匿名实现类的非匿名对象
Flash flash = new Flash();
com.transferDate(flash);
//创建接口的非匿名实现类的匿名对象
com.transferDate(new Flash());
//创建接口的匿名实现类的非匿名对象
Usb phone = new Usb() {
public void start() {
System.out.println("手机开启");
}
public void stop() {
System.out.println("手机关闭");
}
};
com.transferDate(phone);
//创建接口的匿名实现类的匿名对象
com.transferDate(new Usb() {
public void start() {
System.out.println("电脑开启");
}
public void stop() {
System.out.println("电脑关闭");
}
});
}
}
class Computer{
public void transferDate(Usb usb) {
usb.start();
System.out.println("数据传输");
usb.stop();
}
}
interface Usb{
void start();
void stop();
}
class Flash implements Usb{
public void start() {
System.out.println("开启数据传输");
}
public void stop() {
System.out.println("停止数据传输");
}
}
class Printer implements Usb{
public void start() {
System.out.println("开启打印");
}
public void stop() {
System.out.println("停止打印");
}
}
接口的应用:代理模式
package Designpattern;
/*
* 设计模式:代理模式
*
*/
public class ProxyModel {
public static void main(String[] args) {
Server server = new Server();
ProxyServer proxyserver =new ProxyServer(server);
proxyserver.browse();
}
}
interface Network{
public void browse();//上网浏览
}
//被代理类
class Server implements Network{
public void browse() {
System.out.println("真实的服务器上网浏览");
}
}
//代理类
class ProxyServer implements Network{
private Network work;
public ProxyServer(Network work) {
this.work=work;
}
public void check() {
System.out.println("联网之前的检查工作");
}
public void browse() {
check();
work.browse();
}
}
接口的应用:工厂模式
理解工场模式的使用
详情看笔记文件 pdf
jdk8及以后接口的使用
package InterfaceTest;
/*
* jdk8及以后的接口的应用:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法
* 静态方法:public static void method(){
*
* }
* 默认方法: public defalut void method(){
*
* }
* 可缩写为defalut void method(){
* }
*
*/
public class JDK8Interface {
public static void main(String[] args) {
SubClass s = new SubClass();
// s.method1();
//知识点1:接口中定义的静态方法,只能通过接口来调用
CompareA.method1();
//知识点2:通过实现类的对象可以调用接口中的默认方法
//如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法
s.method2();
//知识点三:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的方法,
//那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法--->类优先原则
s.method3();
//知识点4:如果实现类实现了多个接口,而这多个接口中定义同名同参数的默认方法
//这就需要我们必须在实现类中重写此方法,来明确指定到底执行哪个接口中的方法或者自己定义方法体
SubClass2 ss = new SubClass2();
ss.method3();
}
}
interface CompareA{
//静态方法
public static void method1() {
System.out.println("CompareA:北京");
}
//默认方法
public default void method2() {
System.out.println("CompareA:上海");
}
default void method3() {
System.out.println("CompareA:上海");
}
}
interface CompareB{
default void method3() {
System.out.println("CompareB:上海");
}
}
class SubClass extends SuperClass implements CompareA,CompareB{
public void method2() {
System.out.println("SubClass:上海");
}
}
class SubClass2 extends SuperClass2 implements CompareA,CompareB{
public void method3() {
System.out.println("SubClass2:深圳");
}
//如何在子类或实现类的方法中调用父类、接口中被重写的方法
public void method4() {
method3();
super.method3();
CompareA.super.method3();
CompareB.super.method3();
}
}
class SuperClass{
public void method3() {
System.out.println("SuperClass:上海");
}
}
class SuperClass2{
public void method3() {
System.out.println("SuperClass:上海");
}
}
十一、内部类的使用
package innerclass;
/*
* 类的内部成员之五:内部类
* 1. Java中允许讲一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类
*
* 2.内部类的分类:成员内部类(静态、非静态) vs 局部内部类(方法内、代码块内、构造器内)
*
* 3.成员内部类:
* 一方面:作为外部类的成员:
* >调用外部类的结构
* >可以被static修饰
* >可以被4种不同的权限修饰
*
* 另一方面:作为一个类:
* >类内可以定义属性、方法、构造器等
* >可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以继承
* >可以被abstract修饰
*
* 4.关注如下三个问题
* 4.1 如何实例化成员内部类的对象
* 4.2 如何在成员内部类中区分调用外部类的结构
* 4.3 开发中局部内部类的使用
*
* 5.总结:成员内部类和局部内部类,在编译以后,都会生成字节码文件
* 格式:成员内部类:外部类$内部类名.class
* 局部内部类:外部类$数字 内部类名.class
*/
public class InnerClassTest {
public static void main(String[] args) {
//创建Dog实例(静态的成员内部类)
Animal.Dog dog = new Animal.Dog();
dog.eat();
//创建Bird实例(非静态的成员内部类)
// Animal.Bird bird = new Animal.Bird();
Animal am = new Animal();
Animal.Bird bird = am.new Bird();
bird.sing();
bird.display("黄鹂");
}
}
class Animal{
String name="动物";
int age;
public void eat() {
System.out.println("动物吃东西");
}
//静态成员内部类
static class Dog{
String name = "乐乐";
int age;
public void eat() {
System.out.println("狗吃骨头");
}
}
//非静态成员内部类
class Bird{
String name="保安";
public void eat() {
Animal.this.eat();//调用外部类的非静态属性
}
public void sing() {
System.out.println("保安唱歌");
}
//如何在成员内部类中区分调用外部类的结构
public void display(String name) {
System.out.println(name);//调用形参name
System.out.println(this.name);//调用内部类中name
System.out.println(Animal.this.name);//调用外部类中name
}
}
//方法中局部内部类
public void method() {
//局部内部类
class AA{
}
}
//代码块中局部内部类
{
class BB{
}
}
//构造器中局部内部类
public Animal() {
class CC{
}
}
}
package innerclass;
/*
* 在局部内部类的方法中,如果调用局部内部类所声明的方法(比如:method)中的num
* 要求此局部变量声明为final的,java8及以上可以省略final的声明,但还是要求是final的
*/
public class InnerTest1 {
//开发中很少见
public void method() {
//局部内部类
final int num =10; //final可省略
class AA{
public void show() {
// num=1;//num是final,不可更改
System.out.println(num);
}
}
}
//常用局部内部类的使用方法
//返回一个实现了Comparable接口的类的对象
public Comparable getComparable() {
//创建一个实现了Comparable接口的类
class MyComparable implements Comparable{
@Override
public int compareTo(Object o) {
return 0;
}
}
return new MyComparable();
//方式二:匿名类
// return new Comparable() {
// @Override
// public int compareTo(Object o) {
// return 0;
// }
// };
}
}
第六章 异常处理
一、异常概述与异常体系结构
异常:在Java语言中,将程序执行中发生的不正常的情况称为“异常”
(开发过程中的语法错误和逻辑错误不是异常)
Java程序在执行过程中所发生的的异常事件可分为两类:
Error:Java虚拟机无法解决的严重问题:如JVM系统内部错误,资源耗尽等严重情况,比如:StackOverflowErrror和OOM,一般不编写针对性的代码进行处理
Exception:其他因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如:空指针访问、试图读取不存在文件、网络连接中断、数组角标越界
package exception;
/*
* Error:
* java虚拟机无法解决的严重问题,如:JVM系统内部错误、资源耗尽等严重情况,比如:StackOverflowError
* 和OMM,一般不编写针对想的代码进行处理
*/
public class ErrorTest {
public static void main(String[] args) {
//1.栈溢出:java.lang.StackOverflowError
// main(args);
//2.堆溢出:java.lang.OutOfMemoryError
// Integer[] arr = new Integer[1024*1024*1024];
}
}
package exception;
import java.io.File;
import java.io.FileInputStream;
import java.util.Date;
import java.util.Scanner;
import org.junit.Test;
/*
* 一、异常体系结构
*
* java.lang.Throwable
* |-----java.lang.Error:一般不编写针对性的代码进行处理
* |-----java.lang.Exception:可以进行异常的处理
* |-----编译时异常(checked):可在编译时期就对其进行处理
* |-------IOException
* |------FileNotFoundException
* |-------ClassNotFoundException
* |-----运行时异常(unchecked):只能在运行时才会显示
* |------NullPointerException
* |------ArrayIndexOutOfBoundsException
* |------ClassCastException
* |------NumberFormatException
* |------InputMismatchException
* |------ArithmeticException
*
* 面试题:常见的异常都有哪些?举例说明
*
*/
public class ExceptionTest {
//*************以下是编译时异常***************************
@Test
public void test7() {
File file = new File("hello.txt");
FileInputStream fis = new FileInputStream(file);
int data = fis.read();
while(data!=-1) {
System.out.print((char)data);
data = fis.read();
}
fis.close();//流的关闭
}
//*************以下是运行时异常**************************
//ArithmeticException
@Test
public void test6() {
int a=10;
int b=0;
System.out.println(a / b);
}
//InputMismatchException
@Test
public void test5() {
Scanner scan = new Scanner(System.in);
int score = scan.nextInt();
System.out.println(score);
}
// NumberFormatException
@Test
public void test4() {
String str = "abc";
int a= Integer.parseInt(str);
}
//ClassCastException
@Test
public void test3() {
Object obj = new Date();
String str = (String)obj;
}
//IndexOutOfBoundsException
@Test
public void test2() {
//ArrayIndexOutOfBoundsException
int[] arr = new int[3];
System.out.println(arr[3]);
//StringIndexOutOfBoundsException
String str ="abc";
System.out.println(str.charAt(3));
}
//NullPointerException
@Test
public void test1() {
int[] arr = new int[3];
arr=null;
System.out.println(arr[1]);
}
}
二、try-catch-finally结构的使用
package exception;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.junit.Test;
/*
* 一、异常的处理:抓抛类型
*
* 过程一:"抛":程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象
* 并将此对象抛出
* 一旦抛出对象以后,其他的代码就不在执行
* 关于异常对象的产生:①系统自动生成的异常对象
* ②手动生成的一个异常对象,并抛出(throw)
* 过程二:"抓":可以理解为异常的处理方式:①try-catch-finally ② throws
*
* 二、try-catch-finally的使用
*
* try{
* //可能出现异常的代码
* }catch(异常类型1 变量名1){
* //处理异常的方式1
* }catch(异常类型2 变量名2){
* //处理异常的方式2
* }catch(异常类型3 变量名3){
* //处理异常的方式3
* }
* finally{
* //一定会执行的代码
* }
*
* 说明:
* 1.finally是可选的
* 2.使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象
* 根据此对象的类型,去catch中进行匹配
* 3.一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常处理,一旦处理完成,就跳出当前的
* try-catch结构(没有写finally的情况),继续执行其后的代码
* 4.catch中的异常类型如果没有子父类关系,则谁声明在上,谁声明在下无所谓
* catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面,否则,报错
* 5.常用的异常对象处理的方式:①String getMessage() ②printStackTrace()
* 6.在try结构中声明的变量,在出了try结构后就不能再使用
*
*体会1:使用try-catch-finally处理编译时异常,使得程序在编译时就不再报错,但是运行时仍可能报错
* 相当于我们使用try-catch-finally将一个编译时可能出现的异常,延迟到运行时出现
*
*体会2:开发中由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了,
* 针对于编译时异常,我们一定要考虑异常的处理
*/
public class TryCatchTest {
@Test
public void test1() {
String str = "123";
str = "abc";
int num=0;
try {
num = Integer.parseInt(str);
System.out.println("hello--1");
}catch(NumberFormatException e) {
System.out.println("出现数值转换异常");
//String getMessage()
System.out.println(e.getMessage());
//printStackTrace()
e.printStackTrace();
}catch(NullPointerException e) {
System.out.println("空指针异常");
}
System.out.println("hello--2");
System.out.println(num);
}
@Test
public void test7() {
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) {
e.printStackTrace();
}
}
}
package exception;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.junit.Test;
/*
* try-catch-finally中finally的使用
*
* 1.finally时可选的
*
* 2.finaly中声明的是一定会被执行的代码。即使catch中又出现异常了,try中有return语句,catch中有return语句等情况
*
* 3.像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己收订的进行资源的释放。此时的资源释放,就需要声明在finally中
*/
public class FinallyTest {
@Test
public void test3() {
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(IOException e) {
e.printStackTrace();
}finally {
try {
if(fis!=null)//避免在try结构中出现filenotfoundexception,导致出现空指针
fis.close();//流的关闭
}catch (IOException e) {
e.printStackTrace();
}
}
}
@Test
public void test2() {
int num=method();
System.out.println(num);
}
public int method() {
try {
int[] arr = new int[10];
System.out.println(arr[10]);
return 1;
}catch(ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
return 2;
}finally {
System.out.println("我一定会被执行!!!");
}
}
@Test
public void test1() {
try {
int a=10;
int b=0;
System.out.println(a / b);
}catch(ArithmeticException e) {
// e.printStackTrace();
int[] arr = new int[10];
System.out.println(arr[10]);
}catch(Exception e) {
e.printStackTrace();
}finally {
System.out.println("我一定会被执行");
}
}
}
三、throws的使用
package exception;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.junit.Test;
/*
* 异常处理方式二:throws+异常类型
*
* 1."throws + 异常类型"写在方法的声明处,指明此方法执行时,可能会抛出的异常类型
* 一旦当方法执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常
* 类型时,就会被抛出,异常代码后续的代码,就不再执行了
*
* 2.体会:try-catch-finally:真正的将异常给处理掉了
* throws的方式只是将异常抛给了方法的调用者,并没有真正的将异常处理掉
* 3.开发中如何选择使用try-catch-finally,还是使用throws?
* 3.1 如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着
* 子类重写的方法中有异常,必须使用try-catch-finally方式处理
* 3.2 执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的,我们建议这几个方法使用throws的方式
* 进行处理,而执行的方法a可以考虑使用try-catch-finally方式进行处理
*/
public class ThrowsTest {
public static void main(String[] args) {
try {
test2();
} catch (IOException e) {
e.printStackTrace();
}
test3();
}
public static void test3() {
try {
test2();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void test2() throws IOException {
test1();
}
public static void test1() throws FileNotFoundException,IOException{
File file = new File("hello.txt");
FileInputStream fis = new FileInputStream(file);
int data = fis.read();
while(data!=-1) {
System.out.print((char)data);
data = fis.read();
}
fis.close();//流的关闭
System.out.println("输出测试");//当以上出现异常并前去处理后,出现异常以后的代码不会被执行
}
}
package exception;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
* 方法重写的规则之一:
* 1.子类重写的方法抛出异常类型不大于父类被重写的方法抛出的异常类型
* 3.开发中如何选择使用try-catch-finally,还是使用throws?
* 3.1 如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着
* 子类重写的方法中有异常,必须使用try-catch-finally方式处理
* 3.2 执行的方法a中,其中先后又调用了另外的几个方法,这几个方法是递进关系执行的,我们建议这几个方法使用throws的方式
* 进行处理,而执行的方法a可以考虑使用try-catch-finally方式进行处理
*/
public class ThrowsOverride{
public static void main(String[] args) {
ThrowsOverride test = new ThrowsOverride();
test.display(new SubClass());
}
public void display(SuperClass s) {
try {
s.method();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class SuperClass{
public void method() throws IOException{
}
}
class SubClass extends SuperClass{
public void method() throws FileNotFoundException{
}
}
四、如何手动抛出异常(throw)
package exception;
/*
* 关于异常对象的产生:①系统自动生成的异常对象
* ②手动生成的一个异常对象,并抛出(throw)
* 如何手动抛出异常
*/
public class ThrowTest {
public static void main(String[] args) {
Student t = new Student();
try {
t.regist(-10);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
class Student{
private int id;
public void regist(int id) throws Exception{
if(id>0) {
this.id=id;
}else {
// throw new RuntimeException("输入错误");
throw new Exception("输入错误");
}
}
}
五、如何自定义异常类
package exception;
/*
* 如何自定义异常类
* 1.继承于现有的异常结构:RuntimeException、Exception
* 2.提供全局常量:serialVersionUID
* 3.提供重载构造器
*/
public class MyException extends Exception {
static final long serialVersionUID = -7034897190745766939L;
public MyException() {
}
public MyException(String msg) {
super(msg);
}
}