常见的DOS命令:-exit推出当前DOS窗口
-cls清屏
-dir列出当前目录下所有的文件/子目录
-cd命令表示:change directory[改变目录]
cd+所要去的路径
-cd..回到上一级目录
-cd \ 直接回到根路径
-切换盘符:
c:回车
d:回车
e:回车
作为Java程序员,新建.Java结尾的文件,文件扩展名是.Java
*Java程序的运行包括两个非常重要的阶段
-编译阶段
-运行阶段
.Java(源文件)→.class(字节码)→类装载器→Java虚拟机(JVM)→操作系统→硬件平台
*编译阶段
-编译阶段主要任务是检查Java源文件是否符合Java语法,
符合Java语法则能够生成正常的字节码文件
不符合Java语法则无法生成字节码文件
-字节码文件不是纯粹的二进制,这种文件无法在操作系统当中直接执行。
-编译阶段过程:
*程序员需要在硬盘的某个位置新建一个.Java扩展名的文件,
该文件称为Java源文件,源文件当中编写的是Java源代码/源程序。
而这个源程序不能随意编写,必须符合Java语法规则
*Java程序员需要使用JDK当中自带的Javac.exe命令惊醒Java程序的编译。
Javac怎么用?在哪用?
-在DOS命令窗口中使用。
-Javac的使用规则:Javac Java源文件的路径
Javac是一个Java编译工具/命令。
*一个Java源文件可以生成多个.class文件
*运行阶段
-JDK安装之后,除了自带一个Javac.exe之外,还有另一个工具/命令,叫做Java.exe
Java.exe命令主要负责运行阶段
-Java.exe在那用?怎么用?
-在DOS窗口中使用
-Java.exe怎么用?
Java.类名
例如:硬盘上有一个A.class,那么就这样用:Java.A
千万注意:不要写成这样:Java A.class【这种格式是错误的】
-运行阶段的过程:
*打开DOS命令窗口
*输入Java.A
*Java.exe命令会启动Java虚拟机(JVM),JVM会启动类加载器classloader
*classloader回去硬盘上搜索A.class文件,找到该文件则将该文件的字节码装载到JVM当中
*JVM将A.class字节码文件解释成二进制1010101010这样的数据。
*然后操作系统执行二进制和底层硬件平台进行交互。
*安装JDK【JDK一般需要重oracle的官网下载】
*开发HelloWorld.java源程序
*将HelloWorld.Java源程序通过Javac工具进行编译:
-首先解决的问题是:Javac命令是否可用
-打开DOS命令窗口,直接输入javac,然后回车,出现以下问题:
'javac' 不是内部或外部命令,也不是可运行的程序或批处理文件。
出现以上问题是因为:windows操作系统无法找到javac命令文件
-怎么解决以上javac不可用的问题?
Windows操作系统是如何搜索硬盘上莫格命令?
*首先会从但钱目录下搜索
*当前目录搜索不到的话,会从环境变量path指定的路径当中搜索某个命令
*如果都搜索不到,则报以上的错误。
-配置环境变量path,专门给Windows操作系统指路的。电脑属性-高级系统设置-高级-环境属性
-javac命令怎么用?
java Java源文件路径
注意:路径包括相对路径和绝对路径,都可以。
*运行Java程序
-需要使用java.exe命令
-首先测试Java命令是否可用
-使用方式:
Java 类名
硬盘上有HelloWorld.class,那么类名就是:HelloWorld
Java HelloWorld
一定要注意:Java命令后面跟的不是文件路径,是一个类的名字。
-首先你需要先将DOS窗口中的目录切换到HelloWorld.class文件所在目录
-然后直接执行:Java HelloWorld 回车
*Java.exe命令会启动JVM
*JVM启动之后会启动类加载其ClassLoader
*ClassLoader会在硬盘上的莫格位置搜索HelloWorld.class字节码文件
*找到该文件执行
*找不到该文件报错
*一个好的开发习惯应该是多编写注释,这样程序的可读性增强。
*Java注释怎么写呢?
-单行注释
//单行注释,只注释当前行
-多行注释
/*多行注释
多行注释
多行注释
多行注释
多行注释
.....
*/
-javadoc注释
/**
* javadoc注释
* javadoc注释
* javadoc注释
* javadoc注释
*/
注意:这种注释是比较专业的注释,盖住是信息会被javadoc.exe工具解析并提取生成帮助文档。
*一个Java源文件当中可以定义多个class
*一个Java源文件中不一定有public的class
*一个class会对应生成一个XXX.class的字节码文件
*一个Java源文件当中定义公开的类的话,public的class只能有一个,并且该类名名称必须
和Java源文件名称一致。
*每一个class当中都可以编写main方法,都可以设定程序的入口,像执行B.class中的main方法:
-Java B
*注意:挡在命令窗口中执行Java.Hello,那么要求Hello.class当中必须有主方法。没有主方法会出现
运行阶段的错误。
D:\Course\chengxu\1>java B
错误: 在类 B 中找不到 main 方法, 请将 main 方法定义为:public static void main(String[] args)
否则 JavaFX 应用程序类必须扩展javafx.application.Application
*标识符
*关键字
*字面值
*变量
*数据类型
*二进制
*字符编码
*运算符
*控制语句
*方法
*递归
-在Java源程序当中凡是程序员有权利自己命名的单词都是标识符
-标识符在Editplus编辑器当中以黑色字体高亮显示
-标识符可以标识什么元素呢?
*类名
*方法名
*变量名
*接口名
*常量名
。。。
-一个合法的标识符只能由数字、字母、下划线。美元符号组成,不能含有其他符号
-不能以数字开头
-严格区分大小写
-关键字不能做标识符(public、class、static...)
-理论上无长度限制,但是最好不要太长
-最好见名知意
public class UserService{
public void login(String a,String password)
}
-遵守驼峰命名方式
SystemService
UserService
CustomerService
-类名、接口名:首字母大写,后面每个单词的首字母大写
-变量名、方法名:首字母小写,后面每个单词首字母大写
-常量名:全部大写
合法 不合法
---------------------------
_123Test 123Test
HelloWorld Hello World(有空格)
public1 class/public
public
class
void
if
for
static
while
do
default
byte
short
int
long
float
double
boolean
char
private
protected
switch
true
false
throw
try
catch
......
*提醒:关键字不需要单独拿出来记忆,编写过程中记忆;关键字都是小写的。
*字面值:
-10、100
-3.14
-"abc"
-'a'
-true、false
*字面值就是数据
*字面值是Java源程序的组成部分之一。包括标识符和关键字都是Java源程序的组成部分。
*数字在现实生活当中是分门别类的,所以数据在计算机语言当中也是有类型的:【数据类型】
-10、100 属于整数型字面值
-3.14 属于浮点型字面值
-true、false 属于布尔型字面值
-"abc"、"中国人" 属于字符串型字符型
-'a'、'人' 属于字符型字面值
*注意: Java语言当中所有的字符串型字面值必须使用使用双引号括起来:双引号是半角的
Java语言当中所有的字符型字面值必须用单引号括起来:单引号必须是半角的
*变量本质上来说是内存中的一块空间,这块空间由“数据类型”、“名字”、“字面值”。
*变量包含三部分:数据类型、名称、字面值【数据】
*变量是内存中存储数据最基本的单元。
*不同的数据有不同的数据类型,不同的数据类型底层会分配不同大小的空间。
*数据类型是知道程序在运行阶段应该分配多大的内存空间。
变量中存储的具体的“数据必须”必须和变量的“数据类型”一致,当不一致是编译报错。
数据类型 变量名;
语法格式:
变量名 = 字面值;
要求:字面值的数据类型必须和变量的数据类型一致。
=等号是一个运算符,叫做赋值运算符,先运算等式右边的表达式,表达式执行结束之后的结果赋值给左边的变量。
int i = 10;
int i = 10;
System.out.println(i);//10
int i = 20;
System.out.println(i);//20
int i = 100;
System.out println(i);//100
*第一种:读取变量中保存的具体数据 get/获取
*第二种:修改变量中保存的具体数据 set/设置
int i = 10;//set
System.out.println(i);//get
int a,b,c;
int i;程序执行到这里,内存空间并没有开辟出来,变量i并没有初始化,所以没有复制之前是无法访问的。
第一行;
第二行;
第三行;
特点:第二行的代码必须完整的结束之后第三行的程序才能执行。
根据变量声明的位置来分类:
*局部变量
-在方法体当中声明的变量
*成员变量
-在方法体之外【类体之内】声明的变量
程序当中有很多数据类型,每一个数据都是有相关的类型的,不同数据类型的数据占用的空间大小不同。
数据类型的作用是知道Jvm在运行程序的时候给该数据分配多大的内存空间
*基本数据类型
*引用数据类型【后边讲】
-类
-接口
-数组
......
*基本数据类型包括四大类八小种:
第一类:整数型
byte,short,int,long
第二类:浮点型
float,double
第三类:布尔型
boolean
第四类:字符型
char
*字符串使用双引号 "abc"
*字符使用单引号 'a'
基本数据类型 占用空间大小【单位:字节】
------------------------------------
byte 1
short 2
int 4
long 8
float 4
double 8
boolean 1
char 2
【现代计算机底层采用交流电的方式,接通和断开就两种状态,计算机只识别1或0,其他不认识】
*一种数据的表示形式
1 byte = 8 bit【1个字节等于8个比特位】一个比特位表示一个二进制位:1/0
1 KB = 1024 byte
1 MB = 1024 KB
1 GB = 1024 MB
1 TB = 1024 GB
*关于java中的数字类型,数字都是有正负之分的。所以在数字的二进制当中有一个二进制位被称为“符号位”。
并且这个“符号位”在所有二进制位的最左边,0表示正数。1表示负数。
*byte类型最大值:01111111【10000000(二进制)- 1】
*byte类型最大值:2的7次方-1,结果是:127;最小值:-128 范围:-128~+127
类型 取值范围
--------------------------------------
byte 【-128~127】
short 【-32768~32767】
int 【-2147483648~2147483647】
long
float
double
boolean 【true/false】
char 【0~65535】
注意:short和char所表示的种类总数是一样的,只不过char可以白哦是更大的正整数。
因为char没有负数
数据类型 默认值
------------------------------------
byte,short,int,long 0
float,double 0.0
boolean false【在c语言中,true是1,false是0】
char \u0000
八种基本数据类型默认值一切向0看齐
*算数运算符
*关系运算符
*逻辑运算符【布尔运算符】
*赋值类运算符
*字符串运算符
*三元运算符
+ 求和
- 相减
* 乘积
/ 商
% 求余数【取模】
++ 自加1
-- 自减1
注意:一个表达式当中有多个运算符,运算符有优先级,不确定的加小括号,优先级提升,无需专门记忆运算符优先级。
public class OperatorTest01
{
public static void main(String[] args){
int i = 10;
int j = 3;
System.out.println(i + j);//13
System.out.println(i - j);//3
System.out.println(i * j);//30
System.out.println(i / j);//3
System.out.println(i % j);//1
//以下以++为例,--运算符自学
//关于++运算符【自加1】
int k = 10;
//++运算符可以出现在变量的后面【单目运算符】
k ++;
System.out.println(k); //11
int y = 10;
//++运算符可以出现在变量的前面
++ y;
System.out.println(y); //11
//小结:++运算符可以出现在变量前也可以出现在变量后,无论是变量前还是变量后,只要++运算结束,该变量中的值一定会自加1。
//++出现在变量之后
//规则:先做赋值运算,再对变量中保存的值进行自加1
int a = 100;
int b = a ++;
System.out.println(b); //100;先做赋值运算
System.out.println(a); //101;再对变量中保存的值进行自加1运算
//++ 出现在变量前
//规则:先进行自加1运算,再进行赋值
int m = 20;
int n = ++ m;
System.out.println(m); //21
System.out.println(n); //21
int e = 100;
System.out.println(e ++); //100;相当于把e ++赋值给类函数里的(int x = e ++)。
System.out.println(e); //101
int s = 100;
System.out.println(++ s); //101
System.out.println(s); //101
System.out.println(-- s); //100;s = 100
System.out.println(s ++); //100;s = 101
System.out.println(s --); //101;s = 100
System.out.println(s --); //100;s = 99
System.out.println(s --); //99;s = 98
System.out.println(s); //98
}
}
> 大于
>= 大于等于
< 小于
<= 小于等于
== 等于
!= 不等于
= 是赋值运算符
==是关系运算符
关系运算符的运算结果一定是布尔类型:true/false
关系运算符的比较原理:比较的时候是将变量中的值的大小进行比较。
public class OperatorTest02
{
public static void main(String[] args){
int a = 10;
int b = 10;
System.out.println(a > b); //false
System.out.println(a >= b); //true
System.out.println(a < b); //false
System.out.println(a <= b); //true
System.out.println(a == b); //true
System.out.println(a != b); //false
}
}
& 逻辑与 (两边的算子都是true,结果才是true)
| 逻辑或 (两边的算子只要有一个true,结果就是true)
! 逻辑非 (取反,!false就是true,!true就是false,这是一个单目运算符)
^ 逻辑异或(两边的算子只要不一样,结果就是true)
&& 短路与
|| 短路或
*逻辑运算符要求两边的算子都是布尔类型,并且逻辑运算符最终的运算结果也是一个布尔类型。
*短路与和逻辑与最终的运算结果是相同的,只不过短路与存在短路现象。
*短路或和逻辑或最终的运算结果是相同的,只不过短路与存在短路现象。
*什么情况下发生短路现象呢?
*什么时候使用逻辑与运算符?什么时候使用短路与运算符?
public class OperatorTest03
{
public static void main(String[] args){
//运算符优先级不确定,加小括号
System.out.println(5 > 3 & 5 > 2); //true
System.out.println(5 > 3 & 5 > 6); //false
System.out.println(5 > 3 | 5 > 6); //true
System.out.println(true ^ false); //true
System.out.println(true ^ true); //false
//逻辑与和短路与
int x = 10;
int y = 8;
/*
//逻辑与
System.out.println(x < y & ++ x < y); //false
System.out.println(x); //11
*/
//短路与
System.out.println(x < y && ++ x < y); //false
//x < y结果是false,整个表达式结果已经确定是false,所以后面的表达式没有再执行,这种现象被称为短路现象。
//短路与才会有短路现象,逻辑与不会存在短路现象。短路与更智能,短路与更常用;但是要求全部运行,使用逻辑与。
System.out.println(x); //10
int n = 11;
int m = 9;
System.out.println(n > m || n ++ > m); //true
//n > m结果是true,整个表达式结果已经确定是true,所以后面表达式没有执行。
System.out.println(n); //11
}
}
赋值类运算符包括两种:
*基本的赋值运算符
=
*扩展的赋值运算符
+=
-=
*=
/=
%=
赋值类运算符优先级:先执行等号右边的表达式,将执行结果赋值给左边的变量。
扩展的赋值运算符不改变运算结果的类型,假设最初这个变量的类型是byte类型,无论怎么进行追加/追减,最终该变量的数据类型还是byte类型。
public class OperatorTest01
{
public static void main(String[] args){
//基本的赋值运算符
int i = 10;
System.out.println(i); //10
i = i+5;
System.out.println(i); //15
//扩展赋值运算符【+=运算符可以翻译为"追加/累加"】
i += 5; //等同于:i = i + 5;
System.out.println(i); //20
i -= 5; //等同于:i = i - 5;
System.out.println(i); //15
i *= 2; //等同于:i = i * 2;
System.out.println(i); //30
i /= 3; //等同于:i = i / 3;
System.out.println(i); //10
i %= 3; //等同于:i = i % 3;
System.out.println(i); //1
//---------------------------------
//10没有超出byte取值范围,可以直接赋值
byte b = 10;
//b = 15;//可以编译通过
//编译错误
//编译器只检查语法不运行程序,编译器发现b+5的类型是int类型,b是byte类型的变量
//大容量转小容量需要强制转换符
//b = b + 5;
//纠正
b = (byte)(b + 5);
System.out.println(b); //15
byte x = 10;
x += 5; //等同于 x = (byte)(x + 5);
System.out.println(x); //15
byte z = 0;
z += 128;
System.out.println(z); //-128【损失精度】
}
}
a)、+运算符再java语言当中有两个作用:
*加法运算,求和
*字符串的连接运算
b)、当“+”运算符两边都是数据的话,一定是进行加法运算
c)、当“+”运算符两边的数据只要有一个数据是字符串,一定会进行字符串连接运算。
并且,连接运算之后的结果还是一个字符串类型。
数字 + 数字 --> 数字【求和】
数字 + "字符串" --> "字符串"【字符串连接】
d)、在一个表达式当中可以出现多个“+”,再没有添加小括号的前提下,遵循自左向右的顺序依次运算。
public class OperatorTest02
{
public static void main(String[] args){
System.out.println(10 + 20 + "30"); //3030
System.out.println(10 + (20 + "30")); //102030
int a = 10;
int b = 20;
//要求在控制台上以动态的方式输出"10 + 20 = 30"
System.out.println("10 + 20 =" + (a + b));
System.out.println(a + "+" + b + "=" +(a + b));
//引用类型String
//String是SUN在JavaSE当中提供的字符串类型
//String.class字节码文件
//int是基本数据类型,i是变量名, 10是int类型的字面值
//int i = 10;
//String是引用数据类型,s是变量名,"abc"是String类型的字面值
//String s = "abc";
//String ss = 10;编译错误,类型不兼容
//定义一个String类型的变量,起名username,赋值"zhangsan"
String username = "zhzangsan";
System.out.println("登陆成功,欢迎" + username + "回来!");
}
}
a)、语法规则:
布尔表达式 ? 表达式1 :表达式2【这不是java语句,不可以单独写】
b)、三元运算符的执行原理?
当布尔表达式的结果为true的时候,选择表达式1作为整个表达式的执行结果
当布尔表达式的结果为false的时候,选择表达式2作为整个表达式的执行结果
public class OperatorTest03
{
public static void main(String[] args){
//布尔类型的变量
boolean sex = false;
//编译报错,因为它不是一个完整的java语句
//sex ? '男' : '女';
char c = sex ? '男' : '女';
System.out.println(c); //女
sex = true;
c = (sex ? '男' : '女');
System.out.println(c); //男
//语法错误,编译报错,结果可能是String【加双引号的是字符串,就是String类型】,也可能是char,但是前面不能用char来接收数据。
//类型不兼容
//char c1 = sex ? "男" : '女';
//可以
System.out.println(sex ? "男" : '女');
}
}
*选择结构
-if, if...else
-switch
*循环结构
-for
-while
-do...while()
*控制循环的语句
-break
-contiune
第一种:【如果布尔表达式为true,执行下面语句;如果布尔表达式为false,则不再执行下面语句】
if(布尔表达式){
java语句;
java语句;
java语句;
java语句;
java语句;
....
}
第二种:【如果布尔表达式为true,执行第一个大括号里的语句;如果布尔表达式为false,执行else后面的语句】
if(布尔表达式){
java语句;
java语句;
...
}elae{
java语句;
java语句;
...
}
第三种:【如果第一个布尔表达式为true,则执行第一个大括号的语句;如果为false,则继续判断下一个else if,以此类推】
if(布尔表达式){
java语句;
java语句;
....
}else if(布尔表达式){
java语句;
java语句;
...
}else if(布尔表达式){
java语句;
java语句;
...
}...
第四种:
if(布尔表达式){
java语句;
java语句;
....
}else if(布尔表达式){
java语句;
java语句;
...
}else if(布尔表达式){
java语句;
java语句;
...
}else{
java语句;
java语句;
...
}
注意:嵌套使用的时候,代码格式要保证完美。【该缩进的时候必须缩进】
{
{
{
{
}
}
}
}
if(true/false){
一条java语句;
}
if(true/false) 一条java语句;
这种方式不推荐使用,能看懂就行。
public class IfTest01
{
public static void main(String[] args){
//需求:所在位置的五公里范围内有KFC的话,去KFC吃午饭
//公里数
double distance = 6.0;//单位:KM
//判断语句
if(distance < 5){
System.out.println("去KFC吃午饭!");
}else{
System.out.println("不去KFC吃午饭!");
}
//需求:假设系统给定一个考生的成绩,成绩可能带有小数点,根据学生的成绩判断该学生的成绩等级
/*
[90 - 100] A
[80 - 90) B
[70 - 80) C
[60 - 70) D
[0 - 60) E
*/
double grade = 89.5;
//判断语句
/*
if (grade >= 90 && grade <= 100)
{
System.out.println("成绩为A");
}else{
if (grade >= 80 && grade < 90)
{
System.out.println("成绩为B");
}else{
if (grade >= 70 && grade < 80)
{
System.out.println("成绩为C");
}else{
if (grade >= 60 && grade < 70)
{
System.out.println("成绩为D");
}else{
if (grade >= 0 && grade < 60)
{
System.out.println("成绩为E");
}else{
System.out.println("无效成绩");
}
}
}
}
}
*/
/*
if (grade > 100 || grade < 0)
{
System.out.println("无效成绩");
}else if (grade >= 90)
{
System.out.println("成绩为A");
}else if (grade >= 80)
{
System.out.println("成绩为B");
}else if (grade >= 70)
{
System.out.println("成绩为C");
}else if(grade >= 60)
{
System.out.println("成绩为D");
}else{
System.out.println("成绩为E");
}
*/
grade = 100;
//改进
String score = "成绩为E";
if (grade > 100 || grade < 0)
{
score = "无效成绩";
}else if (grade >= 90)
{
score = "成绩为A";
}else if (grade >= 80)
{
score = "成绩为B";
}else if (grade >= 70)
{
score = "成绩为C";
}else if(grade >= 60)
{
score = "成绩为D";
}
System.out.println(score);
}
}
a)、System.out.println(); 负责向控制台输出【从内存到控制台,输出的过程,这是从内存中出来的】
接受用户键盘输入,从键盘到内存。【输入的过程,到内存中去】
以下代码不要问为什么!固定编写就可以接收用户键盘的输入。
*/
public class KeyInputTest
{
public static void main(String[] args){
//第一步:创建键盘扫描器对象
java.util.Scanner s = new java.util.Scanner(System.in);
//第二步:调用Scanner对象的next()方法开始接收用户键盘输入
//程序执行到这里会停下来等待用户的输入
//当用户输入,并且最终敲回车键的时候,键入的信息会自动赋值给userInputContent
//程序输入到这,用户输入的信息已经到内存中了。
//接收文本【以字符串的形式接收】
String userInputContent = s.next(); //标识符的命名规范:见名知意
//将内存中的数据输出到控制台
System.out.println("您输入了:" + userInputContent);
//接收数字【以整数型int的形式来接收】
//int num = s.nextInt();
//System.out.println("您输入的数字是:" + num);
}
}
/*
总结:记住代码,或者直接复制粘贴
java.util.Scanner s = new java.util.Scanner(System.in);
String userInputContent = s.next();
System.out.println("您输入了:" + userInputContent);
int num = s.nextInt();
System.out.println("您输入的数字是:" + num);
*/
b)、需求:
假设系统给定一个人的年龄,根据年龄来判断这个人处于生命的那个阶段,年龄必须在[0 - 150]
[0 - 5] 幼儿
[6 - 10] 少儿
[11 - 18] 青少年
[19 - 35] 青年
[36 - 55] 中年
[56 - 150] 老年
public class IfTest02
{
public static void main(String[] args){
//1、接收键盘输入:年龄【输入的时候必须输入数字】
java.util.Scanner s = new java.util.Scanner(System.in);
System.out.print("请输入您的年龄:");//输出提示信息,不然用户根本不知道这里要干嘛!
int age = s.nextInt();//停下来等待用户输入,输入之后自动赋值给age变量
//2、根据需求进行业务逻辑判断
String str = "您的年龄阶段是幼儿";
if (age < 0 || age > 150)
{
str = "您提供的年龄不合法,年龄值需要在[0 - 150]之间";
}else if (age >= 56)
{
str = "您的年龄阶段是老年";
}else if (age >= 36)
{
str = "您的年龄阶段是中年";
}else if (age >= 19)
{
str = "您的年龄阶段是青年";
}else if (age >= 11)
{
str = "您的年龄阶段是青少年";
}else if (age >= 6)
{
str = "您的年龄阶段是少儿";
}
//System.out.println("您的年龄阶段是" + str);
System.out.println(str);
}
}
c)、需求:
判断当前的天气:
当下雨的时候:
带雨伞:
判断性别:
当性别为男:带一把大黑伞
当性别为女:带一把小花伞
当外边是晴天的时候:
判断天气温度:
当天气温度在30度以上:
当性别为男:戴墨镜
当性别为女:涂防晒
注意:如果整数型字面值等于用“==”而不是“=”,“=”表示赋值;字符串用equals函数。
public class IfTest03
{
public static void main(String[] args){
//接收天气
java.util.Scanner s = new java.util.Scanner(System.in);
System.out.print("您所在城市的天气为:");
String weather = s.next();
//接收性别
System.out.print("您的性别为:");
String gender = s.next();
if (weather.equals("阴天"))
{
if (gender.equals("男"))
{
System.out.println("您应该带一把大黑伞");
}else if(gender.equals("女"))
{
System.out.println("您应该带一把小花伞");
}else{
System.out.println("无效输入");
}
}else if (weather.equals("晴天"))
{
//接收气温
System.out.print("当前气温为:");
int a = s.nextInt();
if (a > 30)
{
if (gender.equals("男"))
{
System.out.println("您应该戴墨镜");
}else if(gender.equals("女"))
{
System.out.println("您应该涂防晒");
}else{
System.out.println("无效输入");
}
}else{
System.out.println("您所在的城市气温舒适");
}
}else{
System.out.println("对不起,您输入的天气不存在");
}
}
}
d)、补充if语句
public class IfTest04
{
public static void main(String[] args){
boolean sex = true;
if (sex)
{
System.out.println("男");
}else{
System.out.println("女");
}
if (sex) System.out.println("男");else System.out.println("女");
/*
if (sex)
System.out.println("男");
System.out.println("嘿嘿");
else
System.out.println("女");
//以上程序中编译错误出现在26行。esle没有if,两个之间只能隔一行代码。
*/
}
}
一个比较完整的switch语法结构应该这样编写:
switch(int或String类型的字面值或变量){
case int或String类型的字面值或变量:
java语句;
java语句;
...
break;
case int或String类型的字面值或变量:
java语句;
java语句;
...
break;
case int或String类型的字面值或变量:
java语句;
java语句;
...
break;
...
default:
java语句;
...
}
switch后面小括号当中的“数据”和case后面的“数据”进行一一匹配,匹配成功的分支执行。
按照自上而下的顺序一次匹配。
*当然byte,short,char也可以直接写到switch和case后面,因为它们可以进行自动类型转换,可以自动转换成int类型。
*JKD6的,switch和case后面只能探测int类型
*JDK7之后包括7版本在内,引入新特性,switch和case关键字后面可以探测int和String类型的数据。
int i = 10;
switch(i){
case 1: case 2: case 3: case 10:
System.out.println("Test Code!");
}
public class SwitchTest01
{
public static void main(String[] args){
/*
long x = 100L;
switch (x){} //编译报错
*/
//解决编译错误
long x = 100L;
switch ((int)x){}
//较完整的switch语句
//接收用户输入:
//1 表示星期一
//2 表示星期二
//...
//7 表示星期日
java.util.Scanner s = new java.util.Scanner(System.in);
System.out.print("请输入数字:");
int num = s.nextInt();
switch (num)
{
case 1: case 0://case合并,输入0也是显示周一
System.out.println("今天是周一");
break;
case 2:
System.out.println("今天是周二");
break;
case 3:
System.out.println("今天是周三");
break;
case 4:
System.out.println("今天是周四");
break;
case 5:
System.out.println("今天是周五");
break;
case 6:
System.out.println("今天是周六");
break;
case 7:
System.out.println("今天是周日");
break;
default:
System.out.println("您输入的数字无效");
}
}
}
public class SwitchTest02
{
public static void main(String[] args){
java.util.Scanner s = new java.util.Scanner(System.in);
System.out.print("请输入星期几:");
String week = s.next();
switch (week)
{
case "星期一":
System.out.println(1);
break;
case "星期二":
System.out.println(2);
break;
default :
System.out.println("无效数值");
}
}
}
+
-
*
/
%
实现思路:
1)、选择所有数据从键盘输入
2)、选择switch语句进行判断
3)、需要从控制台输入三次:
*第一个数字
*运算符
*第二个数字
最终在控制台上是这样一个场景:
欢迎使用简单计算器系统
请输入第一个数字:
请输入运算符:+、-、*、/、%
请输入第二个数字:
运算结果:
public class SwitchTest03
{
public static void main(String[] args){
java.util.Scanner s = new java.util.Scanner(System.in);
System.out.println("欢迎使用简单计算器系统!");
System.out.print("请输入第一个数字:");
int num1 = s.nextInt();
System.out.print("请输入运算符:");
String operator = s.next();
System.out.print("请输入第二个数字:");
int num2 = s.nextInt();
/*自己编写的代码,还不错,嘿嘿!
switch (operator)
{
case "+":
System.out.println(num1 + " + " + num2 + " = " + (num1 + num2));
break;
case "-":
System.out.println(num1 + " - " + num2 + " = " + (num1 - num2));
break;
case "*":
System.out.println(num1 + " * " + num2 + " = " + (num1 * num2));
break;
case "/":
System.out.println(num1 + " / " + num2 + " = " + (num1 / num2));
break;
case "%":
System.out.println(num1 + " % " + num2 + " = " + (num1 % num2));
break;
default :
System.out.println("无效输入!");
}
*/
int result = 0;
switch (operator)
{
case "+":
result = num1 + num2;
break;
case "-":
result = num1 - num2;
break;
case "*":
result = num1 * num2;
break;
case "/":
result = num1 / num2;
break;
case "%":
result = num1 % num2;
break;
default :
System.out.println("无效输入!");
}
System.out.println(num1 + operator + num2 + " = " + result);
}
}
a)、有效成绩范围:[0 - 100]
b)、考试成绩可能带有小数
c)、考试成绩和等级之间的对照关系:
[90 - 100] A
[80 - 90) B
[70 - 80) C
[60 - 70) D
[0 - 60) E
d)、以上需求必须采用switch语句,不能采用if
窍门:(int)(成绩 / 10):0、1、2、3、4、5、6、7、8、9、10
public class SwitchTest04
{
public static void main(String[] args){
java.util.Scanner s = new java.util.Scanner(System.in);
System.out.print("请您输入成绩:");
double score = s.nextDouble();
int garde = (int)(score / 10);
switch (garde)
{
case 10: case 9:
System.out.println("您的成绩等级为A");
break;
case 8:
System.out.println("您的成绩等级为B");
break;
case 7:
System.out.println("您的成绩等级为C");
break;
case 6:
System.out.println("您的成绩等级为D");
break;
case 5: case 4: case 3: case 2: case 1: case 0:
System.out.println("您的成绩等级为E");
break;
default :
System.out.println("无效成绩");
}
}
}
在程序当中,总有一些需要反复/重复执行的代码,假设没有循环结构,那么这段需要重复执行的代码自然是需要重新编写的。
代码无法得到重复使用。所以多数编程语言都是支持循环结构的。将来把需要反复执行的代码片段放到“循环体”,再联合“计
数器”,共同控制这段需要反复执行的代码。
*for循环
*while循环
*do...while循环
语法结构:
for(初始化表达式 ; 布尔表达式 ; 更新表达式){
//是需要重复执行的代码片段【循环体:有java语句构成】
}
a)、初始化表达式、布尔表达式、更新表达式都不是必须的!【但两个分号是必须的】
b)、初始化表达式最先执行,并且再整个for循环当中只执行一次。
c)、布尔表达式必须是true/false,不能是其它值。
d)、for的执行过程:
*先执行初始化表达式,并且该表达式只执行一次
*判断布尔表达式是true/false
-布尔表达式true:执行循环体
*执行更新表达式
*判断布尔表达式是true/false
-布尔表达式true:执行循环体
*执行更新表达式
*判断布尔表达式是true/false
-布尔表达式true...
....
-布尔表达式false:循环结束
-布尔表达式false:循环结束
public class ForTest01
{
public static void main(String[] args){
//需求:输出数字1 ~ 10
int a = 1;
for (a = 1;a <=10 ; a++ )
{
System.out.println(a);
}
//死循环
for (; ; )
{
System.out.println("死循环");
}
}
}
public class ForTest02
{
public static void main(String[] args){
//以下变量i的作用域是仅限于当前的for循环内部使用
for (int i = 0;i < 10 ; i++ )
{
System.out.println("i --->" + i);
}
for (int i = 0;i <=10 ; i++ )
{
System.out.println("i --->>" + i);
}
//这个变量i可以在main方法的作用域当中使用吗? ----不可以
//编译出错,找不到符号
//System.out.println("i = " + i);
int i = 0;
for (;i <= 10 ; i++ )
{
System.out.println("i = " + i);
}
System.out.println("i === " + i); //11
int j;
for (j = 0;j < 10 ; j++ )
{
System.out.println("j = " + j);
}
System.out.println("j === " + j); //10
}
}
public class ForTest03
{
public static void main(String[] args){
//在前一个程序基础之上,计算1~100所以奇数的和
// 1 + 3 + 5 + 7 + 9 +... + 99累加
//运算符+=就是专门完成累加的。
//定义一个变量【盒子】,初始值为0
int sum = 0;
for (int i = 1;i <= 100 ;i += 2 ) //输出i为奇数
{
//定义sum不能放到这里,因为这里是循环体,放在这里每循环一次sun清零。
//int sum = 0;
sum += i;
//输出也不可以放在这里,放在这里就是循环一次报一个数,我们要的是最终结果
//System.out.println("sum = " + sum);
}
System.out.println("sum = " + sum);
//1~100偶数和
//归零
sum = 0;
for (int i = 1;i <=100 ; i++ )
{
if (i % 2 == 0)
{
sum += i;
}
}
System.out.println("sum = " + sum);
sum = 0;
for (int i = 2;i <= 100 ;i += 2 )
{
sum += i;
}
System.out.println("sum = " + sum);
}
}
public class ForTest04
{
public static void main(String[] args){
for (int i = 0;i < 10; i++ ) //循环10次
{
System.out.println("Begin");
//这里的代码片段输出10遍
for (int j = 0;j < 1; j++ ) //循环1次,输出0
{
System.out.println("j -->" + j);
}
System.out.println("Over");
}
for (int i = 1;i <= 5; i++)
{
System.out.println("开始");
for (int j = 1; j <= 5; j++)
{
System.out.println(i * j);
}
System.out.println("结束");
}
}
}
编写循环,找出1~100中所有的素数
素数:又被称为质数,能够被1和自身整除不能被其他数字整除的数字称为素数。1既不是素数也不是质数。
public class ForTest05
{
public static void main(String[] args){
for (int i = 2;i <= 100 ;i++ )
{
boolean isSuShu = true;
for (int j = 2;j < i ;j++ )
{
//对小于i的整数j取余数
//有余数为0的就不是质数
if (i % j == 0)
{
isSuShu = false;
break;
}
//if放在第二个循环体里面的话,第二个循环体循环多少次,i就输出多少次。
//if (isSuShu)
//{
// System.out.println(i);
//}
}
//System.out.println(isSuShu ? i + "是素数" : i + "不是素数");
if (isSuShu)
{
System.out.println(i);
}
}
}
}
升级版:
编写for循环找出1~10000中所有的素数
要求每8个换一行输出
public class ForTest05
{
public static void main(String[] args){
int count = 0;
for (int i = 2; i <= 10000; i++)
{
boolean isSuShu = true;
for (int j = 2;j < i; j++)
{
if (i % j == 0)
{
isSuShu = false;
break;
}
}
if (isSuShu)
{
System.out.print(i + " ");
count ++;
/*
if (count == 8)
{
System.out.println(); //输出换行符
count = 0;//归0
}
*/
if (count % 8 ==0)
{
System.out.println(); //输出换行符
}
}
}
}
}
while(布尔表达式){
循环体;
}
先判断布尔表达式的结果:
*true
-执行循环体
*判断布尔表达式的结果
*true
-执行循环体
*判断布尔表达式的结果
...
*false
-循环结束
*false
-循环结束
0 ~ N次
注意:while循环的循环体可能执行次数为0次。
public class WhileTest01
{
public static void main(String[] args){
/*死循环
while (true)
{
System.out.println("死循环");
}
//编译器检测到该程序永远无法被执行,所以编译报错
//System.out.println("Hello World!");
*/
int i = 10;
int j = 3;
while (i > j)
{
System.out.println("还是死循环");
}
//编译通过
System.out.println("Hello World!");
/*
while (10 > 3)
{
System.out.println("还是死循环");
}
//编译报错
System.out.println("Hello World!");
*/
}
}
public class WhileTest02
{
public static void main(String[] args){
int i = 1;
while (i <= 10)
{
System.out.println(i); //1 ~ 10
i ++;
}
System.out.println("end i --> " + i); //11
int j = 10;
while (j > 0)
{
System.out.println(j--); //10 ~ 1
System.out.println("j -> " + j); //9 ~ 0
}
System.out.println("end j --> " + j); //0
int k = 10;
while (k > 0)
{
System.out.println(--k); //9 ~ 0
}
System.out.println("end k --> " + k); //0
}
}
do{
循环体;
}while(布尔表达式);
-执行循环体
*判断布尔表达式的结果
*true
-执行循环体
*判断布尔表达式的结果
*true
-执行循环体
*判断布尔表达式的结果
...
*false
-循环结束
*false
-循环结束
do...while循环的循环体代码片段执行的次数是:1 ~ N次
do...while循环语句最终有一个“分号”别丢了!!!
public class DoWhileTest01
{
public static void main(String[] args){
int i = 10;
do{
System.out.println(i--);
}
while (i > 0); //10 ~ 1
System.out.println("end i --> " + i); //0
}
}
因为当程序循环到莫格条件的时候,后续的程序没有必要执行了,再执行也是耗费资源,所以
可以终止循环,这样可以提高成勋的执行效率。
需要给循环起名,采用这种语法:break 循环名称;
public class BreakTest01
{
public static void main(String[] args){
for (int i = 0; i < 10; i++)
{
if (i == 5)
{
break; //终止的是当前的for循环。
}
System.out.println(i); //0 ~ 4;i = 5的时候,break跳出循环。
}
//这里的程序和以上的for循环无关。
System.out.println("Hello World!");
for(int j = 0;j <3 ;j++)
{
int count = 0;
for (int i = 0;i < 10 ;i++ )
{
if (i == 5)
{
break; //终止的是内层for循环,不会影响外层for循环
}
System.out.print(i + " ");
count ++;
if (count % 5 == 0) //每输出5个数字换一行
{
System.out.println();
}
}
}
System.out.println("Hello World!");
//以下语法使用较少
//给for起名for1
for1:for(int j = 0;j <3 ;j++)
{
int count = 0;
//给for起名for2
for2:for (int i = 0;i < 10 ;i++ )
{
if (i == 5)
{
break for1; //终止的是内层for循环,不会影响外层for循环
}
System.out.println(i);
}
}
}
}
*break表示循环不执行了。
*continue表示直接进入下一次循环继续执行。
continue 循环名称;【作为了解内容】
public class ContinueTest01
{
public static void main(String[] args){
for (int i = 0;i < 10 ;i ++ )
{
if (i == 5)
{
break;
}
System.out.println("i = " + i); //0 ~ 4
}
System.out.println("Hello World!");
for (int i = 0;i < 10 ;i ++ )
{
if (i == 5)
{
continue; //只要这个语句执行,当前本次循环停止,直接进入下一次循环“继续”执行。
}
System.out.println("i = " + i); //0 1 2 3 4 6 7 8 9 ;5被跳过了
}
}
}
*方法的基础语法
*方法的重载机制overload
*以下的代码都是完成两个int类型数据的和,相同的代码写了三遍(只不过每次参与求和的具体数据不同),
代码没有得到重复使用。
*应该在java语言中有这样的一种机制:
-某个功能代码只需要写一遍。
-要使用这个功能,只需要给这个功能传递具体的数据;
-这个功能完成之后返回一个最终的结果。
这样代码就可以重复利用,提高代码复用性。【这就是“方法”】
*使用这个方法我们称为"调用/invoke"
public class MethodTest01
{
public static void main(String[] args){
//需求1:请编写程序计算10和20的和,并将结果输出【功能:计算两个int类型数据的和】
int a = 10;
int b = 20;
int c = a + b;
System.out.println(a + " + " + b + " = " + c);
//需求2:请编写程序计算666和888的和,并将结果输出【功能:计算两个int类型数据的和】
int d = 666;
int e = 888;
int f = d + e;
System.out.println(d + " + " + e + " = " + f);
//需求3:请编写程序计算111和222的和,并将结果输出【功能:计算两个int类型数据的和】
int g = 111;
int h = 222;
int i = g + h;
System.out.println(g + " + " + h + " = " + i);
//以上三个需求其实就是一个需求,这个需求是:计算两个int类型数据的和,功能相同,只不过是每一次参与计算的具体数据不同。
}
}
*代码得到了重复使用
方法地本质是什么?
*方法就是一段代码片段,并且这段代码片段可以完成某个特定的功能,并且可以被重复地使用。
方法,对应的英语单词是:Method
方法在c语言中叫做函数/Function
方法定义在类体当中,在一个类当中可以定义多个方法,方法编写地位置没有先后顺序,可以随意。
方法体当中不能再定义方法!!!!
方法体由java语句构成,方法体当中的代码遵守自上而下地顺序一次执行。
public class MethodTest02
{
public static void main(String[] args){
java.util.Scanner s = new java.util.Scanner(System.in);
int i = s.nextInt();
int j = s.nextInt();
//计算两个int类型数据的和
MethodTest02.sumInt(i,j);
}
//单独地定义一个方法
//该方法完成计算两个int类型数据地和,并且将结果输出
public static void sumInt(int a,int b){
int c = a + b;
System.out.println(a + " + " + b + " = " + c);
}
}
[修饰符列表] 返回值类型 方法名(形式参数列表){
方法体;
return 字面值;
}
a)、关于修饰符列表
*可选项,不是必须的
*目前统一写成:public static【以后讲】
*方法的修饰符列表当中有“static关键字”的话,怎么调用这个方法?
-类名.方法名(实际参数列表);这个.代表“的”
b)、返回值类型
*什么是返回值?
一个方法是可以完成某个特定功能的,这个功能结束之后大多数是需要返回最终的执行结果的,
执行结果可能是一个具体存在的数据。而这个具体存在的数据就是返回值。
*返回值类型?
返回值是一个具体存在的数据,数据都是有类型的,此处需要指定的是返回值的具体类型。
*返回值都可以指定哪些类型呢?
java任意一中类型都可以,包括基本数据类型和所有的引用数据类型。
*也可能这个方法执行结束之后不返回任何数据,java中规定,当一个方法执行结束之后不反悔任何数据的话,
返回值类型位置必须编写:“void关键字”。
*返回值类型若不是void,表示这个方法执行结束之后必须返回一个具体的数值。
当方法执行结束之后没有返回任何数据的话,编译器报错。怎么返回值,代码怎么写呢?"return 值;",并且要求
“值”的数据类型必须和“方法的返回值类型”一致,不然编译器报错。
*返回值类型是void的时候,在方法体当中不能编写"return 值;"值这样的语句。当时要注意可以编写"return;"这样的语句。
*只要带有return关键字的语句执行,return语句所在的方法结束。【不是jvm结束,是所在的方法结束】
c)、方法名:
*只要是合法的标识符就行
*方法名最好见名知意
*方法名最好是动词
*方法名首字母要求小写,后面每个单词首字母大写
d)、形式参数列表:简称形参
*形参是局部变量:int a; double b;float c;....
*形参的个数可以是:0~N个
*多个形参之间用"逗号"隔开
*形参中起决定性作用是形参的数据类型,形参的名字就是局部变量的名字。
*方法在调用的时候,实际给这个方法传递的真实数据被称为:实际参数,简称实参。
*实参列表和形参列表必须满足
-数量相同
-类型对应相同
例如:
方法定义
public static int sum(String 变量名,int 合法的标识符就行){ //(int a,int b)是形参列表
}
方法调用
sum("abc","def");编译器报错
sum("abc",20); //(10,20)是实参列表
e)、方法体必须由大括号括起来,方法体当中的代码有顺序,遵循自上而下的顺序依次执行。并且方法体由java语句构成,
每一个java语句以";"结尾。
方法只定义不去调用时是不会执行的。只有再调用的时候才会执行。
语法规则:《方法的修饰符列表当中有static》
类名.方法名(实参列表);<这是一条java语句,表示调用某个类的某个方法,传递这样的实参。>
//public表示公开的
//class表示定义类
//MethodTest03是一个类名
public class MethodTest03 //表示定义一个公开的类,起名MethodTest03,由于是公开的类,所以源文件名必须:MethodTest03.java
{
//类体
//类体中不能直接编写java语句,除声明变量之外
//方法出现再类体中
//public是公开的
//static是静态的
//void表示方法执行结束之后不反悔任何数据
//main是方法名:主方法
//(String[] args):形式参数列表,其中String[]是一种引用数据类型,args是一个局部变量的变量名
//所以以下只有args这个局部变量的变量名是随意的
//主方法就需要这样固定编写,这是程序的入口。【sun规定的,必须这样写】
public static void main(String[] args){
//这里的程序是一定会执行的
//main方法是JVM负责调用的,是一个入口位置
//从这里作为七点开始执行程序
//既然是这样,我们就可以再这里编写java语句来调用其它的方法
//调用MethodTest03的sumInt方法,传递两个实参
MethodTest03.sumInt(10,20);
//一个方法可以被重复使用,重复调用
int a = 100;
MethodTest03.sumInt(a,500);
//再次调用方法
int k = 10;
int f = 90;
MethodTest03.sumInt(k,f);
}
//自定义方法,不是程序入口
//方法作用:计算两个int类型数据的和,不要求返回结果,但是要求将结果直接输出到控制台
//修饰符列表:public static
//返回值类型:void
//方法名:sumInt
//形式参数列表:(int a,int b)
//方法体:
public static void sumInt(int a,int b){
System.out.println(a + " + " + b + " = " + (a + b));
}
}
//类型不同的时候,要求能够进行相应的自动类型转换。
public class MethodTest04
{
public static void main(String[] args){
//编译报错:参数数量不同
//MethodTest04.sum();
//编译报错:实参和形参的类型不是对应相同的
//MethodTest04.sum(true,false);
//可以
MethodTest04.sum(10L,20L);
//存在自动类型转换,int转换为long
MethodTest04.sum(10,20);
//编译报错:参数类型不是对应相同的。
//MethodTest04.sum(3.0,20);
//可以
MethodTest04.sum((long)3.0,20);
}
public static void sum(long x,long y){
System.out.println(x + " + " + y + " = " + (x + y));
}
}
a)、方法的修饰符列表中有static关键字,完整的调用方式是:类名.方法名(实参列表);
b)、但是,有的时候"类名."可以省略,什么情况下可以省略呢?
m1(){
m2();
}
m1方法和m2方法在同一个类体当中的时候,"类名."可以省略不写。
c)、建议在一个Java源文件中只定义一个class,比较清晰。
public class MethodTest05
{
public static void main(String[] args){
//调用方法
MethodTest05.m();
//对于方法的修饰符列表当中有static关键字的:"类名."可以省略不写
m();
//调用其他类【不是本类中的】方法
A.doOther();
//省略"类名."试试
//编译报错,"类名."省略之后,默认在当前类中找"doOther"方法,在当前类中该方法不存在。
//doOther();
}
public static void m(){
System.out.println("m method execute!");
//可以省略"类名."调用
m2();
//不想调用当前本类当中的m2方法,那这个时候就必须添加"类名."了。
A.m2();
}
public static void m2(){
System.out.println("m2 method execute!");
}
}
class A
{
public static void doOther(){
System.out.println("A's doOther method invoke!");
}
public static void m2(){
System.out.println("A's m2 method execute!");
}
}
main begin
m1 begin
m2 begin
m3 begin
m3 over
m2 over
m1 over
main over
对于当前的程序来说:
*main方法最先被调用,main方法也是最后一个结束。
*最后调用的方法是m3方法,该方法最先结束。
public class MethodTest06
{
public static void main(String[] args){
System.out.println("main begin");
m1();
System.out.println("main over");
}
public static void m1(){
System.out.println("m1 begin");
m2();
System.out.println("m1 over");
}
public static void m2(){
System.out.println("m2 begin");
m3();
System.out.println("m2 over");
}
public static void m3(){
System.out.println("m3 begin");
System.out.println("m3 over");
}
}
a)、返回值不是void的时候:
要求方法必须保证百分之百地执行"return 值;"这样的语句来完成值地返回。
没有这个语句编译器会报错。
b)、一个方法有返回值地时候,当我们调用这个方法的时候,方法返回了一个值,
对于调用者来说,这个返回值可以选择接收也可以选择不接收。
但是大部分情况下我们都是选择接收的。
public class MethodTest07
{
public static void main(String[] args){
//调用方法
m(60,3);//这里没有接收这个方法的返回数据
//这里接收以下方法执行结束之后的返回值
//采用变量接收,变量的数据类型需要和返回值的数据类型一致,或者可以自动类型转换。
//赋值运算符的右边先执行,将执行结果赋值左边的变量。
int i = m(60,3);
System.out.println(i);
long x = m(60,3);
System.out.println(x);
System.out.println(m(60,3));
}
/*
需求:
请定义并实现方法,该方法可以计算两个int类型数据的商,要求将最终的计算结果返回给调用者。
*/
//编译报错:缺少返回语句
/*
public static int m(int a , int b){
}
*/
//编译报错:缺少返回值
/*
public static int m(int a , int b){
return;
}
*/
//编译报错:方法定义的时候要求返回一个int类型,此时返回布尔类型,类型不兼容
/*
public static int m(int a , int b){
return true;
}
*/
public static int m(int a , int b){
return a / b;
}
}
*带有return关键字的java语句只要执行,所在的方法执行结束。
*在"同一个作用域"当中,return语句下面不能编写任何代码,因为这些代码永远都执行不到,所以编译报错。
public class MethodTest08
{
public static void main(String[] args){
int retuenValue = m();
System.out.println(retuenValue);
System.out.println(m());
}
public static int m(){
return 10 > 3 ? 1 : 0;
}
//编译报错:缺少返回语句,以下程序编译器认为无法百分之百保证"return 1;"会执行。
/*
public static int m(){
int a = 10;
if (a > 3)
{
return 1;
}
}
*/
//以下程序可以保证"return 1;"或者"return 0;"执行,编译通过。
public static int m1(){
int a = 10;
if (a > 3)
{
return 1;
//这里不能编写代码,编译报错,因为无法访问的语句。
//System.out.println("Hello!");
}
/*方法1:加else
else{
return 0;
}
*/
//方法2:因为如果if为真,return 1; 语句结束;为假,return 0;语句结束。【带有return关键字的java语句只要执行,所在的方法执行结束。】
//这里的代码可以执行到。
System.out.println("Hello!");
return 0;
}
}
"return;"语句出现在返回值为void的方法当中主要是为了用来结束当前方法。
public class MethodTest09
{
public static void main(String[] args){
m();
for (int j = 10;j > 0 ;j-- )
{
if (j == 2)
{
return; //结束的是main方法
}
System.out.println("j -->" + j);
}
System.out.println("Hello World!");
}
public static void m(){
//编译报错:不兼容的类型: 意外的返回值
//return 10;
//编译通过
//return;
for (int i = 0;i < 10 ;i++ )
{
if (i == 5)
{
//终止的是for循环
//break;
return; //终止的不是for循环,终止的是m()方法。所以"System.out.println("Hello World!");"这个语句无法执行。
}
System.out.println("i -->" + i);
}
System.out.println("Hello World!");
}
}
a)、方法只定义不调用,是不会执行的,并且在JVM中也不会给该方法分配"运行所属"的内存空间。
b)、在JVM内存划分上有这样三块主要的内存空间(当然,除了这三块之外还有其它的内存空间):
*方法区内存
*堆内存
*栈内存
c)、关于栈数据结构:
*栈:stack,是一种数据结构
*数据结构反应的是数据的存储形态。
*数据结构是独立的学科,不属于任何编程语言的范畴,只不过在大多数编程语言当中要使用数据结构。
*作为程序员需要提前精通:数据结构 + 算法【计算机专业必修一门课程】
*java程序员在不精通数据结构和算法的前提下,也可以进行java开发,因为java有一套庞大的类库支撑,
别人写好了,直接用。【java SE当中的集合章节,使用了大量的数据结构】
*常见的数据结构:
-数组
-队列
-栈
-二叉树
-哈希表/散列表
...
d)、方法执行的时候代码片段存在哪里?方法执行的时候执行过程的内存在哪里分配?
*方法代码片段属于.class字节码文件的一部分,字节码在类加载的时候,将其放到了方法区当中。
所以JVM中的三块主要的内存空间中方法区内存最先有数据。存放了代码片段。
*代码片段虽然在方法区内存当中只有一份,但是可以被重复调用。
每一次调用这个方法的时候,需要给该方法分配独立的活动场所,
在栈内存中分配。【在栈内存中分配方法运行的所属内存空间】
e)、方法在调用的瞬间,会给该方法分配内存空间,此时会在栈中发生压栈动作;
方法执行结束之后,给该方法分配的内存空间全部释放,此时发生弹栈动作。
*压栈:给方法分配内存
*弹栈:释放该方法的内存空间
f)、局部变量在"方法体"中声明,局部变量在运行阶段内存在栈中分配。
*方法执行过程中的内存分配
*方法的重载机制
*方法的递归调用
a)、//注意:再EditPlus当中,字体颜色为红色的表示一个类的名字,并且这个类是JavaSE类库中自带的。
//我们自定义的类MethodTest01,字体颜色是黑色,是标识符。
//其实JavaSE类库中自带的类,例如:String.class、System.class,这些类的类名也是标识符。
//只要是类名就一定是标识符。
//重点:方法调用的时候,在参数传递的时候,实际上传递的是变量中保存的那个"值"传过去了。
public class MethodTest01
{
public static void main(String[] args){
int a = 10;
int b = 20;
int retValue = sumInt(a,b);
System.out.println("retValue = " + retValue);
}
public static int sumInt(int i, int j){
int result = i + j;
int num = 3;
int retValue = divide(result,num);
return retValue;
}
public static int divide(int x,int y){
int z = x / y;
return z;
}
}
/*
画图依据:
a)、只要涉及到参数传递问题,传递的是变量中保存的值。
b)、画图的时候,必须遵循"方法自上而下的顺序依次执行"这个原则。
*/
以下代码不使用"方法重载机制",不适用overload,分析程序存在哪些缺点?
a)、sumInt,sumLong,sumDouble方法虽然功能不同,但是功能是相似的。都是求和。
再以下程序当中功能相似的方法,分别起了三个不同的名字,这对于程序员来说,调用方法的时候不方便,
程序员需要记忆更多的方法才能完成调用。【不方便】
b)、代码不美观
有没有这样的一种机制:
功能虽然不同,但是"功能相似"的时候,有没有这样一种机制,可以让程序员使用这些方法的时候就像在使用一个方法一样,
这样程序员以后编写代码比较方便,也不需要及记忆更多的方法名。代码也会美观。
有这种机制:方法重载机制/Overload
public class OverloadTest01
{
//入口
public static void main(String[] args){
//调用方法
int a = sumInt(1,2);
System.out.println(a);
double b = sumDouble(1.0,2.0);
System.out.println(b);
long c = sumLong(1L,2L);
System.out.println(c);
}
//定义一个方法,可以计算两个int数据类型的和
public static int sumInt(int a,int b){
return a + b;
}
//定义一个方法,可以计算两个double数据类型的和
public static double sumDouble(double a,double b){
return a + b;
}
//定义一个方法,可以计算两个long数据类型的和
public static long sumLong(long a,long b){
return a + b;
}
//最终希望达到的效果是:程序员再使用上面的三个相似的方法的时候,就像在用一个方法。
//Java支持这种机制【有些语言不支持,例如以后要学习的:JavaScript】
}
*程序员调用方法的时候比较方便,虽然调用的是不同的方法,但是就感觉在使用一个方法一样。
不需要记忆更多的方法名。
*代码美观。
前提:功能相似的时候方法名可以相同。但是,功能不同的时候,尽可能让这两个方法的方法名不同。
public class OverloadTest02
{
public static void main(String[] args){
//调用方法的时候就像再使用一个方法一样
//参数的类型不同,对应调用的方法不同。此时区分方法不在依靠方法名了,依靠的是参数的数据类型
System.out.println(sum(1,2));
System.out.println(sum(1.0,2.0));
System.out.println(sum(1L,2L));
}
//以下三个方法构成了方法重载机制
public static int sum(int a,int b){
System.out.println("sum(int,int)");
return a + b;
}
public static double sum(double a,double b){
System.out.println("sum(double,double)");
return a + b;
}
public static long sum(long a,long b){
System.out.println("sum(long,long)");
return a + b;
}
}
a)、方法重载又被称为:overload
b)、什么时候考虑使用方法重载?
*功能相似的时候,尽可能让方法名相同;但是功能不同/不相似的时候,尽可能让方法名不同。
c)、什么条件满足之后构成了方法重载?
*在同一个类当中
*方法名相同
*参数列表不同:
-数量不同
-顺序不同
-类型不同
d)、方法重载和什么有关系,和什么没有关系?
*方法重载和方法名+参数列表有关系
*方法重载和返回值类型无关
*方法重载和修饰符列表无关
public class OverloadTest03
{
//以下两个方法构成重载
public static void m1(){}
public static void m1(int a){}
//以下两个方法构成重载
public static void m2(int a,double b){}
public static void m2(double a,int b){}
//以下两个方法构成重载
public static void m3(int a){}
public static void m3(double a){}
//编译错误:以下不是方法重载,是发生了方法重复了
/*
public static void m4(int a,int b){}
public static void m4(int b,int a){}
*/
}
5)、方法重载的具体应用
public class OverloadTest04
{
public static void main(String[] args){
/*
System.out.println("Hello World!");
System.out.println(10);
System.out.println(true);
*/
工具.打印(10);
工具.打印(false);
工具.打印("abc");
工具.打印(3.0);
}
}
//自定义类,可封装,之后直接调用。
class 工具
{
public static void 打印(byte b){
System.out.println(b);
}
public static void 打印(short b){
System.out.println(b);
}
public static void 打印(int b){
System.out.println(b);
}
public static void 打印(long b){
System.out.println(b);
}
public static void 打印(float b){
System.out.println(b);
}
public static void 打印(double b){
System.out.println(b);
}
public static void 打印(boolean b){
System.out.println(b);
}
public static void 打印(char b){
System.out.println(b);
}
public static void 打印(String b){
System.out.println(b);
}
}
a)、什么是递归?
方法自身调用自身
b)、递归是很耗费栈内存的,递归算法可以不用的时候尽量别用。
c)、以下程序运行的时候发生了这样一个错误【不是异常,是错误error】
java.lang.StackOverflowError
栈内存溢出错误
错误发生无法挽回,只有一个结果,就是JVM停止工作。
d)、递归必须有结束条件,没有结束条件一定会发生栈内存一处错误。
e)、递归即使有了结束条件,即使结束条件正确,也可能会发生占内存溢出错误,因为递归地太深了。
public class RecursionTest01
{
//主方法
public static void main(String[] args){
System.out.println("main begin");
//调用doSome方法
doSome();
System.out.println("main over");
}
//以下的代码片段虽然只有一份,但是可以被重复地调用。
//并且只要调用doSome方法就会再栈内存中重新分配一块所属的内存空间。
public static void doSome(){
System.out.println("doSome begin");
doSome(); //这行代码不结束,下一行程序是不能执行的。
System.out.println("doSome over");
}
}
public class RecursionTest02
{
public static void main(String[] args){
/*
//1~10的和
int b = 0;
for (int i = 1;i <= 10 ;i++ )
{
b += i;
}
System.out.println(b);
*/
//直接调用方法即可
java.util.Scanner s = new java.util.Scanner(System.in);
int n = s.nextInt();
int result = sum(n);
System.out.println("sum = " + result);
}
//单独定义一个方法,这是一个独立的功能,可以完成1~N的求和
public static int sum(int n){
int result = 0;
for (int i = 1;i <= n ;i++ )
{
result += i;
}
return result;
}
}
--> 1 + 2 + 3 + 4
--> 4 + 3 + 2 + 1:n的最初值是4,建议采用这种方式
public class RecursionTest03
{
public static void main(String[] args){
//1~4的和
int n = 4;
int retValue = sum(n);
System.out.println(retValue);
}
public static int sum(int n){
if (n == 1) //结束条件
{
return 1;
}
return n + sum(n - 1);
}
}
//n + sum(n - 1)
//4 + sum(3)
//4 + 3 + sum(2)
//4 + 3 + 2 + sum(1)
//4 + 3 + 2 + 1
public class RecursionTest04
{
public static void main(String[] args){
java.util.Scanner s = new java.util.Scanner(System.in);
int retResult = mul(s.nextInt());
System.out.println(retResult);
}
/*
//不使用递归方法
public static int mul(int n){
int result = 1;
for (int i = 1;i <= n ;i++ )
{
result *= i; //result = result * i
}
return result;
}
*/
//使用递归方法
//不懂递归的同学,必须将以下的程序记住,因为面试题出现的几率太高了。
public static int mul(int n){
if (n == 1)
{
return 1;
}
return n * mul(n - 1);
}
}
-面向过程:主要关注点是:实现的具体过程,因果关系【集成显卡的开发思路】
*优点:对于业务逻辑比较简单的程序,可以达到快速开发,前期投入成本较低。
*缺点:采用面向过程的方式开发很难解决非常复杂的业务逻辑,
另外面向过程的方式导致软件元素之间的"耦合度"非常高,
只要其中一环出问题,整个系统受到影响,导致最终的软件扩展力差。
其次,没有独立体的概念,所以无法达到组件复用。
-面向对象:主要关注点是:主要关注对象【独立体】能完成哪些功能。【独立显卡的开发思路】
*优点:耦合度低,扩展能力强。更容易解决显示世界当中更复杂的业务逻辑,组件复用性强。
*缺点:前期投入成本较高,需要进行独立体的抽取,大量的系统分析与设计。
-c语言是纯面向过程、c++半面向对象、Java纯面向对象。
-当前出现的一些新的编程语言多数都是面向对象的。人是以面向对象的方式认识世界的。
-面向对象更符合人的思维方式。
-封装
-继承
-多态
所有面向对象的编程语言都有这三大特征。
采用面向对象的方式开发一个软件,生命周期当中:【整个声明周期中贯穿使用OO面向对象的方式】
*面向对象的分析:OOA
*面向对象的设计:OOD
*面向对象的编程:OOP
-什么是类?
*类是现实世界当中不存在的,是一个模板,是一个概念。是人类大脑思考抽象的结果。
*类表示了一类事物
*再现实世界当中,对象A与对象B之间具有共同特征,进行抽象总结出一个模板,这个模板被称为类。
-什么是对象?
*对象是实际存在的个体。现实世界当中实际存在。
-描述一下整个软件开发的过程:
*程序员先观察世界,从现实世界当中寻找对象
*寻找了N多个对象之后,发现所有的对象都有一个共同特征
*程序员再大脑中形成了一个模板【类】
*Java程序员可以通过Java代码来表述一个类
*Java程序中有了类的定义
*然后通过类就可以创建对象
*有了对象之后,可以让对象直接写作起来形成一个系统。
-对象--【抽象】-->类
-对象又被称为实例/instance
-类--【实例化】-->对象
-重点:
*类描述的是对象的共同特征。
共同特征例如:身高特征
这个身高特征在访问的时候,必须先创建对象,通过对象去访问这个特征。
因为这个特征具体到每个对象上之后,值不同。有的对象身高1.80,有的对象升高2.80
-一个类主要描述什么信息呢?
*一个类主要描述的是:状态 + 动作。
状态信息:名字、身高、性别、年龄
动作信息:吃、唱歌、跳舞、学习
状态-->一个类的属性
动作-->一个类的方法
*定义类的语法:
[修饰符列表] class 类{
属性;//描述对象的状态信息
方法;//描述对象的动作信息
}
注意:状态和动作作用到某个对象上之后,发现最终的结果可能不一样;
对象和对象之间有共同特征,但是具体到对象之后有数据上的差异。
语法结构:
[修饰符列表] class 类名{
属性;
方法;
}
学生类,描述所有学生对象的共同特征:
学生对象有哪些状态信息:
*学号【int】
*姓名【String】
*性别【boolean】
*年龄【int】【年龄是一个属性,年龄是一个数据,是数据就应该有一个数据类型】
*住址【String】
...
学生对象有哪些动作信息:
*吃饭
*睡觉
*学习
*娱乐
*唱歌
*跳舞
...
重点:属性通常是采用一个变量的方式来定义的。
int num;
int age;
String name;
String address;
boolean sex;
*java语言当中的两种数据类型:
-基本数据类型:
byte
short
int
long
float
double
boolean
char
-引用数据类型
String.class sun提供的
System.class
Student.class 程序员自定义的
User.class
Product.class
Customer.class
......
int age = 10;
String userName = "zhangsan";
student s = ???;
-Java语言中所有的class都属于引用数据类型。
a)、JVM(java虚拟机)主要包括三块内存空间,分别是:栈内存,堆内存,方法去内存。
b)、堆内存和方法区内存各有一个,一个线程一个栈内存。
c)、方法调用的时候,该方法所需要的内存空间在栈内存中分配,称为压栈;
方法执行结束之后,该方法所属的内存空间释放,称为弹栈。
d)、栈中主要存储的是方法体当中的局部变量。
e)、方法的代码片段以及整个类的代码片段都被存储到方法区内存当中,
在类加载的时候这些代码片段会被载入。
f)、在执行程序的过程中使用new运算符创建java对象,存储在堆内存当中;
对象内部有实例对象,所以实例对象存储在堆内存当中。
g)、变量分类:
-局部变量【方法体中声明】
-成员变量【方法体外声明】
*实例变量【前面修饰符没有static】
*静态变量【前面修饰符中有static】
h)、静态变量存储在方法区内存当中【先背会】
i)、三块内存当中变化最频繁的是栈内存,最先有数据的是方法区内存,垃圾回收器主要针对的是堆内存。
j)、垃圾回收器【自动垃圾回收机制、GC机制】什么时候会考虑将某个java对象的内存回收呢?
*当堆内存当中的java对象成为垃圾数据的时候,会被垃圾回收器回收。
*什么时候堆内存中的java对象会变成垃圾呢?
-没有更多的引用指向它的时候,因为访问对象只能通过引用的方式访问。
//学生类是一个模板
//描述了所有学生的共同特征【状态 + 行为】
//当前类只描述学生的状态信息【属性】
//Student是一个类,属于应用数据类型,这个类型名就是:Student
public class Student
{
//类体 = 属性 + 方法
//属性【存储数据采用变量形式】
//由于变量定义在类体当中,方法体之外,这种变量被称为成员变量
//
//所有学生都有学号
//但是每一个血汗俄国的学号都是不同的
//所以要访问这个学号必须先创建对象,通过对象去访问学号信息
//学号信息不能通过"类"去访问,所以这种成员变量又被叫做:实例变量
//对象又被称为实例,实例变量又被称为对象变量。【对象级别的变量】
//不创建对象,这个num变量的内存空间室不存在的,只有创建了对象,这个num变量内存空间才会创建。
int num;
//姓名
String name;
//年龄
int age;
//性别
boolean sex;
//住址
String addr;
}
//成员变量没有手动赋值的话,系统会赋默认值
//默认值
/*
数据类型 默认值
---------------------------------------
byte,short,int,long 0
float,double 0.0
boolean false
char \u0000
引用数据类型 null 空值
*/
public class OOTest01
{
public static void main(String[] args){
//int是基本数据类型
//i是一个变量名
//10是一个int类型的字面值
int i = 10;
//通过一个类可以实例化N个对象
//实例化对象的语法:new 类型();
//new室java语言当中的一个运算符
//new运算符的作用是创建对象,在JVM堆内存当中开辟心得内存空间
//方法区内存:在类加载的时候,class字节码片段被加载到该内存空间当中。
//栈内存(局部变量):方法代码片段执行的时候,会给该方法分配内存空间,在栈内存中压栈。
//堆内存:new的对象在堆内存中存储
//Student是一个引用型数据变量
//s是一个变量名
//new Student()是一个学生对象
//Student s;是一个局部变量【在栈内存中存储】
//变量s称为引用
//什么是对象?在new运算符在堆内存开辟的内存空间称为对象
//什么是引用?引用是一个变量【变量包括:局部变量和成员变量】,只不过这个变量中保存了另一个java对象的内存地址。
//java语言当中,程序员不能直接操作堆内存,java中没有指针。不像c语言。
//java语言当中,程序员只能通过"引用"去访问堆内存当中对象内部内部的实例对象
Student s = new Student();
//访问实例变量的语法格式:
// 读取数据:引用.变量名
// 修改数据:引用.变量名 = 值;
/*
int stuNum = s.num;
String stuName = s.name;
int stuAge = s.age;
boolean stuSex = s.sex;
String stuAddr = s.addr;
System.out.println("学号 = " + stuNum); //0
System.out.println("姓名 = " + stuName); //null
System.out.println("年龄 = " + stuAge); //0
System.out.println("性别 = " + stuSex); //false
System.out.println("地址 = " + stuAddr); //null
*/
System.out.println("学号 = " + s.num); //0
System.out.println("姓名 = " + s.name); //null
System.out.println("年龄 = " + s.age); //0
System.out.println("性别 = " + s.sex); //false
System.out.println("地址 = " + s.addr); //null
System.out.println("------------");
s.num = 10;
s.name = "jack";
s.age = 20;
s.sex = true;
s.addr = "南京";
System.out.println("学号 = " + s.num); //10
System.out.println("姓名 = " + s.name); //jack
System.out.println("年龄 = " + s.age); //20
System.out.println("性别 = " + s.sex); //true
System.out.println("地址 = " + s.addr); //南京
System.out.println("------------");
//再通过类实例化一个全新的对象
Student stu = new Student();
System.out.println("学号 = " + stu.num); //0
System.out.println("姓名 = " + stu.name); //null
System.out.println("年龄 = " + stu.age); //0
System.out.println("性别 = " + stu.sex); //false
System.out.println("地址 = " + stu.addr); //null
//编译报错,num这个实例变量不能直接采用"类名"的方式访问
//因为num是实例变量,对象级别的变量,变量存储在java对象内部,必须现有对象
//通过对象才能访问num这个实例变量,不能直接通过"类名"访问
//System.out.println(Student.num);
}
}
/*
局部变量在栈内存中存储
成员变量中的实例变量在堆内存的java对象内部存储
实例对象是一个对象一份,100个对象100份
*/
//用户类
public class User
{
//属性【以下都是成员变量之实例变量】
//用户编号
//int是一种基本数据类型:整数型
//num是一个实例变量
int num;
//用户名
//String是一种引用数据类型:代表字符串
//name是一个实例变量
//name是一个引用
String name;
//家庭住址
//Address是一种引用数据类型:代表家庭住址
//addr是一个实例变量
//addr是一个引用
Address addr;
}
//家庭住址类
public class Address
{
//属性【成员变量之实例变量】
//城市
//String是一种引用数据类型
//city是一个实例变量
//city是一个引用:保存内存地址的一个变量,该变量保存内存地址指向堆内存当中的对象。
String city;
//街道
String street;
//邮编
String zipcode;
}
//测试类
public class OOTest02
{
public static void main(String[] args){
//创建User对象
//u是局部变量
//u保存内存地址只想堆内存的User对象
User u = new User();
//输出User对象中实例对象的值
System.out.println(u.num); //0
System.out.println(u.name);//null
System.out.println(u.addr);//null
System.out.println("------------");
//修改User对象内部实例变量的值
u.num = 10;
u.name = "jack";
//编译报错:不兼容的类型: String无法转换为Address
//u.addr = "南京";
u.addr = new Address();
//在main方法中目前只能看到一个引用"u"
//一切都是只能通过"u"来进行访问
u.addr.city = "南京";
u.addr.street = "南京路";
u.addr.zipcode = "223900";
System.out.println(u.num);
System.out.println(u.name);
System.out.println(u.addr.city);
System.out.println(u.addr.street);
System.out.println(u.addr.zipcode);
}
}
public class OOTest03
{
public static void main(String[] args){
//u是引用
//u是局部变量
User u = new User();
//上一个版本中编写的
//u.addr = new Address();
//a是引用
//a是局部变量
Address a = new Address();
u.addr = a;
System.out.println(u.addr.city); //null
a.city = "南京";
System.out.println(a.city); //南京
System.out.println(u.addr.city); //南京
}
}
//丈夫类
public class Husband
{
//姓名
String name;
//丈夫对象当中含有妻子对象的内存地址【妻子引用】
Wife w;
}
//妻子类
public class Wife
{
//姓名
String name;
//妻子对象中含有丈夫对象的内存地址【丈夫引用】
Husband h;
}
public class OOTest04
{
public static void main(String[] args){
//创建一个丈夫对象
Husband huangxiaoming = new Husband();
huangxiaoming.name = "黄晓明";
//创建一个妻子对象
Wife baby = new Wife();
baby.name = "baby";
//结婚【能通过丈夫找到妻子,通过妻子也可以找到丈夫】
huangxiaoming.w = baby;
baby.h = huangxiaoming;
//得到以上"黄晓明"妻子的名字
System.out.println(huangxiaoming.name + "的妻子是" + huangxiaoming.w.name);
}
}
//顾客类
public class Customer
{
//id
int id;
}
//测试类
public class OOTest05
{
public static void main(String[] args){
Customer c = new Customer();
System.out.println(c.id); //0
c = null;
//以下程序编译可以通过,因为符合语法
//运行出现空指针异常
//空引用访问"实例"相关的数据一定会出现空指针异常
//java.lang.NullPointerException
System.out.println(c.id);
}
}
//"实例"相关的数据表示:这个数据访问的时候必须有对象的参与。这种数据就是实例相关的数据。
定义一个计算机类【电脑/笔记本】,计算机类有那些属性:
*品牌
*型号
*颜色
定义一个学生类
*学号
*姓名
*学生有一台笔记本电脑
请编写程序来表示以上的类,然后分别将类创建为对象,
对象数量不限,然后让其中的一个学生去使用其中的一台笔记本电脑。
然后编译并运行,并且将整个执行过程采用图形的方式描述出来。
public class OOTest06
{
public static void main(String[] args){
//创建一个学生对象
Student stu = new Student();
stu.name = "XGJ";
stu.num = 10;
//创建一个电脑对象
Computer com = new Computer();
com.model = "Y7000P";
com.color = "Black";
stu.c = com;
com.s = stu;
System.out.println(stu.name + "的电脑型号是:" + stu.c.model);
System.out.println(stu.name + "的电脑颜色是:" + stu.c.color);
System.out.println("--------------------------");
System.out.println(com.model + "的主人的姓名是:" + com.s.name);
System.out.println(com.model + "的主人的学号是:" + com.s.num);
//换电脑了
//重新创建一个电脑
Computer com2 = new Computer();
com2.model = "Y9000P";
com2.color = "Black";
stu.c = com2;
System.out.println("--------------------------");
System.out.println(stu.name + "的电脑型号是:" + stu.c.model);
System.out.println(stu.name + "的电脑颜色是:" + stu.c.color);
}
}
class Student //学生类
{
//学号
int num;
//姓名
String name;
//有一台电脑
Computer c;
}
class Computer //电脑类
{
//品牌
String brand;
//型号
String model;
//颜色
String color;
Student s;
}
一个User对象表示一个用户,用户的年龄值不可能为负数,
以下程序当中年龄值为负数 程序运行的时候并没有报错,这是当前程序存在的缺陷。
面向对象包括三大特征:
- 封装
- 继承
- 多态
当前主要讲解的是封装机制。为什么要封装?封装有什么好处?
封装的好处:
a)、封装之后,对于那个事物来说,看不到事物比较复杂的一面,只能看到该事物简单的那一面。
复杂性封装,对外提供简单的操作入口。照相机就是一个很好的封装案例,照相机的实现原理非常复杂,
但是对于使用照相机的人来说,操作起来是非常方便的,非常便捷的。还有像电视机也是封装的,
电视机内存实现非常复杂,但是对于使用者来说不需要关心内部的实现原理,只需要会操作遥控器就行。
b)、封装之后才会形成真正的"对象",真正的"独立体"。
c)、封装就意味着以后的程序可以重复使用,并且这个事物适应性比较强,在任何场合都可以使用。
d)、封装之后对于事物本身提高了安全性。【安全级别高】
a)、所有属性私有化,使用Private关键字进行修饰,private表示私有的,
修饰的所有数据只能在本类中访问。
b)、对外提供简单的操作入口,也就是说以后外部程序想要访问age属性,
必须通过这些简单的入口进行访问:
*对外提供两个公开的方法,分别是set方法和get方法
*想修改age属性调用set方法
*想读取age属性调用get方法
c)、set方法的命名规范:
public void set + 属性名首字母大写(形参){
}
public void setAge(int a){
age = a;
}
d)、get方法命名规范:
public int getAge(){
return age;
}
回想一下,一个属性通常访问的时候包括几种访问形式?
*第一种方式:想读取这个属性的值,读取get
*第二种方式:想修改这个属性的值,修改set
需要大家先背会以下内容:
*setter and getter方法没有static关键字
*有static关键字修饰的方法怎么调用:类名.方法名(实参);
*没有static关键字修饰的方法怎么调用:引用.方法名(实参);
//用户类
public class User {
//属性私有化
private int age;
//set方法没有返回值,因为set方法只负责修改数据
/*
public void setAge(int age) {
age = age;//java有就近原则,这里其实并没有给age属性赋值,这里的age都是局部变量age
}
*/
//setter
public void setAge(int a){
//编写业务逻辑代码进行安全控制
//age = a;
if(a < 0 || a > 150){
System.out.println("对不起,您提供的年龄不合法");
return;
}
//程序执行到这里的话,说明a是合法的,则进行赋值运算
age = a;
}
//getter
public int getAge(){
return age;
}
}
//测试
public class UserTest {
public static void main(String[] args) {
//创建User对象
User user = new User();
//编译报错,age属性私有化,在外部程序中不能直接访问
//从此之后age属性非常安全,但是有点太安全了。
//对于目前的程序来说,age属性彻底在外部访问不到了。
//System.out.println(user.age);
//修改
user.setAge(-100); //对不起,您提供的年龄不合法
//读取
System.out.println(user.getAge()); //0
}
}
a)、构造方法又被称为构造函数/构造器/Constructor
b)、构造方法语法结构:
[修饰符列表] 构造方法名(形式参数列表){
构造方法体;
}
c)、回顾普通方法的语法结构:
[修饰符列表] 返回值类型 方法名(形式参数列表){
方法体;
}
d)、对于构造方法来说,"返回值类型"不需要指定,并且也不能写"void",
只要写上"void"这个方法就成为普通方法了。
e)、对于构造方法来说,构造方法的方法名必须和类名保持一致。
f)、构造方法的作用?
构造方法存在的意义是,通过构造方法的调用,可以创造对象。
g)、构造方法怎么调用?
-普通方法是这样调用的:方法修饰符中有static的时候:类名.方法名(实参列表);
方法修饰符中没有static的时候:引用.方法名(实参列表);
-new 构造方法名(实参列表);
h)、构造方法调用执行之后,有返回值吗?
每一个构造方法实际上执行结束之后都有返回值,但是这个"return 值;"这样的语句不需要写;
构造方法结束的时候java程序自动返回值,并且返回值类型是构造方法所在类的类型。
由于构造方法的返回值类型就是类本身,所以返回值类型不需要编写。
i)、当一个类中没有定义任何构造方法的话,系统默认给该类提供一个无参数的构造方法
这个构造方法被称为缺省构造器。
j)、当一个类显示的将构造方法定义出来了,那么系统则不再默认为这个类提供缺省构造器;
建议开发中手动地为当前类提供无参数构造方法,因为无参数构造方法太常用了。
k)、构造方法支持重载机制,在一个类当中编写多个构造方法,
这多个构造方法显然以及构成方法重载机制。
//用户类
public class User {
public User(){
System.out.println("User's Default Construotor Invoke");
}
//有参数的构造方法
public User(int a) {
System.out.println("带有int类型参数的构造器");
}
public User(String name){
System.out.println("带有String类型参数的构造器");
}
public User(int i,String name){
System.out.println("带有int,String类型参数的构造器");
}
}
//方法构造测试
public class ConstructorTest01 {
public static void main(String[] args) {
//创建User对象
//调用User类的构造方法来完成对象的创建
//以下程序创建了4个对象,只要构造函数调用就会创建对象,并且一定是在"堆内存"中开辟内存空间。
User u1 = new User(); //User's Default Construotor Invoke
User u2 = new User(10); //带有int类型参数的构造器
User u3 = new User("a"); //带有String类型参数的构造器
User u4 = new User(10,"a"); //带有int,String类型参数的构造器
//调用修饰符中有static的普通方法【两种方式】
ConstructorTest01.doSome();
doSome();
//调用修饰符中没有static的普通方法,先创建对象,在引用.方法名();
ConstructorTest01 c = new ConstructorTest01();
c.doOther();
}
public static void doSome(){
System.out.println("do some!");
}
public void doOther(){
System.out.println("do other!");
}
}
1、创建对象
2、创建对象的同时,初始化实例变量的内存空间。【给实例变量赋值】
成员变量之实例变量,属于对象级别的变量,这种变量必须先有对象才能有实例变量
实例变量没有手动复制的时候,系统默认赋值,那么这个系统默认赋值狮子啊什么时候完成的呢?
是在类加载的时候吗?不是,因为类加载的时候只加载了代码片段,还没来得及创建对象,
所以此时实例变量并没有初始化。
实际上,实例变量的内存空间实在构造方法执行过程当中完成开辟,完成初始化的。
系统在默认赋值的时候,也是在构造方法执行过程当中完成的赋值。
实例变量是存储在JVM的堆内存java对象内部。
//账户类
public class Account {
//账号
private String actno; //实例变量/对象变量,也就是说,必须先有对象才能有对应的实例变量。
//余额
private double balance;
//无参数构造器
public Account(){
//初始化实例变量的内存空间
}
public Account(String s){
actno = s;
//balance = 0.0;
}
public Account(double d){
balance = d;
}
public Account(String s,double d){
actno = s;
balance = d;
}
public String getActno() {
return actno;
}
public void setActno(String actno) {
this.actno = actno;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
}
//账户测试类
public class ConstructorTest02 {
public static void main(String[] args) {
//在myeclipse当中怎么查看范文的是哪个属性,查看访问的是哪个方法?
//按住ctrl键,鼠标移动到查看到元素上,出现下划线的时候开始单机
//另外,在一个类当中元素过多,想快速查看,在当前类中使用ctrl + o快捷键,
//然后输入要查找的元素名称,该名称不一定的输入全名称。
//创建对象
Account act1 = new Account();
//给对象的属性赋值
act1.setActno("XGJ");
act1.setBalance(2000.0);
System.out.println("账户:" + act1.getActno()); //XGJ
System.out.println("余额:" + act1.getBalance()); //2000.0
Account act2 = new Account("111");
System.out.println("账户:" + act2.getActno()); //111
System.out.println("余额:" + act2.getBalance()); //0.0
Account act3 = new Account(2000.0);
System.out.println("账户:" + act3.getActno()); //null
System.out.println("余额:" + act3.getBalance()); //2000.0
Account act4 = new Account("XGJ",2000.0);
System.out.println("账户:" + act4.getActno()); //XGJ
System.out.println("余额:" + act4.getBalance()); //2000.0
}
}
*对象:目前在使用new运算符在堆内存中开辟的内存空间称为对象
*引用:是一个变量,不一定是局部变量,还可能是成员变量。引用保存了内存地址,指向了堆内存中的对象。
*所有访问实例相关的数据,都需要通过"引用."的方式访问,因为只有通过引用才能找到对象。
*只有一个空的引用,访问对象的实例相关的数据会出现空指针异常。
主要研究和学习的是方法在调用的时候,涉及到参数传递的问题,到底是怎么传递数据的呢?【值传递】
*Java语言当中方法调用的时候涉及到参数传递的问题,参数传递实际上传递的是变量中保存的具体值。
int i = 10;
add(i); 等同于add(10);
public class Test01 {
public static void main(String[] args) {
int i = 10; //add方法调用的时候,给add方法传递了一个变量i,到底传的是什么?
add(i);
System.out.println("main --> " + i); //10
}
public static void add(int i){
i++;
System.out.println("add --> " + i); //11
}
}
*最终结论:
方法调用的时候,涉及到参数传递的问题,传递的时候,java只遵循一种语法机制,就是将变量中保存的
"值"传递过去了;只不过有的时候这个"值"是一个字面值10,有的时候这个"值"是java对象的
内存地址0x1234。
public class Test02 {
public static void main(String[] args) {
User u = new User(20);
//u = 0x1234
//传递u给add方法的时候,实际上传递的是u变量中保存的值,
//只不过这个值是一个java对象的内存地址。
add(u); //等同于add(0x1234);
System.out.println("main --> " + u.age); //21
}
private static void add(User u) {
u.age++;
System.out.println("u.age --> " + u.age); //21
}
}
class User{
//实例变量
int age;
//构造方法
public User(int i) {
age = i;
}
}
a)、this是一个关键字,翻译为:这个
b)、this是一个引用,this是一个变量,this变量中保存了内存地址指向了自身,
this存储在JVM堆内存java对象内部。
c)、创建100个java对象,每一个对象都有this,也就是说有100个不同的this。
d)、this出现在"实例方法"【不带static】当中,this指向当前正在执行这个动作的对象。
e)、this在多数情况下是可以省略不写的。
f)、this不能使用在带有static的方法当中。
public class Customer {
//姓名【堆内存的对象内部存储,所以访问该数据的时候,必须先创建对象,通过引用方式访问】
String name; //实例变量:必须采用"引用."的方式访问。
//构造方法
public Customer(){
}
//不带有static关键字的一个方法
//顾客购物的行为
//每一个顾客购物最终的结果是不一样的
//所以购物这个行为是属于对象级别的行为
//由于每一个对象再执行购物这个动作的时候最终结果不同,所以购物这个动作必须有"对象那个"参与。
//重点:没有static关键字的方法被称为"实例方法",实例方法怎么访问?"引用."
//重点:没有static关键字的变量被称为"实例变量"
//注意:当一个行为/动作执行的过程中是需要对象参与的,那么这个方法一定要定义为"实例方法",
不要带static关键字。
//一下方法定义为实例方法,因为每一个顾客在真正购物的时候,最终结果是不同的。
所以这个动作在完成的收必须要对象的参与。
public void shopping(){
//当张三在购物的时候,输出张三在购物
//当李四在购物的时候,输出李四在购物
//System.out.println("xxx在购物!");
//由于name是一个实例变量,所以这个name访问的时候一定访问的是当前对象的name。
//所以多数情况下"this."是可以省略的。
//System.out.println(name + "在购物!");
System.out.println(this.name + "在购物!"); //this就代表当前在执行这个动作的对象,
c1在执行就代表c1;c2在执行就代表c2。
}
public static void doSome(Customer a){
//这个执行过程当中没有"当前对象",因为带有static的方法是通过"类名."的方式访问的。
//或者说这个"上下文"当中没有"当前对象",自然也不存在this
//(this代表的是当前正在执行这个动作的对象)
//以下程序为什么会编译错误呢?
//doSome方法调用不是对象去调用,是一个类名去调用,执行过程中没有"当前对象"
//那么是一个"实例变量",一下代码的含义是:访问当前对象的name,没有当前对象,
//自然也不能访问当前对象的name
//System.out.println(name);
//static的方法调用不需要对象,直接使用类名,所以执行过程中没有当前对象,
//所以不能使用this
a.name = "王二麻";
}
public static void doOther(){
//假设想访问name这个实例变量的话应该怎么做?
//System.out.println(name);//编译报错
//可以采用以下方案,但是以下方案,绝对不是访问当前对象的name
//创建对象
Customer c = new Customer();
System.out.println(c.name); //这里访问的name是c引用指向的name
}
public class CustomerTest {
public static void main(String[] args) {
//创建Customer对象
Customer c1 = new Customer();
c1.name = "张三";
c1.shopping();
//再创建Customer对象
Customer c2 = new Customer();
c2.name = "李四";
c2.shopping();
//调用doSome方法(修饰符列表上有static)
//采用"类名."的方法访问,显然这个方法在执行的时候不需要对象的参加
Customer c3 = new Customer();
Customer.doSome(c3);
System.out.println(c3.name);
//调用doOther
Customer.doOther();
}
}
}
public class ThisTest {
//实例变量(引用.的方式访问)
int num = 10;
//带有static的方法
//JVM负责调用main方法,JVM怎么调用?
//ThisTest.main(String[]);
public static void main(String[] args) {
//没有但前对象this
//以下代码什么意思?
//访问"当前对象"的num属性
//System.out.println(num); //编译错误
//System.out.println(this.num); //编译错误
//想访问num怎么办?
ThisTest t = new ThisTest();
System.out.println(t.num);
}
}
最终结论:
在带有static的方法当中不能"直接"访问实例变量和实例方法。
因为实例变量和实例方法都需要对象的存在。
而static的方法当中是没有this的,也就是说当前对象是不存在的。
自然也是无法访问当前对象那个的实例变量和实例方法。
public class ThisTest {
//带有static
//主方法
public static void main(String[] args) {
//调用doSome方法
doSome();
//调用doOther方法
//实例方法必须先创建对象,通过引用.方式访问。
//doOther是实例方法,实例方法的调用必须有对象的存在
//以下代码的含义:调用当前对象的doOther方法,但是由于main方法中没有this,
//所以以下方法不能调用。doOther();
ThisTest t = new ThisTest();
t.doOther();
t.run();
}
//带有static
public static void doSome(){
System.out.println("do some!");
}
//实例方法
public void doOther(){
System.out.println("do other!");
}
//实例方法
//run是实例方法,调用run方法的一定是有对象存在的;一定是先创建了一个对象才能调用run方法
public void run(){
//在大括号中的代码执行过程当中一定是存在"当前对象"的。
//也就是说这里一定有"this"的。
System.out.println("run execute!");
//doOther是一个实例方法,实例方法调用必须有对象那个的存在
//以下代码表示的含义就是:调用当前对象的doOther方法
//doOther(); //省略写法,this.大部分情况下都是可以省略的
this.doOther(); //比较完整的写法
}
}
public class ThisTest2 {
//实例变量
String name;
//实例方法
public void doSome(){
this.name = "lisi";
System.out.println("do some!");
}
//主方法带有static
public static void main(String[] args) {
//这里没有this
/*
System.out.println(name);
doSome();
System.out.println(this.name);
this.doSome();
*/
ThisTest2 t = new ThisTest2();
t.name = "zhangsna";
System.out.println(t.name);
t.doSome();
System.out.println(t.name);
}
}
用来区分局部变量和实例变量的时候,"this."不能省略。
//用户类
public class User {
//属性
private int id; //实例变量
private String name;
//构造函数
public User(){
}
/*
public User(int a,String b){
id = a;
name = b;
}
*/
public User(int id,String name){
this.id = id;
this.name = name;
}
//setter and getter
/*
public void setId(int a){
id = a;
}
*/
public void setId(int id){
//id = id; //这个程序的id和实例变量id无关,不能采用这中方式
//等号后面的This.id是实例变量id
//等号后面的id是局部变量id
this.id = id;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class UserTest {
public static void main(String[] args) {
User u1 = new User(20,"zhangsan");
System.out.println(u1.getId()); //20
System.out.println(u1.getName()); //zhangsan
//想修改名字
u1.setName("lisi");
System.out.println(u1.getName()); //lisi
}
}
this可以用在哪里?
*可以使用在实例方法当中,代表当前对象【语法格式:this.】
*可以使用在构造方法当中,通过单签的构造方法调用其他的构造方法【语法格式:this(实参);】
重点:this(实参);这种语法只能出现在构造函数第一行。
public class Data {
//属性
private int year;
private int month;
private int day;
//构造函数
public Data(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
/*
需求:当程序员调用以下无参数的构造方法的时候,默认创建的日期是"1970-1-1"
*/
public Data(){
/*
this.year = 1970;
this.month = 1;
this.day = 1;
*/
//以上代码可以通过调用另一个方法来完成
//但前提是不能创建新的对象;以下代码表示i创建了一个全新的对象。
//new Data(1970,1,1);
//需要采用以下的语法来完成构造方法的调用
//这种方式不会创建新的java对象,但同时又可以达到调用其他的构造方法。
this(1970,1,1);
}
//setter and getter
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 void print(){
System.out.println(this.year + "年" + this.month + "月" + this.day + "日");
}
}
public class DataTest {
public static void main(String[] args) {
//创建日期对象1
Data time1 = new Data();
//System.out.println("当前日期为:" + time1.getYear()+ " - " +
time1.getMonth() + " - " + time1.getDay()); //当前日期为:1970 - 1 - 1
time1.print();
//创建日期对象2
Data time2 = new Data(2021,12,4);
time2.print();
}
}
public class Test {
//没有static的变量
int i = 10;
//带有static的方法
public static void doSome(){
System.out.println("do some!");
}
//没有static的方法
public void doOther(){
System.out.println("do other!");
}
//带有static的方法
public static void method1(){
//调用doSome方法
//使用完整的方式调用
Test.doSome();
//使用省略的方式调用
doSome();
//调用doOther方法
//使用完整的方式调用
Test t1 = new Test();
t1.doOther();
//使用省略的方式调用【无】
//访问i
//完整的方式访问
System.out.println(t1.i);
//省略的方式访问【无】
}
//没有static的方法
public void method2(){
//调用doSome方法
//使用完整的方式调用
Test.doSome();
//使用省略的方式调用
doSome();
//调用doOther方法
//使用完整的方式调用
this.doOther();
//使用省略的方式调用
doOther();
//访问i
//完整的方式访问
System.out.println(this.i);
//省略的方式访问
System.out.println(i);
}
//主方法
public static void main(String[] args) {
//要求在这里编写程序调用method1
//使用完整的方式调用
Test.method1();
//使用省略的方式调用
method1();
//要求在这里编写程序调用method2
//使用完整的方式调用
Test t2 = new Test();
t2.method2();
//使用省略的方式调用【无】
}
}
空引用访问实例相关的数据,因为实例相关的数据就是对象相关的数据,
这些数据在访问的时候,必须有对象的参与,当空引用的时候,对象不存在,
访问这些示例数据一定会出现空指针异常。
*实例相关的数据包括:
实例变量【对象需要存在】
实例方法【对象需要存在】
public class Test {
public static void main(String[] args) {
Test.doSome();
doSome();
Test t = new Test();
t.doSome();
//引用是空
t = null;
//带有static的方法既可以采用类名的方式访问,也可以采用引用的方式访问
//但是即使采用引用的方式去访问,实际上执行的时候和引用指向的对象无关。
//在myeclipse中开发的时候,使用引用的方式访问带有satic的方法,程序会出现警告。
//所以带有static的方法还是建议用"类名."的方式访问。
t.doSome(); //这里不会出现空指针异常。
}
//带有static的方法,需要使用"类名."的方式访问
public static void doSome(){
System.out.println("do some!");
}
}
public class Chinese {
//身份证号【每一个对象的身份证好不同】
//实例变量
String id;
//姓名【每一个对象的姓名不同】
//实例变量
String name;
//国籍【每一个对象由于都是由"Chinese类"实例化的,所以每一个对象的国籍相同】
//无论通过"Chinese类"实例化多少个java对象,这些java对象的国籍都是"中国"。
//实例变量【实例对象是一个Java对象就有一个,100个Java对象就有100个】,
//分析这种设计方式有什么缺点?
//实例变量存储java对象内部,在对内存当中,在构造方法的时候初始化。
//所有的中国人的国籍都是"中国",这里声明为实例变量肖然是不合适的,
//太浪费内存空间,没必要让每一个对象都保留一个"国籍"内存。
String country;
//构造方法
public Chinese(){
}
public Chinese(String id, String name, String country) {
this.id = id;
this.name = name;
this.country = country;
}
}
public class ChineseTest {
public static void main(String[] args) {
//创建中国人的对象1
Chinese zhangsan = new Chinese("1","张三","中国");
System.out.println(zhangsan.id + " , " + zhangsan.name + " , " +
zhangsan.country);
//创建中国人的对象2
Chinese lisi = new Chinese("2","李四","中国");
System.out.println(lisi.id + " , " + lisi.name + " , " + lisi.country);
}
}
*什么时候成员变量声明为实例变量呢?
-所有对象都有这个属性,但是这个属性的值会随着对象的变化而变化
【不同对象的这个属性具体的值不同】
*什么时候成员变量声明为静态变量呢?
-所有对象都有这个属性,并且所有对象的这个属性的值都是一样的,建议定义为静态变量,
节省内存的开销。
*静态变量在类加载的时候初始化,内存在方法区中开辟,访问的时候不需要创建对象,
静态直接使用"类名.变量名"的方式访问。
*关于jva当中static关键字:
-static英语档次翻译为静态的
-static修饰的方法是静态方法
-static修饰的变量是静态变量
-所有static修饰的元素都称为静态的,都可以使用类名.的方式访问,当然也可以用引用.的方式访问
【但不建议】
-static修饰的所有元素都是类级别的特征,和具体的对象无关。
public class Chinese {
//身份证号【每一个对象的身份证好不同】
//实例变量
String id;
//姓名【每一个对象的姓名不同】
//实例变量
String name;
//国籍【所有对象的国籍一样,这种特征属于类级别的特征,可以提升为整个模板的特征,
//可以在变量前添加static关键字修饰】
//静态变量,静态变量在累加子啊的时候初始化,不需要创建对象就开辟了。
//静态变量存储在方法区内存中。
static String country = "中国";
//构造方法
public Chinese(){
}
public Chinese(String id, String name) {
this.id = id;
this.name = name;
}
}
public class ChineseTest {
public static void main(String[] args) {
//创建中国人的对象1
Chinese zhangsan = new Chinese("1","张三");
System.out.println(zhangsan.id + " , " + zhangsan.name + " , "
+ Chinese.country);
//创建中国人的对象2
Chinese lisi = new Chinese("2","李四");
System.out.println(lisi.id + " , " + lisi.name + " , " + Chinese.country);
System.out.println(Chinese.country); //中国
System.out.println(zhangsan.country); //中国
zhangsan = null;
//所有静态的数据都是可以采用类名.,也可以采用引用.,但是建议采用类名.的方式访问。
//采用引用.的方式访问的时候,即使引用是null,也不会出现空指针异常。
//因为访问静态的数据不需要对象的存在。
System.out.println(zhangsan.country); //中国
}
}
*语法格式:
static{
java语句;
}
*静态代码块在类加载的时候执行,并且只执行一次。
*静态代码块在一个类中可以编写多个,并且遵循自上而下的顺序依次执行。
*静态代码块的作用是什么?怎么用?用在哪儿?什么时候用?
-当然和具体的需求有关,例如项目中要求在类加载的时刻/时机执行代码完成日志记录。
那么这段记录日志的代码就可以编写到静态代码块当中,完成日志记录。
-静态代码块是java为程序员准备一个特殊的时刻,这个特殊的时刻被称为类加载时时刻。
若希望在此时刻执行一段特殊的程序,这段程序可以直接放到静态代码快当中。
*通常在静态代码块当中完成预备工作,先完成数据的准备工作,例如:初始化连接池,解析XML配置文件...
public class StaticTest01 {
static{
System.out.println("类加载 --> 1");
}
static{
System.out.println("类加载 --> 2");
}
static{
System.out.println("类加载 --> 3");
}
public static void main(String[] args) {
System.out.println("main begin!");
}
}
*实例代码块可以编写多个,也是遵循自上而下的顺序依次执行。
*示例代码块在构造方法执行之前执行,构造方法执行一次,实例代码块对应执行一次;
构造方法不执行,实例代码块也不执行
*示例代码块也是java语言为程序员准备的一个特殊时机,这个特殊时机被称为:对象初始化时机
public class Test {
public Test(){
System.out.println("Test类的缺省构造器执行");
}
//实例代码块
{
System.out.println(1);
}
//实例代码块
{
System.out.println(2);
}
//实例代码块
{
System.out.println(3);
}
public static void main(String[] args) {
System.out.println("main begain!");
new Test();
}
public class MainTest {
public static void main(String[] args) {
main(10);
main("Hello World!");
}
public static void main(int i){
System.out.println(i);
}
public static void main(String args){
System.out.println(args);
}
}
方法描述的是动作,当前所有的对象执行这个动作的时候,最终产生的影响是一样的,
那么这个动作已经不再属于某一个对象动作了,
可以将这个动作提升为类级别的动作,模板级别的动作。
静态变量中无法访问实例变量和实例方法。
大多数方法都定义为实例方法,一般一个行为或者一个动作在发生的时候,都需要对象的参与。
但是也有例外,例如:大多数"工具类"中的方法都是静态方法,因为工具类就是方便编程,
为了方便方法的调用,自然不需要new对象是最好的
public class StaticTest {
//实例变量
int i = 100;
//实例方法
public void doSome(){
System.out.println("do some!");
}
public static void main(String[] args) {
java.util.Scanner s = new java.util.Scanner(System.in);
//System.out.println(i);
//doSome();
StaticTest st = new StaticTest();
System.out.println(st.i);
st.doSome();
//使用数学工具类
System.out.println(MathUtil.sumInt(s.nextInt(), s.nextInt()));
System.out.println(MathUtil.divide(s.nextInt(), s.nextInt()));
}
}
总结:
class lei{
静态代码块;
实例代码块;
静态变量
实例变量
构造方法
静态方法
实例方法
}
//数学工具类
public class MathUtil {
public static int sumInt(int a,int b){
return a + b;
}
public static int divide(int a,int b){
return a / b;
}
}
a)、继承是面向对象三大特征之一,三大特征分别是:封装、继承、多态。
b)、继承的基本作用是:代码复用。但是继承最"重要"的作用是:
有了继承才有了以后的方法的覆盖和多态机制。
c)、继承语法格式:
[修饰符列表] class 类名 ectends 父类名{
类体 = 属性 + 方法;
}
d)、java语言当中的继承只支持单继承,一个类不能同时继承很多类,只能继承一个类。
在C++中支持多继承。
e)、关于继承中的一些属于:
B类继承A类,其中:
A类称为:父类、基类、superclass
B类称为:子类、派生类、subclass
f)、在java语言当中,子类继承父类都继承哪些数据呢?
*私有的不支持继承
*构造方法不支持继承
*其他数据都可以继承
g)、虽然java语言当中只支持单继承,但是一个类也可以间接继承其它类,例如:
C extends B{
}
B extends A{
}
A extends T{
}
C类直接继承B类,但是C类间接继承T A B类。
h)、java语言中假设一个类没有显示地继承任何类,
该类默认继承JavaSE库当中提供的java.lang.Objest类。
java语言中任何一个类都有Object类的特征。
//账户类
public class Account {
private String actno;
private double balance;
public Account(){
}
public Account(String actno, double balance) {
this.actno = actno;
this.balance = balance;
}
public String getActno() {
return actno;
}
public void setActno(String actno) {
this.actno = actno;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
}
//信用账户类
public class CreditAccount extends Account {
private double credit;
public CreditAccount() {
}
public double getCredit() {
return credit;
}
public void setCredit(double credit) {
this.credit = credit;
}
}
//继承测试类1
public class ExtendsTest {
//myecllipse快捷键:查找类型(Open Type)【ctrl + shift + T】
//myecllipse快捷键:查找资源(Open Resource)【ctrl + shift + R】
public static void main(String[] args) {
/*
ExtendsTest et = new ExtendsTest();
String s = et.toString(); //这里编译通过了,这说明可以调用toString方法,
ExtendsTest类中有toString方法,从Object类中继承过来的
System.out.println(s); //ExtendsTest01.ExtendsTest@55f33675
*/
CreditAccount act = new CreditAccount();
act.setActno("act-001");
act.setBalance(10000.0);
act.setCredit(100.0);
System.out.println(act.getActno());
System.out.println(act.getBalance());
System.out.println(act.getCredit());
}
}
//继承测试类2
public class ExtendsTest2 {
public static void main(String[] args) {
C c = new C();
c.doSome(); //这里调用的doSome方法是从B类中继承过来的doSome方法。
}
}
class A{
public void doSome(){
System.out.println("do some!");
}
}
class B extends A{
}
class C extends B{
}
a)、方法重载又称为Overload
b)、方法重载什么时候使用?
当在同一个类当中,方法完成的功能是相似的,建议方法名相同,
这样方法方便程序员的编程,就像在调用一个方法似的;代码美观。
c)、什么条件满足之后构成方法重载?
*在同一个类当中
*方法名相同
*参数列表不同:类型、顺序、个数
d)、方法重载和什么无关?
*和方法的返回值类型无关
*和方法的修饰符列表无关
a)、方法覆盖又被称为方法重写,英语档次是override【官方的】/overwrite
b)、什么时候使用方法重写?
当父类中的方法已经无法满足当前子类的业务需求,子类有必要将父类中继承过来的方法进行重新编写,
这个重新编写的过程称为方法重写/方法覆盖。
c)、什么条件满足之后方法会发生重写呢?【代码满足什么条件之后就构成方法的覆盖呢?】
*方法重写发生在有继承关系的父子类之间
*方法重写的时候:返回值类型相同,方法名相同,新参列表相同。
*方法重写的时候:访问权限不能更低,可以更高。
*方法重写的时候:抛出异常不能更多,可以更少。【以后讲,讲完异常之后才能解释】
d)、建议方法重写的时候尽量复制粘贴,不要编写,容易出错,导致没有产生覆盖。
e)、注意:
-私有方法不能继承,所以不能覆盖
-构造方法不能继承,所以不能覆盖
-静态方法不存在覆盖。【讲完多态之后解释】
-覆盖只针对方法,不针对属性。
public class OverrideTest01 {
public static void main(String[] args) {
//创建动物对象
Animal a = new Animal();
a.move();
//创建猫科类动物对象
Cat c = new Cat();
c.move();
//创建飞禽类动物对象
Bird b = new Bird();
b.move();
YingWu y = new YingWu();
y.move();
}
}
//动物类
public class Animal {
//动物都是可以移动的
public void move(){
System.out.println("动物在移动!");
}
}
//猫科类
public class Cat extends Animal {
public void move(){
System.out.println("猫在爬树!");
}
}
//飞禽类
public class Bird extends Animal {
public void move(){
System.out.println("鸟在飞翔!");
}
}
public class YingWu extends Bird {
//这里的move方法覆盖的是Bird中的move方法。
public void move() {
System.out.println("鹦鹉飞不起来!");
}
}
成员变量之实例变量:实例变量有默认值 + final修饰的变量一旦赋值不能重新赋值;
综合考虑,java语言最终规定实例变量使用final修饰之后,必须手动赋值,不能采用系统默认值
final修饰的引用虽然指向某个对象之后不能指向其他对象,但是所指向的对象内部的内存是可以被修改的。
关于myeclipse怎么链接源码?
打开某个.class字节码文件,当没有看到源码的时候:
点击"Attached Source":
-Workspace..【源码在当前的工作区当中】
-External File..【源码在个压缩包当中】
-External Folder..【源码在某个目录当中】
以后尽量所有的程序都链接源码,没有源码从网上找,或者跟老师要源码。养成看源代码的好习惯。
对于以后大家所学的类库,一般都是包括三个部分:
-源码【可以看源码来理解程序】
-字节码【程序开发的过程中使用的就是这部分】
-帮助文档【对源码的解释说明被提取出来,更方便程序的开发】
//final测试01
public class FinalTest01 {
public static void main(String[] args) {
int i = 10;
System.out.println(i);
i = 20;
System.out.println(i);
final int k = 100;
System.out.println(k);
//编译错误:无法为最终变量k分配值
//k = 200;
final int m;
m = 200;
//编译错误:无法为最终变量k分配值
//m = 300;
}
}
public final class A {
}
/*
* final修饰的类无法被继承
public class B extends A {
}
*/
public class B{
}
public class C {
public final void m1(){
}
}
public class D extends C {
/*
final修饰的方法无法被覆盖
public void m1(){
}
*/
}
//final测试02
public class FinalTest02 {
//成员变量之实例变量
//实例变量有默认值 + final修饰的变量一旦赋值不能重新赋值
//综合考虑,java语言最终规定实例变量使用final修饰之后,必须手动赋值,不能采用系统默认值
//final int age; //编译错误
//第一种解决方案
final int age = 10;
//第二种解决方案
final int num;
public FinalTest02(){
this.num = 200;
}
//以上的两种解决方案:其实本质上就是一种方式,都是在构造方法执行过程当中给实例变量赋值。
public static void main(String[] args) {
final int a;
a = 100;
//不可二次赋值
//a = 200;
}
}
//final测试03
public class FinalTest03 {
public static void main(String[] args) {
//创建用户对象
User u = new User(100); //User u = 0x1234;
//有创建了一个新的User对象
//程序执行到此处表示以上程序已经编程垃圾数据,等待垃圾回收器的回收。
u = new User(200); //u = 0x2586;
//创建用户对象
final User user = new User(30);
//user = new User(50); //final修饰的引用一旦指向某个对象之后,不能再指向其他对象,
//那么被指向的对象无法被垃圾回收器回收。
user.id = 50; //final修饰的引用虽然指向某个对象之后不能指向其他对象,
//但是所指向的对象内部的内存是可以被修改的。
}
}
public class User {
int id;
public User(int id) {
super();
this.id = id;
}
}
//final测试04
public class FinalTest03 {
public static void main(String[] args) {
System.out.println(Chinese.GUO_JI);
System.out.println("圆周率:" + Math.PI);
}
}
class Math{
public static final double PI = 3.141592653579;
}
//中国人
class Chinese{
//国籍
//需求:每一个中国人的国籍都是中国,而且国籍不会发生改变,为了防止国籍被修改,建议加final修饰。
//final修饰的实例变量是不可变的,这种变量一般和static联合使用,被称为"常量"
//常量的定义语法格式:
//public static final 类型 常量名 = 值;
//java规范中要求所有常量的名字全部大写,每个单词之间使用下划线链接
//static final String country = "中国";
public static final String GUO_JI = "中国";
}
查找比较方便,管理比较方便,易维护。
-在java源程序的第一行编写package语句。
-package只能编写一个语句。
-语法结构:
package 包名;
公司域名倒序 + 项目名 + 模块名 + 功能名;
采用这种方式重名的几率较低。因为公司域名具有全球唯一性。
例如:
com.bjpowernode.oa.user.service;
org.apache.tomcat.core;
-使用了package机制之后,类名不再是Test01了,类名是:com.bjpowernode.
javase.day11.Test01
-编译:javac java云文件路径(在硬盘上生成一个class文件:Test01.class)
-手动方式创建目录,将Test01.class字节码文件放到执行的目录下
-运行:java com.bjpowernode.javase.day11.Test01
-另一种方式(编译 + 运行):
*编译:Javac -d 编译之后存放的路径 java源文件的路径
*例如:将F:\Hello.java文件编译之后放到c:\目录下
javac -d C:\ F:\Hello.java
*javac -d . *.java
将当前路径中*.java编译之后存放到当前目录下。
*运行:JVM的类加载器ClassLoader默认从当前路径下加载。
保证DOS命令窗口的路径先切换到com所在的路径,执行:java com.bjpowernode.
javse.day11.Test01
//package测试01
package com.bjpowernode.javase.day11; //4个目录【目录之间使用.隔开】
public class Test01
{
public static void main(String[] args){
System.out.println("Test01's main method execute!");
}
}
//package测试02
public class Test02
{
public static void main(String[] args){
//完整类名是:com.bjpowernode.javase.day11.Test01
com.bjpowernode.javase.day11.Test01 t = new com.bjpowernode.
javase.day11.Test01();
System.out.println(t); //com.bjpowernode.javase.day11.Test01@54bedef2
//可以省略包名,因为Test01和Test02在同一个软件包当中。
Test01 tt = new Test01();
System.out.println(tt); //com.bjpowernode.javase.day11.Test01@5caf905d
}
}
//package测试03
public class Test03
{
public static void main(String[] args){
//创建Test01对象
//以下代码编译错误:当省略包名之后,会在当前包下找Test01
//实际上编译器去找:com.bjpowernode.Test01了。这个类不存在
//Test01 tt = new Test01();
//System.out.println(tt);
//修改以上的错误
//包名不要省略,添加包名
//结论:什么时候前边的包名可以省略?Test03和Test01在同一个包下的时候不需要加包名。
com.bjpowernode.javase.day11.Test01 tt = new com.bjpowernode.
javase.day11.Test01();
System.out.println(tt);
}
}
//package测试04
import语句用来完成导入其它类,同一个包下的类不需要导入,不在同一个包下需要手动导入。
import语法格式:
import 类名;
import 包名.*
import语句需要编写在package语句之下,class语句之上。
//import com.bjpowernode.javase.day11.Test01;
import com.bjpowernode.javase.day11.*;
import java.util.Date;//import java.util.*;
public class Test04
{
public static void main(String[] args){
com.bjpowernode.javase.day11.Test01 tt = new com.bjpowernode.
javase.day11.Test01();
System.out.println(tt);
com.bjpowernode.javase.day11.Test01 ttt = new com.bjpowernode.
javase.day11.Test01();
System.out.println(ttt);
com.bjpowernode.javase.day11.Test01 tttt = new com.bjpowernode.
javase.day11.Test01();
System.out.println(tttt);
//以上程序可以就是麻烦
Test01 x = new Test01();
System.out.println(x); //com.bjpowernode.javase.day11.Test01@8efb846
Test01 y = new Test01();
System.out.println(y); //com.bjpowernode.javase.day11.Test01@2a84aee7
//Java.lang.*;不需要手动引入,系统自动引入。
//lang:language语言包,是java语言的核心类,不需要手动引入。
String s = "abc";
System.out.println(s);
//直接编写以下代码编译错误,因为Date类没有找到
//Date d = new Date();
//java.util Date d = new java.util.Date();
//使用import语句之后:import java.util.Date;
Date d = new Date();
System.out.println(d); //Mon Dec 06 16:12:55 CST 2021
}
}
/*
myeclipse导入类的快捷键:ctrl + shift + o
最终结论:
什么时候需要import?
*不是java.lang包下,并且不在同一个包下的时候,需要使用import进行引入。
*/
-public 表示公开的,在任何位置都可以访问
-protected 同包,子类中可以访问
-缺省 只是同包下可以访问
-private 表示私有的,只能本类中访问
private < 缺省 < protected < public
public class Test01 {
public static void main(String[] args) {
User u = new User();
System.out.println(u.i);
System.out.println(u.j);
}
}
public class User {
//受保护的
protected int i = 10;
//缺省的
int j = 20;
private void m1(){
}
public void m2(){
}
protected void m3(){
}
void m4(){
}
}
import Test04.User; //导入Test04.class
public class UserTest {
public static void main(String[] args) {
User u = new User();
//System.out.println(u.i);编译出错:受保护的protected只能在同包,或者子类中访问
//System.out.println(u.j);编译出错:缺省只能在同包中访问
}
}
import Test04.User; //导入Test04.class
public class Driver extends User { //Test04里的User子类
public void m(){
System.out.println(i); //编译通过:受保护的可以在子类中访问
//System.out.println(j);编译出错:缺省只能在同包中访问
}
}
**类只能采用public和缺省的修饰符进行修饰。【内部类除外】
public class Customer {
}
class Student {
}
this:
this能出现在实例方法和构造方法中。
this的语法是:"this."、"this()"
this不能使用在静态方法当中。
this.大部分情况下是可以省略的。
this.什么时候不能省略呢?在区分局部变量和实例变量的时候不能省略。
public void setName(String name){
this.name = name;
}
this()只能出现在构造方法第一行,通过当前的构造方法去调用“本类”中其他的构造方法,
目的是:代码复用。
super:
super能出现在实例方法和构造方法中。
super的语法是:"super."、"super()"
super不能使用在静态方法当中。
super.大部分情况下是可以省略的。
super.什么时候不能省略呢?
super()只能出现在构造方法第一行,通过当前的构造方法去调用“父类”中其他的构造方法,
目的是:创建子类对象的时候,先初始化父类型特征。
表示通过子类的构造方法调用父类的构造方法;模拟现实世界中的这种场景:
要想有儿子,需要先有父亲。【注意:是调用,不是继承,构造方法不可继承。】
当一个构造方法第一行:
既没有this()有没有super()的话,默认会有一个super();
表示通过当前子类的方法调用弗雷德无参数构造方法。所以必须保证父类的无参数构造方法是存在的。
this()和super()不能共存,它们都是只能出现在构造方法第一行。
public class SuperTest01{
public static void main(String[] args){
//创建子类对象
/*
A类的无参数构造方法!
B类的无参数构造方法!
*/
new B();
}
}
class A{
//建议手动地将一个无参数构造方法写出来。
public A(){
System.out.println("A类的无参数构造方法!");
}
//一个类如果没有手动提供任何构造方法,系统会默认提供一个无参数构造方法。
//一个类如果手动提供了一个构造方法,那么无参数构造系统将不再提供。
public A(int i){
System.out.println("A类的有参数构造方法(int)!");
}
}
class B extends A{
/*
public B(){
//super();
System.out.println("B类的无参数构造方法!");
}
*/
//调用父类中有参数的构造方法
public B(){
this("zhangsan");
//super(1); //编译报错,因为this()和super()不能共存,它们都是只能出现在构造方法第一行。
//所以现在只能调用A类无参数构造方法【在下一个构造方法第一行有一个super();】
System.out.println("B类的无参数构造方法!");
}
public B(String name){
System.out.println("B类的有参数构造方法(String)!");
}
}
/*
判断程序的输出结果:1、3、6、5、4。
在java语言中不管是new什么对象,最后老祖宗的Object类的无参数构造方法一定会执行。
(Object类中的无参数构造方法是处于“栈顶部”)
栈顶的特点:
最后调用,但是最先执行结束
后进先出原则
*/
public class SuperTest02{
public static void main(String[] args){
new C();
//输出结果:1、3、6、5、4
}
}
class A{
public A(){
System.out.println("A的无参数构造执行!"); //1
}
}
class B extends A{
public B(){
System.out.println("B的无参数构造执行!"); //2
}
public B(String name){
System.out.println("B类的有参数构造执行(String)!"); //3
}
}
class C extends B{
public C(){
this("zhangsan");
System.out.println("C的无参数构造执行!"); //4
}
public C(String name){
this(name,20);
System.out.println("C的有参数构造执行(String)!"); //5
}
public C(String name,int age){
super(name);
System.out.println("C的有参数构造执行(String,int)!"); //6
}
}
*举个例子:在恰当的时间使用:super(实际参数列表);
*注意:虽然调用构造方法,在构造方法执行的过程中一连串调用了父类的构造方法,
父类的构造方法有继续向下调用它的父类的构造方法,但实际上对象只创建了一个。
*思考:“super(实参)”到底是干啥的?
super(实参)的作用是:初始化当前对象的父类特征,并不是创建新对象,实际上对象之创建了一个。
*super关键字代表什么?
super关键字代表的就是“当前对象”的那部分父类型特征。
我继承了我父亲的一部分特征:
例如:眼睛、皮肤等
super代表的就是“眼睛、皮肤等”。
“眼睛、皮肤等”虽然是继承了父亲的,但这部分是在我身上的、
//测试程序
public class SuperTest03{
public static void main(String[] args){
CreditAccount ca1 = new CreditAccount(); //只创建了一个对象
System.out.println(ca1.getActno() + "," + ca1.getBalance() + "," + ca1.getCredit());
CreditAccount ca2 = new CreditAccount("1111",10000.0,100);
System.out.println(ca2.getActno() + "," + ca2.getBalance() + "," + ca2.getCredit());
}
}
//账户
class Account {
//账号
private String actno; //实例变量/对象变量,也就是说,必须先有对象才能有对应的实例变量。
//余额
private double balance;
//无参数构造器
public Account(){
//初始化实例变量的内存空间
}
public Account(String actno,double balance){
this.actno = actno;
this.balance = balance;
}
public String getActno() {
return actno;
}
public void setActno(String actno) {
this.actno = actno;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
}
//信用账户
class CreditAccount extends Account {
private double credit;
//提供有参数的构造方法
//分析以下程序是否存在编译错误?
public CreditAccount(String actno,double balance,double credit) {
//私有的属性,只能在本类中访问。
/*
this.actno = actno;
this.balance = balance;
*/
//以上两行代码在恰当的位置,正好可以使用:super(actno,balance);
//通过子类的构造方法调用父类的构造方法
super(actno,balance);
this.credit = credit;
}
public CreditAccount() {
}
public double getCredit() {
return credit;
}
public void setCredit(double credit) {
this.credit = credit;
}
}
a)、“this.”和“super.”大部分情况下都是可以省略的。
b)、this.什么时候不能省略?
public void setName(String name){
this.name = name;
}
c)、super.什么时候不能省略?
父中有,子中又有,如果想在子中访问父的特征super.不能省。
public class SuperTest05{
public static void main(String[] args){
Vip v = new Vip("张三");
v.shopping();
}
}
class Customer{
String name;
public Customer(){
}
public Customer(String name){
this.name = name;
}
}
class Vip extends Customer{
//假设子类也有一个同名属性
//java中允许在子类中出现和父类一样的同名变量/同名属性
String name;
public Vip(){
}
public Vip(String name){
super(name);
//this.name = null;
}
public void shopping(){
/*
java是怎么来区分子类和父类的同名属性的?
this.name:当前对象的name属性
super.name:当前对象的父类型特征中的name属性
*/
System.out.println(this.name + "正在购物!"); //null正在购物!
System.out.println(super.name + "正在购物!"); //张三正在购物!
System.out.println(name + "正在购物!"); //null正在购物!
}
}
通过这个测试得出的结论:
super不是引用;super也不保存内存地址;super也不止像任何对象。
super只是代表当前对象内部的那一块父类型的特征。
public class SuperTest06{
//实例方法
public void doSome(){
System.out.println(this); //SuperTest06@2ff4acd0
//输出“引用”的时候,会自动调用引用的toString()方法
//System.out.println(this.toString());
//编译错误:需要“.”
//System.out.println(super);
}
//this和super不能使用在静态方法中。
/*
public static void doOther(){
System.out.println(this);
System.out.println(super.xxx);
}
*/
//静态方法,主方法
public static void main(String[] args){
SuperTest06 st = new SuperTest06();
st.doSome();
}
}
super.属性名; 【访问父类的属性】
super.方法名(实参); 【访问父类的属性】
super(实参); 【调用父类的构造方法】
public class SuperTest07{
public static void main(String[] args){
Cat c = new Cat();
c.yiDong();
}
}
class Animal{
public void move(){
System.out.println("Animal move!");
}
}
class Cat extends Animal{
//对move进行重写。
public void move(){
System.out.println("Cat move!");
}
//单独编写一个子类特有的方法。
public void yiDong(){
this.move(); //Cat move!
move(); //Cat move!
//super.不仅可以访问属性,也可以访问方法。
super.move(); //Animal move!
}
}
a)、Animal、Cat、Bird三个类之间的关系:
Cat继承Animal
Bird继承Animal
Cat和Bird之间没有任何继承关系
b)、面向对象三大特征:封装、继承、多态
c)、关于多态中涉及到的几个概念:
*向上转型(upcasting)
子类型 --> 父类型
又被称为:自动类型转换。
*向下转型(downcasting)
父类型 --> 子类型
又被称为:强制类型转换【需要加强制类型转换符】
*需要记忆:
无论是向上转型还是向下转型,两种类型之间必须有继承关系。
没有继承关系,程序是无法编译通过的。
d)、向下转型编译通过,运行可能错误:Animal a3 = new Bird(); Cat c3 = (Cat)a3;
e)、怎么避免向下转型出现ClassCastException呢?
使用instanceof运算符可以避免出现以上的异常。
f)、instanceof运算符怎么用?
-语法格式:
(引用 instanceof 数据类型名)
-以上运算符的执行结果类型是布尔类型,结果可能是true/false
-关于运算结果true/false
假设:(a instanceof Animal)
true表示:
a这个引用指向的对象是一个Animal类型。
false表示:
a这个引用指向的对象不是一个Animal类型。
g)、java规范中要求:在进行强制类型转换之前,建议采用instanceof运算符进行判断,
避免ClassCastException异常的发生。这是一种编程的好习惯。
public class Test {
public static void main(String[] args) {
//以前编写的程序
Animal a1 = new Animal();
a1.move();
Cat c1 = new Cat();
c1.move();
c1.catchMouse();
Bird b1 = new Bird();
b1.move();
//使用多态语法机制
/**
*Animal和Cat之间存在继承关系,Animal是父类,Cat是子类
*Cat is a Animal【合理的】
* new Cat创建的对象类型是Cat,a2这个引用的数据类型是Animal,可见它们进行了
类型转换:子类型转换成父类型,称为向上转型/upcasting,或者称为自动类型转换。
*java中允许这种语法:父类型引用指向子类型对象。
*/
Animal a2 = new Cat();
//Bird b2 = new Cat(); //编译报错,因为两种类型之间不存在任何继承关系。
//无法向上或者向下转型
/**
*java程序永远都分为编译阶段和运行阶段。
*先分析编译阶段,在分析运行阶段,编译无法通过,根本是无法运行的。
*编译阶段编译器检查a2这个引用的数据类型为Animal,由于Animal.class字节码当中有
move()方法,所以编译通过了。
这个过程我们称为静态绑定,编译阶段绑定;只有静态绑定成功之后才有后续的运行。
*在程序运行阶段JVM堆内存当中真实创建的对象是Cat对象,
那么以下程序在运行阶段一定会调用Cat对象的move()方法,
此时发生了程序的动态绑定,运行阶段绑定。
*无论Cat类有没有重写move方法,运行阶段一定调用的是Cat对象的move方法,
因为底层真实的对象就是Cat对象。
*父类型引用指向子类型对象这种机制导致程序存在编译阶段的绑定和运行阶段的绑定
两种不同的形态/状态,这种机制可以称为一种多态语法机制。
*/
a2.move(); //猫在爬树!
/**
分析以下程序为什么不能调用?
因为编译阶段编译器检查到a2的类型是Animal类型
从Animal.class字节码文件中查找catchMouse()方法,最终没有找到该方法,
导致静态绑定失败,没有绑定成功,也就是说编译失败了,别谈运行了。
*/
//a2.catMouse();
/**
需求:假设想让以上的对象执行catchMouse()方法,怎么办?
a2是无法直接调用的,因为a2的类型是Animal,Animal中没有catchMouse()方法。
我们可以将a2强制类型转换为Cat类型。
a2的类型是Animal(父类),转换成Cat类型(子类),被称为向下转型/downcasting/强制
类型转换。
注:向下转型也需要两种类型之间必须有继承关系,不然编译报错强制类型转换需要加
强制类型转换符。
什么时候需要使用强制类型转换呢?
当调用的方法是子类型中特有的,在父类型当中不存在。必须进行向下转型。
*/
Cat c2 = (Cat)a2;
c2.catchMouse();
//父类型引用指向子类型对象【多态】
Animal a3 = new Bird();
/**
*以下程序编译是没有问题的,因为编译器检查到a3的数据类型是Animal,
Animal和Cat之间存在继承关系,
并且Animal是父类型,Cat是子类型,父类型转换成子类型叫做向下转型,语法合格。
*程序虽然编译通过了,但是程序在运行阶段会出现异常,因为JVM堆内存当中真实存在的对象
是Bird类型,Bird对象无法转换成Cat对象,因为两种类型之间不存在任何继承关系,
此时出现了著名的异常:java.lang.ClassCastException【类型转换异常】,
这种异常总是在"向下转型"的时候发生。
*/
//Cat c3 = (Cat)a3;
/**
*以上异常只有在强制类型转换的时候发生,也就是说"向下转型"存在隐患
(编译过了,但是运行错了!)
*向上转型只要编译通过,运行一定不会出问题。Animal a2 = new Cat();
*向下转型编译通过,运行可能错误:Animal a3 = new Bird(); Cat c3 = (Cat)a3;
*怎么避免向下转型出现ClassCastException呢?
使用instanceof运算符可以避免出现以上的异常。
*instanceof运算符怎么用?
-语法格式:
(引用 instanceof 数据类型名)
-以上运算符的执行结果类型是布尔类型,结果可能是true/false
-关于运算结果true/false
假设:(a instanceof Animal)
true表示:
a这个引用指向的对象是一个Animal类型。
false表示:
a这个引用指向的对象不是一个Animal类型。
*/
if (a3 instanceof Cat) {//a3是一个Cat类型的对象
Cat c3 = (Cat)a3;
c3.catchMouse();
}else if(a3 instanceof Bird) {//a3是一个Bird类型的对象
Bird b3 = (Bird)a3;
b3.fly();
}
}
}
//动物类
public class Animal {
public void move(){
System.out.println("动物在移动!");
}
}
//猫类
public class Cat extends Animal {
//重写父类中继承过来的方法
public void move() {
System.out.println("猫在爬树!");
}
//不是从父类中继承过来的方法
//这个方法是子类对象特有的行为【不是说所有的动物都可以抓老鼠的】
public void catchMouse(){
System.out.println("猫抓老鼠!");
}
}
//鸟儿类
public class Bird extends Animal {
//重写从父类中继承过来的方法
public void move() {
System.out.println("鸟儿在飞翔!");
}
/**
*子类对象特有的行为/动作
*/
public void fly(){
System.out.println("Bird fly!");
}
}
*分析:主人喂养宠物这个场景要实现需要进行类型的抽象:
-主人【类】:主人可以喂养宠物,所以主人有喂养的这个动作
-宠物【类】:宠物可以吃东西,所以宠物有吃东西的这个动作
*面向对象编程的核心:定义好类,然后将类实例化为对象,给一个环境驱使以下,让各个对象之间协作起来形成一个系统。
*多态的作用是什么?
降低程序的耦合度,提高程序的扩展力。能使用多态尽量使用多态;父类型引用指向子类型对象。
*核心:面向抽象编程,尽量不要面向具体编程。
public class Test {
public static void main(String[] args) {
//创建主人对象
Master zhangsan = new Master();
//创建猫对象
//Cat tom = new Cat();
//主人喂养猫
//zhangsan.feed(tom);
zhangsan.feed(new Cat());
//创建狗对象
//Dog erHa = new Dog();
//主人喂养狗
//zhangsan.feed(erHa);
zhangsan.feed(new Dog());
//主人喂养蛇
zhangsan.feed(new Snake());
}
}
//主人
//这种方式没有使用java语言当中的多态机制,存在的缺点:Master的扩展力很差,因为只要加一个新的宠物,
//Master类就需要添加新的方法。
public class Master {
//喂养宠物的方法
public void feed(Cat c){
c.eat();
}
public void feed(Dog d){
d.eat();
}
}
//Master和Cat、Dog这两个类型的关联成都很强,耦合度很高,扩展力差。
*/
//降低程序的耦合度【解耦合】,提高程序的扩展力【软件开发的一个很重要的目标】
public class Master {
//Master类面向的是一个抽象的Pet,不在面向具体的宠物
//提倡:面向抽象编程,不要面向具体编程;面向抽象编程的好处是,耦合度低,扩展力强。
public void feed(Pet pet){//Pet pet是一个父类型引用
pet.eat();
}
}
//宠物
public class Pet {
//所有宠物都可以吃东西
public void eat(){
}
}
//宠物小猫
public class Cat extends Pet{
//小猫爱吃鱼
public void eat(){
System.out.println("小猫正在吃鱼!");
}
}
//宠物小狗
public class Dog extends Pet{
public void eat(){
System.out.println("小狗正在吃肉!");
}
}
//新的宠物蛇
public class Snake extends Pet {
public void eat(){
System.out.println("小蛇正在吞象!");
}
}