JAVA-第一阶段
目录
1、整型的使用细节 2、浮点类型 3、Java API文档 4、字符类型 char
5、字符编码表 6、布尔类型 7、基本数据类型转换 8、强制类型转换
1、算术运算符 2、关系运算符 3、逻辑运算符 4、赋值运算符
5、三元运算符 6、运算符优先级 7、标识符规则与规范 8、关键字与保留字
1、分支控制 2、嵌套分支 3、switch分支结构 4、循环控制-for循环
5、循环控制-while循环 6、do…while循环 7、多重循环 8、化繁为简、先死后活
9、跳转控制语句break 10、跳转控制语句continue 11、跳转控制语句return 12、程序控制结构作业
1、数组的使用方式 2、数组使用注意事项和细节 3、赋值机制与数组拷贝
1、类与对象 2、对象在内存中的存在形式 3、属性 4、方法
5、成员方法的定义及细节 6、方法调用细节说明 7、成员方法传参机制 8、克隆对象
1、注意事项和使用细节 2、创建对象流程分析 3、流程分析(面试题)
------------------------------------------------------------------------------
1、Java技术体系平台
Java SE(Java Standard Edition)标准版
支持面向桌面级应用(如Windows下的应用程序)的Java平台,提供了完整的Java核心API,此版本以前称为J2SE
Java EE(Java Enterprise Edition)企业版
是为开发企业环境下的应用程序提供的一套解决方案。该技术体系中包含的技术如:Servlet、Jsp等,主要针对于Web应用程序开发,版本以前称为J2EE,学习java EE是要以java SE为基础的
Java ME(Java Micro Edition)小型版
支持Java程序运行在移动终端((手机、PDA)上的平台,对Java API有所精简,并加入对移动终端的支持,此版本以前称为J2ME
2、java虚拟机
JVM是一个虚拟的计算机,具有指令集并使用不同的存储区域。负责执行指令,管理数据、内存、寄存器,包含在JDK中
对于不同的平台,有不同的虚拟机,Java 虚拟机机制屏蔽了底层运行平台的差别,实现了“一次编译,到处运行”,所以才说java的跨平台性极好
3、JDK与JRE
JDK的全称(Java Development Kit Java开发工具包)
JDK=JRE +java的开发工具 [ java, javac,javadoc,javap 等]
JDK是提供给Java开发人员使用的,其中包含了java 的开发工具,也包括了JRE。所以安装了JDK,就不用在单独安装JRE了。
JRE的全称(Java Runtime Environment Java运行环境)
JRE=JVM +Java的核心类库 [ 类 ]
包括Java虚拟机(JVM Java Virtual Machine)和Java程序所需的核心类库等,如果想要运行一个开发好的Java程序,计算机中只需要安装JRE即可。
JDK、JRE和JVM的包含关系
①JDK=JRE+开发工具集(例如Javac,java编译工具等)
②JRE=JVM +Java SE标准类库(java核心类库)
③如果只想运行开发好的.class文件只需要JRE
4、下载安装JDK
官方网址:https://www.oracle.com/java/technologies/javase-downloads.html
安装完之后需要配置环境变量
环境变量原理:当前执行的程序在当前目录下如果不存在,win10系统会在系统中已有的一个名为path的环境变量指定的目录中查找。如果仍未找到,会出现错误提示
所以需要将java用到的程序路径存入到环境变量中去
1、我的电脑--属性--高级系统设置--环境变量 2、增加JAVA HOME 环境变量,指向jdk的安装目录 3.编辑path 环境变量,增加%JAVA HOME%\bin 4、打开DOS命令行,任意目录下敲入javac、java,如果出现javac的参数信息,配置成功
centos安装java11
rpm -qa | grep java #查询系统是否已经安装java
卸载安装的java
rpm -e --nodeps java-11-openjdk-devel-11.0.15.0.9-2.el7_9.x86_64 rpm -e --nodeps java-11-openjdk-headless-11.0.15.0.9-2.el7_9.x86_64 rpm -e --nodeps java-11-openjdk-static-libs-11.0.15.0.9-2.el7_9.x86_64 rpm -e --nodeps java-11-openjdk-javadoc-11.0.15.0.9-2.el7_9.x86_64 rpm -e --nodeps java-11-openjdk-jmods-11.0.15.0.9-2.el7_9.x86_64 rpm -e --nodeps java-11-openjdk-src-11.0.15.0.9-2.el7_9.x86_64 rpm -e --nodeps java-11-openjdk-11.0.15.0.9-2.el7_9.x86_64 rpm -e --nodeps java-11-openjdk-demo-11.0.15.0.9-2.el7_9.x86_64 rpm -e --nodeps javapackages-tools-3.4.1-11.el7.noarch rpm -e --nodeps java-11-openjdk-javadoc-zip-11.0.15.0.9-2.el7_9.x86_64
#验证一下是还有jdk rpm -qa | grep java java -version #没有内容证明已经卸载干净了
#yum源搜索java-11的安装包 yum search java-11
yum install java-11* -y #全部安装 java -version #验证安装结果
查看安装的目录,并设置JAVA_HOME
whereis java ll -a /usr/bin/java ll -a /etc/alternatives/java
java-11的安装本地目录为:/usr/lib/jvm/java-11-openjdk-11.0.15.0.9-2.el7_9.x86_64/
添加环境变量,设置设置JAVA_HOME
vi /etc/profile
在文件最后添加以下代码
source /etc/profile #刷新profile文件
5、java快速入门
//这是我的第一个java程序 //public class Hello表示Hello是一个类,是一个public公有的类 //Hello{ }表示一个类的开始和结束 //public static void main(String[] args)表示一个程序的主方法,及程序的入口 //main() { }表示方法的开始和结束 //System.out.println("Hello,World!")表示输出Hello,World!到屏幕上 //“;”表示语句的结束 public class Hello { //编写一个main方法 public static void main(String[] args) { System.out.println("Hello,World!"); } }
6、java开发注意事项
1>Java源文件以.java为扩展名。源文件的基本组成部分是类(class),如本类中的Hello类
2>Java应用程序的执行入口是main()方法。
它有固定的书写格式:
public static void main(String[] args) {...}
3>Java语言严格区分大小写
4>Java方法由一条条语句构成,每个语句以“;”结束
5>大括号都是成对出现的,缺一不可,要习惯先写 { } 再写代码
6>一个源文件中最多只能有一个public类,其它类的个数不限
7>如果源文件包含一个public类,则文件名必须按该类名命名
8>一个源文件中最多只能有一个public类,其它类的个数不限,也可以将main方法写在非public类中,然后指定运行非public类,这样入口方法就是非public的main方法
7、如何快速掌握新技术
\t #一个制表位,实现对齐的功能 \n #换行符 \\ #输出一个\ \" #输出一个" \' #输出一个' \r #一个回车
示例
public class ChangeChar { public static void main(String[] args) { System.out.println("莫使\t金樽\t空对月"); System.out.println("人生得意\n须尽欢"); System.out.println("C:\\Windows\\case\\test.txt"); System.out.println("李白说:\"莫使金樽空对月\""); System.out.println("***莫使金樽空对月\r李白="); //\r\n表示回车加换行 } }
代码注释分为javadoc注释和单行/多行注释
// 单行注释 /* 多行注释 */
javadoc注释被称为文档注释,注释内容可以被JDK提供的工具javadoc 所解析,生成一套以网页文件形式体现的该程序的说明文档,一般写在类
javadoc -d 文件夹名 -author -version char.java
java开发简单规范
1、类、方法的注释,要以javadoc的方式来写注释
2、非Javadoc的注释,往往是给代码的维护者看的,着重告述读者为什么这样写,如何修改,注意什么问题等(单行,多行注释)
3、使用tab操作,实现缩进,默认整体向右边移动;用shift+tab整体向左移
4、运算符和=两边习惯性各加一个空格。比如:2 + 4 * 5 + 345 - 89
5、源文件使用utf-8编码
6、行宽度不要超过80字符
7、代码编写次行风格和行尾风格,推荐使用行尾风格
变量是程序的基本组成单位,变量有三个基本要素(类型+名称+值),变量相当于内存中一个数据存储空间的表示
class variable { public static void main(String[] args) { //定义变量先 int c; //再给变量赋值 c = 9527; System.out.println(9527); //还可以定义变量和赋值一步到位 int a = 1; System.out.println(a+c); } }
变量使用注意事项
1、变量表示内存中的一个存储区域,不同的变量,类型不同,占用的空间大小不同
2、该区域有自己的名称[变量名]和类型[数据类型]
3、变量必须先声明,后使用,即有顺序
4、该区域的数据/值可以在同一类型范围内不断变化
5、变量在同一个作用域内不能重名
6、变量=变量名+值+数据类型,即变量三要素
程序中+号的使用
1、当左右两边都是数值型时,则做加法运算
2、当左右两边有一方为字符串,则做拼接运算
3、运算顺序从左到右
class plus { public static void main(String[] args) { System.out.println(100+98); //198 System.out.println("100"+98); //10098 System.out.println(100+98+"hello"); //198hello System.out.println("hello"+100+98); //hello10098 } }
每一种数据都定义了明确的数据类型,在内存中分配了不同大小的内存空间(字节)
class bytes_test { public static void main(String[] args) { byte n = 200; //故意设置出超过此范围的数,查看会报什么错误 System.out.println(n); } }
1> Java各整数类型有固定的范围和字段长度,不受具体OS[操作系统]的影响,以保证java程序的可移植性
2> Java的整型常量(具体值)默认为int型,声明long型常量须后加l或L
3> java程序中变量常声明为int型,除非不足以表示大数,才使用long
4> bit:计算机中的最小存储单位; byte:计算机中基本存储单元,1byte = 8 bit
Java的浮点类型可以表示一个小数,比如123.4 、7.8 、0.12等等
1> 单精度与双精度的区别
在内存中占有的字节数不同:单精度浮点数在机内占4个字节,双精度浮点数在机内占8个字节;
有效数字位数不同:单精度浮点数有效数字8位,双精度浮点数有效数字16位。
关于浮点数在机器中存放形式的简单说明:浮点数=符号位+指数位+尾数位,尾数部分可能丢失,造成精度损失(小数都是近似值)
2> 使用细节
与整数类型类似,Java浮点类型也有固定的范围和字段长度,不受具体OS的影响。[float 4个字节、double是8个字节]
Java 的浮点型常量(具体值)默认为double型,声明float型常量,须后加‘f‘或‘'F'
class folat_test { public static void main(String[] args) { float n = 3.14F; double m = 3.14; System.out.println(n+" -- "+m); } }
3> 浮点型常量有两种表示形式
十进制数形式,如:5.12、512.0f、.512(必须有小数点)
科学计数法形式,如:5.12e2 [5.12*10的2次方]、5.12E-2 [5.12/10的2次方]
4> 通常情况下,应该使用double型,因为它比float型更精确
class double_duibi { public static void main(String[] args) { double num9 = 2.1234567851; float num10 =2.1234567851F; System.out.println(num9); System.out.println(num10); } }
5> 浮点数使用陷阱:2.7和8.1/3比较
class trap { public static void main(String[] args) { double num1 = 2.7; double num2 = 8.1/3; System.out.println(num1); System.out.println(num2); } }
所以得到一个重要的使用点:当我们对运算结果是小数的进行相等判断时,要小心,应该是以两个数的差值的绝对值,在某个精度范围类判断
但如果是直接查询得的小数或者直接赋值,则可以直接判断相等
class trap { public static void main(String[] args) { double num1 = 2.7; double num2 = 8.1/3; if( Math.abs(num1 - num2) < 0.00001 ) { System.out.println("相等"); } } }
1> API (Application Programming Interface,应用程序编程接口)是Java提供的基本编程接口(java提供的类还有相关的方法)
中文在线文档:https://www.matools.com
2> Java语言提供了大量的基础类,因此 Oracle 公司也为这些基础类提供了相应的API文档,用于告诉开发者如何使用这些类,以及这些类里包含的方法
3> Java类的组织形式
字符类型可以表示单个字符,字符类型是char,char是两个字节(可以存放汉字),多个字符我们用字符串String
1> 字符常量是用单引号(‘ ’)括起来的单个字符。例如:char v1 = 'a'、char v2 = '辉'、char v3 = '9'
2> Java中还允许使用转义字符来将其后的字符转变为特殊字符型常量。例如:char v3 = ‘\n' '\n'表示换行符
3> 在java中, char的本质是一个整数,在输出时,是unicode码对应的字符
4> 可以直接给char赋一个整数,然后输出时,会按照对应的unicode字符输出
5> char类型是可以进行运算的,相当于一个整数,因为它都对应有unicode码.
public class test { public static void main(String[] args) { char v1 = 'a'; char v2 = 98; char v3 = 'a' + 2; char v4 = '赵'; char v5 = '\t'; char v6 = '王'; System.out.println(v1+" "+v2+" "+v3); //a b c System.out.println(v1+v2+v3); //294 System.out.println((int)v1); //97 System.out.println(v4); //赵 System.out.println(v5); //换行符 System.out.println(v4 + v6); //65792 v4和v6本质上是整数,所以结果为将他的码值加起来 } }
字符型存储到计算机中,需要将字符对应的码值(整数)找出来,比如 'a'
存储:'a' ==> 码值97 ==> 二进制(110 0001) ==> 存储
读取:二进制(110 0001) ==> 97 ==> 'a' ==> 显示
ASCII:ASCIl编码表一个字节表示,一个128个字符,实际上一个字节可以表示256个字符,只用了128个
Unicode:Unicode编码表固定大小的编码使用两个字节来表示字符,字母和汉字统一都是占用两个字节,这样浪费空间
utf-8:大小可变的编码表,字母使用1个字节,汉字使用3个字节
gbk:可以表示汉字,而且范围广,字母使用1个字节,汉字2个字节
gb2312:可以表示汉字,gb2312< gbk
big5码:繁体中文、台湾、香港
ASCII码介绍
上个世纪60年代,美国制定了一套字符编码(使用一个字节),对英语字符与二进制位之间的关系,做了统一的规定。这被称为ASCII码
ASCII码一共规定了128个字符的编码,只占用了一个字节的后面7位,最前面的1位统一规定为0
特别提示:一个字节可以表示256个字符,ASCII码只用了128个字符.
缺点:不能表示所有字符。
Unicode编码介绍
Unicode的好处:一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,使用Unicode没有乱码的问题
Unicode的缺点:一个英文字母和一个汉字都占用2个字节,这对于存储空间来说是浪费
2的16次方是65536,所以汉字最多编码是65536个字符
编码0-127的字符是与ASCII的编码一样.比如'a'在ASCII码是0x61,在unicode码是ox0061,都对应97.因此Unicode码兼容ASCII码.
UTF-8编码介绍
UTF-8是在互联网上使用最广的一种 Unicode的实现方式(改进)
UTF-8是一种变长的编码方式。它可以使用1-6个字节表示一个符号,根据不同的符号而变化字节长度
使用大小可变的编码字母占1个字节,汉字占3个字节
布尔类型也叫boolean类型,在java中booolean类型数据只允许取值true和false,无null,同时不能使用其他字符代替true或false
boolean类型占1个字节,boolean类型适于逻辑运算,一般用于程序流程控制
public class test { public static void main(String[] args) { boolean var = true; if(var == true) { System.out.println("This is True."); } else { System.out.println("This is False"); } } }
当java程序在进行赋值或者运算时,精度小的类型自动转换为精度大的数据类型,这个就是自动类型转换。
数据类型按精度(容量)大小排序为
int a = 'a'; double = 80;
1> 有多种类型的数据混合运算时,系统首先自动将所有数据转换成容量最大的那种数据类型,然后再进行计算
2> 当我们把精度(容量)大的数据类型赋值给精度(容量)小的数据类型时,就会报错,反之就会进行自动类型转换
3> (byte, short)和char之间不会相互自动转换
4> byte,short,char他们三者可以计算,在计算时首先转换为int类型
5> boolean不参与转换
6> 自动提升原则:表达式结果的类型自动提升为操作数中最大的类型
//代码示例1 public class type_change { public static void main(String[] args) { byte a = 10; int b = 580; double c = 1314.20; double res = a + b + c; System.out.println(res); } } //代码示例2 public class type_change { public static void main(String[] args) { double c = 1314; System.out.println(c); } } //代码示例3 public class type_change { public static void main(String[] args) { byte a = 13; short b = 200; char c = 'a'; int res = a + b + c; System.out.println(res); } }
自动类型转换的逆过程,将容量大的数据类型转换为容量小的数据类型。使用时要加上强制转换符(),但可能造成精度降低或溢出。
public class type_change { public static void main(String[] args) { int a = 9527; byte res = (byte)a; System.out.println(res); } }
上图的res结果为55,int类型的9527转为二进制为:00000000 00000000 00100101 00110111,因为byte为一个字节,所以只会保留上述二进制中的最后8位,即00110111=55,这种现象称为精度降低
强制类型转换细节说明
1> 当进行转换的数据精度从大——>小,就需要使用到强制转换
2> 强转符号只针对于最近的操作数有效,往往会使用小括号提升优先级
3> char类型可以保存int的常量值,但不能保存int的变量值(byte同理),需要强转,因为变量的大小之前是固定好了的
4> byte和short、char类型在进行运算时,当做int类型处理
5> byte a = 100;在给变量a赋值的时候,首先会判断100在不在byte的范围内,如果在的话则就赋值,不在则报错。
#案例演示1 public class type_change { public static void main(String[] args) { int res = (int)10*3.5; //会报错 int res = (int)(10*3.5); System.out.println(res); } } //案例演示2 public class type_change { public static void main(String[] args) { int a = 97; char b = 97; //char可以保存int的常量值 char res = (char)a; //如果保存int的变量值则需要强转 System.out.println(res); //打印的是97对应的字母 } } //案例演示3 public class type_change { public static void main(String[] args) { byte a = 97; short b = 97; char c = 97; int res = a + b + c; System.out.println(res); //结果:291 } }
基本数据类型和String类型的转换
在程序开发中,我们经常需要将基本数据类型转成String类型。或者将String类型转成基本数据类型。
基本类型转String类型语法:将基本类型的值+""即可
public class HelloWorld { public static void main(String []args) { int a = 9527; String str = a + ""; System.out.println(str); //9527 System.out.println(str + 1); //95271 } }
String类型转基本数据类型
语法:通过基本类型的包装类调用parseXX方法即可
public class HelloWorld { public static void main(String []args) { String str = "88"; int a = Integer.parseInt(str); byte b = Byte.parseByte(str); short c = Short.parseShort(str); long d = Long.parseLong(str); float e = Float.parseFloat(str); double f = Double.parseDouble(str); boolean g = Boolean.parseBoolean("true"); System.out.println(a + 1); System.out.println(b + 1); System.out.println(c + 1); System.out.println(d + 1); System.out.println(e); System.out.println(f); System.out.println(g); } }
注意事项
在将String类型转成基本数据类型时,要确保String类型能够转成有效的数据,比如我们可以把“123",转成一个整数,但是不能把"hello”转成一个整数,如果格式不正确,就会抛出异常,程序就会终止。
public class test { public static void main(String[] args) { String name = "赵大大"; short age = 22; float grade = 99.99F; char gender = '男'; String hobby = "女"; System.out.println("姓名\t年龄\t成绩\t性别\t爱好\n" + name + "\t" + age + "\t" + grade + "\t" + gender + "\t" + hobby); } }
运算符是一种特殊的符号,用以表示数据的运算、赋值和比较等。
算术运算符、赋值运算符、关系运算符[比较运算符]、逻辑运算符、位运算符(需要二进制基础)、三元运算符
算术运算符是对数值类型的变量进行运算的,在Java程序中使用的非常多。
// /的使用 public class HelloWorld { public static void main(String []args) { System.out.println(10 / 4); //2 因为10是int,4也是int,所以结果也是int System.out.println(10.0 / 4); //2.5 运算式中最高级为double,所以结果也为double double a = 10 / 4; System.out.println(a); //2.0 } } // %的使用 %的本质:a - (a / b)取整 * b public class HelloWorld { public static void main(String []args) { System.out.println(10 % 3); //10 - (10 / 3)的取整 *3 = 10 - 9 = 1 System.out.println(-10 % 3); //-10 - (-10 / 3)的取整 *3 = -10 + 9 = -1 System.out.println(10 % -3); //10 - (10 / -3)的取整 *(-3) = 10 - 9 = 1 System.out.println(-10 % -3); //-10 - (-10 / -3)的取整 *(-3) = -10 + 9 = -1 } } // ++的使用 //作为独立语句使用时:i++、++i完全等价于i = i + 1 public class HelloWorld { public static void main(String []args) { int i = 10; i++; System.out.println("i =" + i); //11 ++i; System.out.println("i =" + i); //12 } } //作为表达式使用 ++i表示前++,先自增,后赋值 i++表示后++,先赋值,再自增 public class HelloWorld { public static void main(String []args) { int i = 10; int k = i++; System.out.println("i=" + i + " k=" + k); //i=11 k=10 i = 10; k = ++i; System.out.println("i=" + i + " k=" + k); //i=11 k=11 } } if (i++==6){…} //这里因为是后++,所以是先比较,后自增,这是一个表达式,是判断是否等于的
1> 对于除号"/",它的整数除和小数除是有区别的:整数之间做除法时,只保留整数部分而舍弃小数部分。例如:int x= 10/3 ,结果是3
2> 当对一个数取模时,可以等价 a % b = a - a / b * b,这样我们可以看到取模的一个本质运算。(a/b是java中的除法特点)
3> 当自增作为一个独立语言使用时,不管是++i、还是i++都是一样的,等价于i = i+ 1
4> 当自增作为一个表达式使用时:j = ++i 等价 i = i + 1、j = i
5> 当自增作为一个表达式使用时:j = i++ 等价 j = i、i = i + 1
算术运算符练习题
public class run { public static void main(String[] args) { int i = 1; i = i++; //规则使用临时变量:(1) temp=i、(2) i=i+1、(3)i=temp System.out.println(i); //打印结果:1 int k = 1; k = ++k; //规则使用临时变量:(1) k=k+1、(2) temp=k、(3)k=temp System.out.println(k); //打印结果:2 } }
//定义一个变量保存华氏温度,华氏温度转换摄氏温度的公式为: 5/9*(华氏温度-100),请求出华氏温度对应的摄氏温度。[234.5] public class test { public static void main(String[] args) { double huashi = 234.5; double sheshi = 5.0 / 9 * (huashi - 100); //注意这里必须是5.0,不然在java里5/9=0的 System.out.println(sheshi); } }
(int)(Math.random()*100) + 1; //生成1个1-100的随机数 Math.random()方法可以生成[0, 1)之间的数字,如果用这个数字乘以3就是一个从[0, 3)之间的数字
关系运算符也叫比较运算符,关系运算符的结果都是boolean型,也就是要么是true,要么是false
关系表达式经常用在if结构的条件中或循环结构的条件中
public class HelloWorld { public static void main(String []args) { int number_first = 10; int number_second = 20; System.out.println(number_first == number_second); //false System.out.println(number_first != number_second); //true System.out.println(number_first < number_second); //true System.out.println(number_first > number_second); //false System.out.println(number_first <= number_second); //true System.out.println(number_first >= number_second); //false boolean flog = number_first != number_second; //可以将关系表达式的结果赋给布尔类型的变量 System.out.println(flog); //true } }
1> 关系运算符的结果都是boolean型,也就是要么是true,要么是false
2> 关系运算符组成的表达式,我们称为关系表达式:a >b
3> 比较运算符"=="不能误写成"=","=="为等于。"="为赋值
用于连接多个条件(多个关系表达式),最终的结果也是一个boolean值。
逻辑运算符一览
短路与:&&
短路或:||
逻辑与:&
逻辑或:|
取反:!
逻辑异或:^
a & b : & 叫逻辑与,当a 和 b 同时为true,则结果为true,否则为 false
a && b : && 叫短路与,当a 和 b 同时为true,则结果为true,否则为 false
a l b:| 叫逻辑或,当a和 b ,有一个为true,则结果为true,否则为false
a || b :叫短路或,当a和 b,有一个为true,则结果为true,否则为false
! a:叫取反,或者非运算。当a为true,则结果为false;当a 为 false,结果为true
a ^ b:叫逻辑异或,当a 和 b不同时,则结果为true,否则为false
public class test { public static void main(String[] args) { int number_first = 10; int number_second = 20; System.out.println(!(number_first < number_second)); //false } }
public class test { public static void main(String[] args) { System.out.println((5 > 9) ^ (10 > 8)); //true } }
&&和&使用区别
&&短路与:如果第一个条件为false,则第二个条件不会执行判断,最终结果就是false,效率高
&逻辑与:不管第一个条件是否为false,第二个条件都要判断,效率低
开发中,我们基本使用&&
public class test { public static void main(String[] args) { int number_first = 10; int number_second = 20; if (number_first < 5 && ++number_second > 15) { System.out.println("条件为真"); } System.out.println("number_first=" + number_first + " number_second=" + number_second); //number_first=10 number_second=20 } }
如上图,因为 number_first < 5 为假,所以 ++number_second 就不会执行,如果为逻辑与 & 的话,就会执行 ++number_second了
| 和 || 使用区别
|| 短路或:如果第一个条件为true,则第二个条件不会执行判断,最终结果就为true,效率高
| 逻辑或:不管第一个条件是否为true,第二个条件都要判断,效率低
开发中,我们基本使用 ||
public class test { public static void main(String[] args) { int number_first = 10; int number_second = 20; if (number_first < 15 || ++number_second > 30) { System.out.println("条件为真"); } System.out.println("number_first=" + number_first + " number_second=" + number_second); //number_first=10 number_second=20 } }
如上图,因为 number_first < 15 为真,所以 ++number_second 就不会执行,如果为逻辑或 | 的话,就会执行 ++number_second了
逻辑运算符练习题
注意:x++==6,因为是后++,所以是先判断x==6,再进行自增,x++==6是一个表达式
所以答案如下:6,6 | 6,5 | 11,6 | 11,5
注意:(y=true)是一个赋值语句,&&判断的是赋值完之后的y的值
所以答案为50
赋值运算符就是将某个运算后的值,赋给指定的变量
基本赋值运算符:int a= 10
复合赋值运算符:+=、-=、*=、/=、%=
a += b:[等价a = a + b; ]
a -= b:[等价a = a - b; ]
1> 运算顺序从右往左int num = a + b + c;,先计算a+b+c的值再赋值给num变量
2> 赋值运算符的左边只能是变量,右边可以是变量、表达式、常量值
int num = 20、int num2= 78 * 34 - 10、 int num3 = a
3> 复合赋值运算符等价于下面的效果
比如: a += 3:等价于a = a + 3 其他类推
4> 复合赋值运算符会自动进行类型的强制转换
基本语法
条件表达式 ? 表达式1 : 表达式2 ;
运算规则
1、如果条件表达式为true,运算后的结果是表达式1
2、如果条件表达式为false,运算后的结果是表达式2
使用细节
1、表达式1和表达式2要为可以赋给接收变量的类型(或可以自动转换)
2、三元运算符可以转成 if else 语句
public class test { public static void main(String[] args) { int num_first = 1; int num_second = 20; int result = num_second > num_first ? 13.14 : 5.20 ; //会报错 int result = num_second > num_first ? (int)13.14 : (int)5.20 ; //会损失精度 double result = num_second > num_first ? 13.14 : 5.20 ; //nice System.out.println(result); } }
运算符有不同的优先级,所谓优先级就是表达式运算中的运算顺序
如下表,上一行运算符总优先于下一行。只有单目运算符、赋值运算符是从右向左运算的
标识符概念:Java对各种变量、方法和类等命名时使用的字符序列称为标识符
凡是自己可以起名字的地方都叫标识符 int number_first = 90 ; 中的num_first就是标识符
标识符的命名规则(必须遵守)
1.由26个英文字母大小写、0-9、_ 或 $ 组成
2、数字不可以开头 (int 3ab = 1; 是错误)
3、不可以使用关键字和保留字,但能包含关键字和保留字
4、Java中严格区分大小写,长度无限制。int abc 与 int Abc 是两个变量
5、标识符不能包含空格。(int a b = 90; 是错误的)
标识符的命名规范(更加专业)
1、包名:多单词组成时所有字母都小写: aaa.bbb.ccc 比如com.zrh.crm
2、类名、接口名:多单词组成时,所有单词的首字母大写:XxxYyyZzz【大驼峰】比如:TankShotGame
3、变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写: xxxYyyZz 【小驼峰,简称驼峰法】比如:tankShotGame
4、常量名:所有字母都大写,多单词时每个单词用下划线连接:XXX_YYY_ZZZ 比如:定义一个所得税率TAX_RATE
关键字:被Java语言赋予了特殊含义,用做专门用途的字符串(单词),特点:关键字中所有字母都为小写
Java保留字:现有Java版本尚未使用,但以后版本可能会作为关键字使用
自己命名标识符时要避免使用这些保留字byValue、cast、future、 generic、 inner、 operator、 outer、 rest、var 、 goto 、const
import java.util.Scanner; //1、导入java.util包下的Scanner类 public class input { public static void main(String[] args) { Scanner myScanner = new Scanner(System.in); //2、创建Scanner对象,new=创建一个对象 System.out.print("请输入您的姓名:"); //3、使用相关的方法接收用户的输入 String name = myScanner.next(); //接收用户输入的字符串,当程序执行到next时会等待用户输入 System.out.print("请输入您的年龄:"); int age = myScanner.nextInt(); //接收用户输入的int System.out.print("请输入您的薪水:"); double salary = myScanner.nextDouble(); //接收用户输入的double System.out.print("您的信息如下:\n姓名:" + name +"\t年龄:" + age + "\t薪水:" + salary ); //打印输入的信息 } }
对于整数,有四种表示方式:
二进制: 0,1,满2进1,以0b或0B开头
十进制:0-9,满10进1
八进制:0-7,满8进1,以数字0开头表示。
十六进制:0-9及A(10)-F(15),满16进1,以0x或0X开头表示, 此处的AF不区分大小写。
public class test { public static void main(String[] args) { int numBinary = 0b11001010; //二进制 int numOctal = 0571; //八进制 int numHex = 0xD5; //十六进制 System.out.print("numBinary =" + numBinary + "\tnumOctal=" + numBinary + "\tnumHex=" + numHex); //numBinary =202 numOctal=202 numHex=213 } }
二进制转十进制规则:从最低位(右边)开始,将每个位上的数提取出来,乘以2的(位数-1)次方,然后求和
0b1011=1*2^0+1*2^1+0*2^2+1*2^3=11
八进制转十进制:从最低位(右边)开始,将每个位上的数提取出来,乘以8的(位数-1)次方,然后求和
0234=4*8^0+3*8^1+2*8^2=156
十六进制转十进制规则:从最低位(右边)开始,将每个位上的数提取出来,乘以16的(位数-1)次方,然后求和
0X23A=10*16^0+3*16^1+2*16^2=570
十进制转二进制规则:将该数不断除以2,直到商为0为止,然后将每步得到的余数倒过来,就是对应的二进制
158=158/2=79……0、79/2=39……1、39/2=19……1、19/2=9……1、9/2=4……1、4/2=2……0、2/2=1……0;所以158=ob10011110
十进制转八进制规则:将该数不断除以8,直到商为0为止,然后将每步得到的余数倒过来,就是对应的八进制
131=131/8=16……3、16/8=2……0;所以131=0203
十进制转十六进制规则:将该数不断除以16,直到商为0为止,然后将每步得到的余数倒过来,就是对应的十六进制
237=237/16=14……13;所以237=0XED
二进制转八进制规则:从低位开始,将二进制数每三位一组,转成对应的八进制数即可
0b11010110=011、010、110=3、2、6=0326
二进制转十六进制规则:从低位开始,将二进制数每四位一组,转成对应的十六进制数即可
0b11010110=1101、0110=13、6=0XD6
八进制转二进制规则:将八进制数每1位,转成对应的一个3位的二进制数即可
0237=2、3、7=010、011、111=ob10011111
十六进制转二进制规则:将十六进制数每1位,转成对应的4位的一个二进制数即可
0X23B=2、3、B=0010、0011、1011=ob1000111011
对于有符号的而言:
1> 二进制的最高位是符号位:0表示正数、1表示负数
2> 正数的原码,反码,补码都一样(三码合一)
3> 负数的反码=它的原码符号位不变,其它位取反(0变1、1变0)
4> 负数的补码=它的反码+1,负数的反码=负数的补码-1
5> 0的反码,补码都是0
6> java没有无符号数,换言之,java中的数都是有符号的
7> 在计算机运算的时候,都是以补码的方式来运算的
8> 当我们看运算结果的时候,要看他的原码
#例如:int 2 int 2 的源码=00000000 00000000 00000000 00000010 因为2是正数,所以2的原码、补码、反码都一样,三码合一 #例如:int -2 int -2的源码=10000000 00000000 00000000 00000010 int -2的反码=11111111 11111111 11111111 11111101 int -2的补码=11111111 11111111 11111111 11111110
java中有7个位运算:&、|、^、~、>>、<<、>>>
按位与 &:两位全为1,结果为1,否则为0
按位或 |:两位有一个为1,结果为1,否则为0
按位异或 ^:两位一个为0,一个为1,结果为1,否则为0
按位取反 ~:0变1 ,1变0
算术右移 >>:低位溢出,符号位不变,并用符号位补溢出的高位
算术左移<<:符号位不变,低位补0
逻辑右移>>>:也叫无符号右移,低位溢出,高位补0
public class test { public static void main(String[] args) { //在计算机运算的时候,都是以补码的方式来运算的 //当我们看运算结果的时候,要看他的原码 //2&3 //1、2的原码:00000000 00000000 00000000 00000010 //2、因为2是正数,所以三码合一 //3、3的原码:00000000 00000000 00000000 00000011 //4、因为3是正数,所以三码合一 //&表示两位全为1,结果为1,否则为0 //所以 2&3的补码为:00000000 00000000 00000000 00000010 //2 //因为2&3的补码符号位为0,是正数,正数三码合一,所以原码就为上述补码 System.out.println(2 & 3); //结果为:2 //~-2 //1、-2的原码:10000000 00000000 00000000 00000010 //2、-2为负数,负数要得到补码首先需要得到反码(负数的反码=原码符号位不变,其它位取反) //-2的反码:11111111 11111111 11111111 11111101 //3、-2的补码=负数的补码=它的反码+1 //-2的补码:11111111 11111111 11111111 11111110 //4、~-2的补码:00000000 00000000 00000000 00000001 //1 //5、-2的补码符号位为0,是正数,所以三码合一,所以原码也为上述补码 System.out.println(~-2); //结果为:1 //~2 ////1、2的原码:00000000 00000000 00000000 00000010 ///2、2为正数,所以三码合一 ///2的补码:00000000 00000000 00000000 00000010 ///3、~2的补码:11111111 11111111 11111111 11111101 ///4、~2的补码符号位为1,是负数,所以不能三码合一,需要从补码推导致原码 ///5、负数的反码=负数的补码-1 ///~2的反码:11111111 11111111 11111111 11111100 ///6、负数的原码=符号位不变,反码其他位再取返 ///~2的原码:10000000 00000000 00000000 00000011 //-3 System.out.println(~2); //结果为:-3 } }
public class test { public static void main(String[] args) { // 1 >> 2 算数右移 // 1、1的原码=00000000 00000000 00000000 00000001 // 1为正数,三码合一 // 1的补码:00000000 00000000 00000000 00000001 // 2、>>2表示低位溢出2位,符号位不变,并用符号位补溢出的高位 // 即最低位的01被去除,最前面就空了两位,空的两位用符号位补全 // 00000000 00000000 00000000 00000000 //0 System.out.println(1 >> 2); //结果为:0 //1 << 2 算术左移 // 1、1的原码=00000000 00000000 00000000 00000001 // 1为正数,三码合一 // 1的补码:00000000 00000000 00000000 00000001 // 2、<<2表示符号位不变,低位补2个0 // 00000000 00000000 00000000 00000100 //4 System.out.println(1 << 2); //结果为:4 } }
所以: 1 >> 2 的本质就是 1 / 2 / 2 = 0 、1 << 2 的本质就是 1 * 2 * 2 = 4
在程序中,程序运行的流程控制决定程序是如何执行的,是我们必须掌握的,主要有三大流程控制语句:顺序控制、分支控制、循环控制
顺序控制:程序从上到下逐行地执行,中间没有任何判断和跳转
让程序有选择的的执行,分支控制有三种
//1、单分支if public class HelloWorld { public static void main(String[] args) { int age = 20; if (age > 18){ System.out.println("你的年龄大于18,请为自己的行为负责!"); } } } //2、双分支if-else public class test { public static void main(String[] args) { int yearNum = 2020; if ((yearNum % 4 == 0 && yearNum % 100 != 0) || yearNum % 400 == 0){ System.out.print("此年份是闰年");} else { System.out.print("此年份不是闰年"); } } } //3、多分支if-else if -...-else public class test { public static void main(String[] args) { int grade = 95; if (grade >= 100){ System.out.print("信用极好"); } else if (80 < grade && grade <= 99) { System.out.print("信用优秀"); } else if (60 <= grade && grade<= 80) { System.out.print("信用一般"); } else { System.out.print("信用不及格"); } } }
1> 多分支可以没有else,如果所有的条件表达式都不成立,则一个执行入口都没有
2> 如果有else ,如果所有的条件表达式都不成立,则默认执行else代码块
在一个分支结构中又完整的嵌套了另一个完整的分支结构,里面的分支的结构称为内层分支外面的分支结构称为外层分支,建议不要超过3层,可读性不好
public class test { public static void main(String[] args) { double score = 9.5; if (score > 8.0) { char gender = '女'; //myScanner.next().charAt(0)本质是将字符串的第一个字符转成char if (gender == '男') { System.out.print("恭喜你进入男子组决赛"); } else if (gender == '女') { System.out.print("恭喜你进入女子组决赛"); } else { System.out.print("性别错误,无法参加决赛"); } } else { System.out.println("Sorry,你已被淘汰"); } } }
import java.util.*; public class test { public static void main(String[] args) { Scanner myScanner = new Scanner(System.in); System.out.print("请输入字符:"); char val = myScanner.next().charAt(0); //在java中只要是有值返回的都可以称为表达式 switch(val){ case 'a': System.out.println("星期一"); //break; case 'b': System.out.println("星期二"); //break; case 'c': System.out.println("星期三"); //break; case 'd': System.out.println("星期四"); //break; case 'e': System.out.println("星期五"); //break; default: System.out.println("周末时间"); } } }
1> switch关键字,表示swtich分支
2> 表达式对应一个值
3> case常量1:当表达式的值等于常量1,就执行语句块1
4> break 表示退出swtich
5> 如果和case常量1匹配,就执行语句块1,如果没有匹配,就继续匹配case常量2
6> 如果一个都没有匹配上,执行default
switch结构细节
1> 表达式数据类型,应和case后的常量类型一致,或者是可以自动转成可以相互比较的类型,比如输入的是字符,常量是int就可以,因为char可以自动转为int
2> switch中的表达式返回值必须是:(byte、short、int、char、enum[枚举]、String)
public class test { public static void main(String[] args) { double c = 1.1; switch(c){ //错误,后面类型不符合规定 case 1.1: //错误,后面类型不符合规定 System.out.println( "ok3"); break; } } }
3> case子句中的值必须是常量或常量表达式,而不能是变量
4> default子句是可选的,当没有匹配的case时,执行default
5> break语句用来在执行完一个case分支后使程序跳出switch语句块;如果没有写break,程序会顺序执行到switch结尾,除非遇到break;
//根据用于指定月份,打印该月份所属的季节 //3.4.5春季、6.7.8夏季、9.10.11秋季、12.1.2冬季 //[必须使用Switch且必须使用穿透] public class test { public static void main(String[] args) { int c = 8; switch(c){ case 3: case 4: case 5: System.out.print("春季"); break; case 6: case 7: case 8: System.out.print("夏季"); break; case 9: case 10: case 11: System.out.print("秋季"); break; case 12: case 1: case 2: System.out.print("冬季"); break; default: System.out.print("输入月份有误"); } } }
switch 和 if 的选择
1> 如果判断的具体数值不多,而且符合byte、short 、int、char、enum[枚举]、String这6种类型。虽然两个语句都可以使用,建议使用swtich 语句
2> 其他情况:对区间判断,对结果为boolean类型判断,使用if,if的使用范围更广
1> for 关键字,表示循环控制
2> for有四要素①循环变量初始化②循环条件③循环操作④循环变量迭代
3> 循环操作,这里可以有多条语句,也就是我们要循环执行的代码
4> 如果循环操作(语句)只有一条语句,可以省略 { },建议不要省略
public class test { public static void main(String[] args){ for (int i = 1 ; i < 10 ; i++){ // i < 10 如果改成 true 就会变成死循环,说到底就是一个布尔表达式 System.out.println("Hello,World" + i ); } } }
for循环细节
1> 循环条件是返回一个布尔值的表达式
2> for( ; 循环判断条件 ; )中的循环变量初始化和变量迭代可以写到其它地方,但是两边的分号不能省略
#循环变量初始化 如果写到for外面,那么之后还可以使用循环变量的值,如果循环变量写到for循环里,那么就只能在for循环里面使用 public class test { public static void main(String[] args){ int i = 1; //循环变量初始化写到外面 for ( ; i < 10 ; ){ System.out.println("Hello,World" + i ); i++; } System.out.print(i); //for循环外还可以调用i变量 } }
循环初始值可以有多条初始化语句,但要求类型一样,并且中间用逗号隔开,循环变量迭代也可以有多条变量迭代语句,中间用逗号隔开
public class test { public static void main(String[] args){ for (int i = 1, j = 0 ; i < 10 ;i += 2, j++ ){ System.out.println("i=" + i +" j=" + j ); } } }
for循环练习
//打印1~100之间所有是9的倍数的整数,统计个数及总和. public class test { public static void main(String[] args) { System.out.println("符合条件的整数如下:"); int i = 1; int count = 0; int sum = 0; for( ; i <= 100 ; i++){ if(i % 9 == 0){ count++; sum += i; System.out.print(i + "\t"); } } System.out.print("\n统计个数为:" + count + "\n总和为:" + sum); } }
for( ; ; ;){ } //表示无限循环,也叫死循环,可以配合break一起使用
while循环也有四要素,只不过四要素的位置与for循环不一样而已
细节说明
1> 循环条件是返回一个布尔值的表达式
2> while循环是先判断再执行语句
public class test { public static void main(String[] args) { //打印1-100之间所有能被3整除的数,使用while int number = 1; while (number <= 100) { switch(number % 3){ case 0: System.out.print(number +"\t"); } number++; } } }
1> do while是关键字
2> 也有循环四要素,只是位置不一样
3> 先执行,再判断,也就是说,一定会至少执行一次
4> 最后有一个分号
public class test { public static void main(String[] args){ int i = 1; //循环变量初始化 do{ System.out.println("i = " + i); //循环代码块 i++; //循环变量迭代 } while (i<10); //循环条件 } }
1> 循环条件是返回一个布尔值的表达式
2> do...while循环是先执行,再判断,因此至少执行一次
//打印1—100之间能被5整除但不能被3整除的数 public class test { public static void main(String[] args){ int i = 1; //循环变量初始化 do{ if(i % 5 == 0 && i % 3 != 0){ //循环代码块 System.out.print(i + "\t"); } //循环代码块 i++; //循环变量迭代 } while (i<=100); //循环条件 } }
多重循环也叫嵌套循环,将一个循环放在另一个循环体内,就形成了嵌套循环。其中for、while、do..while 均可以作为外层循环和内层循环。【建议一般使用两层,最多不要超过3层,否则,代码的可读性很差】
实质上,嵌套循环就是把内层循环当成外层循环的循环体。当只有内层循环的循环条件为false时,才会完全跳出内层循环,才可结束外层的当次循环,开始下一次的循环
设外层循环次数为m次,内层为n次,则内层循环体实际上需要执行m*n次
//打印9*9乘法表 public class test { public static void main(String[] args){ for(int i = 1 ; i <= 9 ; i++){ for(int j = 1 ; j <= i ; j++){ System.out.print(j + "*" + i + "=" + (i*j) + " " );} System.out.print("\n"); } } }
//打印空心金字塔 import java.util.*; public class test { public static void main(String[] args){ Scanner myScanner = new Scanner(System.in); System.out.print("请输入打印图案的层数:"); int layer = myScanner.nextInt(); for(int i = 1 ; i <= layer ; i++){ for(int k = 1 ; k <= layer - i ; k++){ //此for循环是为了打印每一行开头的空格 System.out.print(" "); } for(int j = 1 ; j <= 2 * i - 1 ; j++){ //此for循环是为了打印每一行的* //上图中的2 * i - 1表示每一行对应打印*和空格的个数 if((i == layer && j % 2 == 1) || j == 1 || j == 2*i-1){ //打印最后一行的*或者打印每一行的第一个和最后一个位置的* System.out.print("*"); } else System.out.print(" "); //将每一行除了第一个和最后一个以外的所有位置打印成空格 } System.out.println(""); //每打完一层的*和空格就换行 } } }
//打印菱形 import java.util.*; public class test { public static void main(String[] args){ Scanner myScanner = new Scanner(System.in); System.out.print("请输入打印图案的层数:"); int layer = myScanner.nextInt() / 2 + 1; //打印上半部分 for(int i = 1 ; i <= layer ; i++){ for(int k = 1 ; k <= layer - i ; k++){ //此for循环是为了打印每一行开头的空格 System.out.print(" "); } for(int j = 1 ; j <= 2 * i - 1 ; j++){ //此for循环是为了打印每一行的* //上图中的2 * i - 1表示每一行对应打印*和空格的个数 if(j == 1 || j == 2*i-1){ //打印最后一行的*或者打印每一行的第一个和最后一个位置的* System.out.print("*"); } else System.out.print(" "); //将每一行除了第一个和最后一个以外的所有位置打印成空格 } System.out.println(""); //每打完一层的*和空格就换行 } //打印下半部分 for(int i = 1 ; i <= layer - 1 ; i++){ for(int k = 1 ; k <= i ; k++){ //此for循环是为了打印每一行开头的空格 System.out.print(" "); } for(int j = 1 ; j <= (2 * layer - 3) - 2 * (i - 1); j++){ //此for循环是为了打印每一行的* //上图中的(2 * layer - 3) - 2 * (i - 1)表示每一行对应打印*和空格的个数 if(j == 1 || j == (2 * layer - 3) - 2 * (i - 1)){ //打印最后一行的*或者打印每一行的第一个和最后一个位置的* System.out.print("*"); } else System.out.print(" "); //将每一行除了第一个和最后一个以外的所有位置打印成空格 } System.out.println(""); //每打完一层的*和空格就换行 } } }
统计3个班成绩情况,每个班有5名同学,求出各个班的平均分和所有班级的平均分,并统计三个班及格人数(学生的成绩从键盘输入)
1> 化繁为简
①要统计三个班的成绩情况,可以先统计一个班的成绩情况,因为每个班有5个学生,每个学生的成绩都是键盘输入,所以可以设置for循环输入5次
②成绩输入完后计算平均分,可以定义一个double sum把5名学生的成绩进行累和,然后除以人数就可以得到一个班平均值
③一个班处理完了,可以处理三个班,一个班的代码框架完成后,可以在代码框架外加一个循环3次的for循环,这样就可以得到3个班的成绩情况
④要计算三个班的平均分,可以定义一个变量double totalScore 来累和每个班成绩,然后除以三个班的人数就是三个班的平均值
⑤统计三个班的及格人数,可以设置一个变量int count,如果输入学生的成绩>=60,那么就对count++,最后打印count即可
2> 先死后活
①上面所说每班5个学生,就做了5次的for循环,可以将5设置成变量
②上面所说3个班级,就做了3次的for循环,可以将3设置成变量
先死后活的含义就是先将内容写死,然后通过变量将内容替换,这样就可以修改变量的内容达到灵活的目的
break语句用于终止某个语句块的执行,一般使用在switch或者循环 [ for、while、do - while ] 中
public class test { public static void main(String[] args) { label1: for(int i = 1 ; i <= 3 ; i++){ label2: for(int k = 1 ; k <= 3 ; k++){ if(i == 2 && k == 2){ break; //break label1; } System.out.println("i= " + i + " k=" + k); } } } }
break语句出现在多层嵌套的语句块中时,可以通过标签指明要终止的是哪一层语句块
1> break语句可以指定退出哪层
2> label1是标签,名字由程序员指定
3> break后指定到哪个label就退出到哪里
4> 在实际的开发中,尽量不要使用标签.
5> 如果没有指定break,默认退出最近的循环体
上图代码将break label1取消注释以后,结果如下:
//实现登录验证,有3次机会,如果用户名为"丁真",密码"666"提示登录成功,否则提示还有几次机会,请使用for+break import java.util.Scanner; public class test{ public static void main(String[] args){ int oppo = 3; Scanner myScanner = new Scanner(System.in); String username = ""; String password = ""; for(int i = 1 ; i <= 3 ; i++){ System.out.print("请输入用户名:"); String username = myScanner.next(); System.out.print("请输入密码:"); String password = myScanner.next(); if("丁真".equals(username) && "123".equals(password)){ System.out.println("登陆成功"); break; } else { oppo--; System.out.println("登陆失败,剩余" + oppo + "次机会"); } } } }
注意:判断两个字符安川是否相等需要用到String下的equals方法
"丁真".equals(username)
1> continue语句用于结束本次循环,继续执行下一次循环
2> contimue语句出现在多层嵌套的循环语句体中时,可以通过标签指明要跳过的是哪一层循环﹐这个和前面的标签的使用的规则一样
public class test{ public static void main(String[] args){ label1: for(int i = 1 ; i <= 3 ; i++){ label2: for(int k = 1 ; k <= 3 ; k++){ if(k == 2){ continue; //等价于continue label2;结果为打印三次k=1、k=2 //continue label1;结果为打印三次k=1 } System.out.println("k=" + k); } } } }
return使用在方法上,表示跳出所在的方法,在讲解方法的时候,会详细的介绍,这里我们简单的提一下
注意:如果return 写在 main方法,表示退出程序
public class test{ public static void main(String[] args){ int i = 1; do{ if(i == 2){ return; } System.out.println("i=" + i); i++; }while(i <= 3); System.out.println("循环结束"); //return之后这句话就不会被执行 } }
//计算 1-1/2+1/3-1/4......1/100 的和 public class test{ public static void main(String[] args){ double sum = 0.0; for(int i = 1 ; i <= 100 ; i++){ if (i % 2 == 0){ sum -= (1.0 / i); //注意1.0,注意java中/的陷阱 }else{ sum += (1.0 / i);} } System.out.println("和=" + sum); } } //求 1+(1+2)+(1+2+3)+(1+2+3+4)+...+(1+2+...+100)的结果 //第一种 public class test{ public static void main(String[] args){ int sum = 0; int res = 0; for(int i = 1 ; i <= 100 ; i++){ sum += i; res += sum; } System.out.println("result=" + res); } } //第二种 public class test{ public static void main(String[] args){ int sum = 0; for(int i = 1 ; i <= 100 ; i++){ for(int k = 1 ; k <= i ; k++){ sum += k; } } System.out.println("result=" + res); } }
数组可以存放多个同一类型的数据,数组也是一种数据类型,是引用类型,即:数组就是一组数据
public class test{ public static void main(String[] args){ int arr[] = new int[5]; //int表示数组为int类型 //arr表示数组的名称 //[]表示数据 //new int[5]表示在内存中创建数组,数组可以存放5个元素 System.out.print(arr.length);//arr.length表示arr数组的长度 arr[0] = 520; //给数组的第一个元素赋值,数组的索引是从0开始的 System.out.print(arr[0]); } }
1> 动态初始化,声明创建一步到位
public class test{ public static void main(String[] args){ int arr[] = new int[5]; //int arr[] 等价于 int[] arr //创建了一个数组,名字叫arr,可以存放5个int } }
2> 动态初始化,先声明,后创建
public class test{ public static void main(String[] args){ //int[] arr;也可以 int arr[]; //先声明数组 arr = new int[5]; //再创建数组 } }
3> 静态初始化
使用情况:知道数组有多少元素且知道元素具体的值
public class test{ public static void main(String[] args){ //int[] arr;也可以 int arr[] = {1,2,3}; //以上一句代码相当于 //int arr[] = new int[3]; //arr[0] = 1; //arr[1] = 2; //arr[2] = 3; } }
注意:String strs[ ] = new String[ ]{"a","b","c"}; //这样是正确的 String strs[ ] = new String[ 3 ]{"a","b","c"}; //这样是错误的
1> 数组是多个相同类型数据的组合(自动类型转换的也可以放在同一个数组里),实现对这些数据的统一管理
2> 数组中的元素可以是任何数据类型,包括基本类型和引用类型,但是不能混用
3> 数组创建后,如果没有赋值,有默认值
int 0、short 0、byte 0、long 0、float 0.0、double 0.0、char /u0000、boolean false、String null
4> 使用数组的步骤:①声明数组并开辟空间、②给数组各个元素赋值、③使用数组
5> 数组的下标是从0开始的
6> 数组下标必须在指定范围内使用,否则报:下标越界异常,比如
7> 数组属引用类型,数组型数据是对象(object)
//创建一个char类型的26个元素的数组,分别放置'A‘-'Z',使用for 循环访问所有元素并打印出来 public class test{ public static void main(String[] args){ char array[] = new char[26]; for(int i = 0 ; i <= 25 ; i++){ array[i] = (char)('A' + i); //array[]是数组类型,array[1]是char类型 System.out.print(array[i] + " "); } } }
在引用传递的赋值机制下,修改array_second元素的值是会影响到array_first元素的值的,因为array_firs和array_second指向的是同一个内存地址
如果想要拷贝一个相互不影响的数组,那么就需要用拷贝的赋值机制对数组进行拷贝,操作如下:
新建一个数组,数组的长度与需要拷贝的数组相同,再将需要拷贝的数组元素的值一 一赋给新建数组的元素的值即可
public class test{ public static void main(String[] args){ int array_first[] = {1,3,5,7,9}; int array_second[] = new int[array_first.length]; for(int i = 0 ; i < array_first.length ; i++){ array_second[i] = array_first[i];} System.out.println("array_first的地址:" + array_first); //[I@15db9742 System.out.println("array_second的地址:" + array_second); //[I@6d06d69c } }
要求:把数组的元素内容反转
//根据规律反转 public class Test { public static void main(String[] args) { int arrary_first[] = {1,2,3,4,5,6}; int temp = 0; for(int i = 0 ; i < arrary_first.length / 2 ; i++){ temp = arrary_first[i]; arrary_first[i] = arrary_first[arrary_first.length - i - 1]; arrary_first[arrary_first.length - i - 1] = temp; } for(int i = 0 ; i < arrary_first.length ; i++){ System.out.print(arrary_first[i] + " "); } } } //使用逆序赋值反转 public class Test { public static void main(String[] args) { int arrary_first[] = {1,2,3,4,5,6}; int arrary_second[] = new int[arrary_first.length]; for(int i = arrary_first.length - 1 ; i >= 0 ; i--){ arrary_second[arrary_first.length - i - 1] = arrary_first[i]; } arrary_first = arrary_second; for(int i = 0 ; i < arrary_first.length ; i++){ System.out.print(arrary_first[i] + " "); } } }
//实现动态的给数组添加元素效果,实现对数组扩容 //用户可以通过如下方法来决定是否继续添加:添加成功,是否继续? y/n import java.util.Scanner; public class test{ public static void main(String[] args){ int array[] = {1,2,3}; Scanner myScanner = new Scanner(System.in); System.out.println("原始数组内容如下:"); for(int i = 0 ; i < array.length ; i++){ System.out.print(array[i]+" "); } System.out.println(""); do{ int newarray[] = new int[array.length + 1]; for(int i = 0 ; i < array.length ; i++){ newarray[i] = array[i]; } System.out.print("请输入增加的元素:"); int add = myScanner.nextInt(); newarray[array.length] = add ; array = newarray; System.out.print("添加成功,是否继续添加? y/n:"); char val = myScanner.next().charAt(0); if(val != 'y') break; } while(true); System.out.println("修改后数组内容如下:"); for(int i = 0 ; i < array.length ; i++){ System.out.print(array[i]+" "); } } }
//有一个数组{1,2,3, 4,5},可以将该数组进行缩减,提示用户是否继续缩减,每次缩减最后那个元素。当只剩下最后一个元素:提示不能再缩减 import java.util.Scanner; public class test{ public static void main(String[] args){ int array[] = {1,2,3,4,5}; Scanner myScanner = new Scanner(System.in); System.out.println("原始数组内容如下:"); for(int i = 0 ; i < array.length ; i++){ System.out.print(array[i]+" "); } System.out.print("\n准备就绪,是否开始缩减? y/n:"); char val_t = myScanner.next().charAt(0); if(val_t != 'y') return; do{ int newarray[] = new int[array.length - 1]; for(int i = 0 ; i < array.length - 1 ; i++){ newarray[i] = array[i]; } array = newarray; System.out.print("缩减成功,是否继续缩减? y/n:"); char val = myScanner.next().charAt(0); if(val != 'y') break; if(array.length == 1){ System.out.println("数组仅剩一个元素,无法继续缩减"); break; } } while(true); System.out.println("修改后数组内容如下:"); for(int i = 0 ; i < array.length ; i++){ System.out.print(array[i]+" "); } } }
排序是将多个数据,依指定的顺序进行排列的过程
排序的分类:
内部排序:指将需要处理的所有数据都加载到内部存储器中进行排序。包括(交换式排序法、选择式排序法和插入式排序法)
外部排序法:数据量过大,无法全部加载到内存中,需要借助外部存储进行排序。包括(合并排序法和直接合并排序法)
冒泡排序法
冒泡排序(Bubble Soting)的基本思想是:通过对待排序序列从后向前(从下标较大的元素开始〉,依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就像水底下的气泡一样逐渐向上冒。
将五个无序: 24,69,80,57,13 使用冒泡排序法将其排成一个从小到大的有序数列
public class test{ public static void main(String[] args){ int array[] = {24,69,80,57,13}; int temp = 0; int time = 0; for(int k = 1 ; k < array.length ; k++){ time = 0; //防止已经排好序,所以用来直接终止循环 for(int i = 0 ; i < array.length - k ; i++){ if(array[i] > array[i + 1]){ temp = array[i]; array[i] = array[i + 1]; array[i + 1] = temp; time++; //插个眼 } } if(time == 0) break; //眼==0,就代表顺序已经排好了 } for(int i = 0 ; i < array.length ; i++){ System.out.print(array[i]+" "); } } }
1> 从定义形式上看:int[ ][ ] name 或者 int name[ ][ ]
2> 二维数组的每一个元素是一个一维数组
二维数组的每个元素是一维数组,所以如果需要得到每个一维数组的值还需要再次遍历
访问第(i+1)个一维数组的第j+1个值:arr[i][j];
arr[ i ]表示二维数组的第i+1个元素比如:arr[0]表示二维数组的第一个元素
arr.length表示二维数组的长度、arr[ i ].length得到对应的每个一维数组的长度
1> 动态初始化,声明创建一步到位
public class test{ public static void main(String[] args){ int array[][] = new int[2][3]; } }
2> 动态初始化,先声明,后创建
public class test{ public static void main(String[] args){ int array[][]; //先声明 array = new int[2][3]; //后创建 } }
3> 动态初始化,列数不确定
创建二维数组,二维数组中有3个一维数组,但是每个一维数组还没有开辟数据空间
public class test{ public static void main(String[] args){ int array[][] = new int[3][]; //没有给二维数组中的一维数组开辟空间 array[0] = new int[2]; array[1] = new int[2]; array[2] = new int[2]; //以上四句等价于 array[][] = new int[3][2]; } }
public class test{ public static void main(String[] args){ int array[][] = new int[3][]; //没有创建一维数组元素的个数 for(int i = 0 ; i < array.length ; i++){ array[i] = new int[i + 1]; //给一维数组开辟空间 for(int k = 0 ; k< array[i].length ; k++){ array[i][k] = i + 1; //给一维数组的元素赋值 System.out.print(array[i][k] + " "); } System.out.println(); } } }
4> 静态初始化
public class test{ public static void main(String[] args){ int array[][] = new {{1,2,3},{1,2},{1}}; //定义了一个二维数组 //二维数组有三个元素,每个元素都是一维数组 //第一个一维数组有3个元素、第二个有2个元素、最后一个有一个元素 } }
1> 一维数组的声明方式有:int[ ] name或者int name[ ]
2> 二维数组的声明方式有:int[ ][ ] name 或者 int [ ] name[ ] 或者int name[ ][ ]
3> 二维数组实际上是由多个一维数组组成的,它的各个一维数组的长度可以相同,也可以不相同
比如:map[ ][ ]是一个二维数组
int map[ ][ ]= {{1,2},{3,4,5}}
map[0]是一个含有两个元素的一维数组,map[1]是一个含有三个元素的一维数组构成,我们也称为列数不等的二维数组
int[ ] x,y[ ] //表示x是int类型一维数组,y是int类型的二维数组
import java.util.Scanner; public class test{ public static void main(String[] args){ Scanner myScanner = new Scanner(System.in); System.out.print("请输入打印杨辉三角的行数:"); int val = myScanner.nextInt(); //接收杨辉三角的行数 int array[][] = new int[val][]; //未开辟一维数组空间 for(int i = 0 ; i < array.length ; i++){ array[i] = new int[i + 1]; //开辟一维数组的空间 for(int j = 0 ; j < array[i].length ; j++){ if(j == 0 || j == array[i].length - 1){ //首位和末尾打印1 array[i][j] = 1; }else{array[i][j] = array[i - 1][j] + array[i - 1][j - 1];} System.out.print(array[i][j] + " "); } System.out.println(); } } }
//已知有个升序的数组,要求插入一个元素,该数组顺序依然是升序 //比如:[10,12,45,90],添加23后,数组为[10,12,23,45,90] import java.util.Scanner; public class test{ public static void main(String[] args){ Scanner myScanner = new Scanner(System.in); System.out.print("请输入插入的数:"); int val = myScanner.nextInt(); int temp = 0; int array[] = {10,12,45,90,99,102,129,168}; int array_temp[] = new int[array.length + 1]; for(int i = 0 ; i < array.length ; i++){ if(array[i] <= val){array_temp[i] = array[i];} else if(array[i] > val){ temp++; //i + 1的目的是为了将array_temp的i空出来给val array_temp[i + 1] = array[i]; if(temp == 1){ //temp等于1的时候对应的i就是val的索引 array_temp[i] = val; } } } //防止val的值比array每一个元素的值都大 if(array_temp[array.length] == 0){ array_temp[array.length] = val; } array = array_temp; for(int i = 0 ; i < array.length ; i++){ System.out.print(array[i] + " "); } } }
类是抽象的,概念的,代表一类事物,比如人类、猫类、狗类......
类是数据类型;对象是具体的、实际的,代表一个具体事物,即是实例
类是对象的模板,对象是类的一个 个体,对应一个实例
public class test{ public static void main(String[] args){ Cat hk = new Cat(); //创建一个对象 hk.name = "flower"; hk.age = 2; hk.color = "white";
Cat xk = hk; System.out.print("这只猫的信息为:" + hk.name + " " + hk.age + " " + hk.color); } } class Cat{ //定义 Cat 类 String name; int age; String color; }
非基本数据类型都存放在方法区的常量池中,基本数据类型就直接保存在类所在的内存地址里
类和对象的内存分配机制,Java内存的结构分析
1> 栈:一般存放基本数据类型(局部变量)
2> 堆:存放对象(Cat hk,数组等)
3> 方法区:常量池(常量,比如字符串),类加载信息
注意:如果xk = null; 这代表xk置空,置空之后xk与创建的对象就失去了链接
从概念或叫法上看:成员变量= 属性 = field(字段) (即成员变量是用来表示属性的,本文中统一叫属性)
class Cat{ //定义 Cat 类 String name; //成员变量、属性、字段 int age; String color; String[] hosts; //属性可以是基本数据类型,也可以是引用类型(对象,数组) }
注意事项和细节说明
1> 属性的定义语法同变量,示例:访问修饰符 属性类型 属性名;
简单的介绍访问修饰符:控制属性的访问范围
有四种访问修饰符public、proctected、默认、 private
2> 属性的定义类型可以为任意类型,包含基本类型或引用类型
3> 属性如果不赋值,有默认值,规则和数组一致
具体说: int 0、short 0、 byte 0、 long 0、float 0.0、double 0.0、char /u0000、boolean false、String null
public class test{ public static void main(String[] args){ Cat hk = new Cat(); //hk是对象名,也叫对象引用 //new Cat()为创建的对象空间,是真正的对象,对象名只是指向了这个对象空间而已 } }
创建对象及访问属性
创建对象1:先声明,再创建 Cat cat ; //先声明对象cat cat = new Cat(); //创建 创建对象2:直接创建 Cat cat = new Cat(); //如何访问属性 对象名.属性名; cat.name; cat.age; cat.color;
Java创建对象的流程简单分析
1> 先加载Cat类信息(属性和方法信息,只会加载一次)
2> 在堆中分配空间,进行默认初始化(int 0、double 0.0.....)
3> 把地址赋给hk,hk就指向对象
4> 进行指定初始化,比如 hk.name ="flower" hk.age = 2
1> 方法写好后,如果不去调用(使用),不会输出
2> 先创建对象,然后调用方法即可
public class test{ public static void main(String[] args){ Person hk = new Person(); //创建一个对象 hk.speak(); //调用speak方法 } } class Person{ //定义 Cat 类 String name; int age; //1、public表示方法公开 //2、void表示方法没有返回值 //3、speak()中speak为方法名,()为形参列表 //4、{ } 方法体,可以写我们要执行的代码体 public void speak(){ System.out.print("This is a good man."); } }
方法快速入门
public class test{ public static void main(String[] args){ Person hk = new Person(); //创建一个对象 hk.cal(500); //调用cal方法,同时传入参数,即n=500 int result = hk.getSum(10,20); System.out.println(result); } } class Person{ //定义 Cat 类 String name; int age;
//定义speak方法 public void speak(){ System.out.println("This is a good man."); } //定义cal方法 public void cal(int n){ //表示当前有一个 形参 n(形式参数),可以接受用户输入 int sum = 0; for(int i = 1 ; i <= n ; i++){ sum += i; } System.out.println("1+2+...+" + n + "=" + sum); } //定义getSum方法 //1、piblic后的int表示方法执行后,返回一个 int 值 //2、(int num1 , int num2)表示形参列表,可以接受用户传入两个参数 //3、return表示将结果返回 public int getSum(int num1 , int num2){ int res = num1 + num2; return res; } }
方法的调用机制
这里使用getSum方法进行演示
方法调用总结
1> 当程序执行到方法时,就会开辟一个独立的空间(栈空间)
2> 当方法执行完毕,或者执行到return语句时,就会返回相应的结果到调用方法的地方
3> 返回后,栈空间被垃圾回收机制回收,然后继续执行方法后面的代码
4> 当main方法(栈)执行完毕,整个程序退出
1> 形参列表:表示成员方法输入的参数 cal(int n)、getSum(int num1, int num2)
2> 返回数据类型:表示成员方法输出,void表示没有返回值
3> 方法主体:表示为了实现某一功能代码块
4> return语句不是必须的
成员方法的好处:提高代码的复用性、可以将实现的细节封装起来,然后供其他用户来调用即可
方法细节及注意事项
返回数据类型
1> 一个方法最多有一个返回值,如果需要返回多个值可以返回数组
2> 返回类型可以为任意类型,包含基本类型或引用类型(数组,对象)
public class test{ public static void main(String[] args){ myTools tools = new myTools(); //创建对象 int[] result = tools.printArr(20,10); System.out.println("result[0]=" + result[0]); System.out.println("result[1]=" + result[1]); } } class myTools{ public int[] printArr(int num1 , int num2){ //返回数据类型为数组 int[] res = new int[2]; //创建数组 res[0] = num1 + num2; res[1] = num1 - num2; return res; //返回数组 } }
3> 如果方法要求有返回数据类型,则方法体中最后的执行语句必须为 return 值,而且要求返回值类型必须和 return 的值类型一致或兼容
4> 如果方法是void,则方法体中可以没有retun语句,或者只写return ;(即return后没有返回值)
方法名
遵循驼峰命名法,最好见名知义,表达出该功能的意思即可,比如得到两个数的和getSum,开发中按照规范
形参列表
1> 一个方法可以有0个参数,也可以有多个参数,中间用逗号隔开,比如:getSum(int n1,int n2)
2> 参数类型可以为任意类型,包含基本类型或引用类型,比如:printArr(int[ ][ ] map)
3> 调用带参数的方法时,一定对应着参数列表传入相同类型或兼容类型的参数
4> 方法定义时的参数称为形式参数,简称形参;方法调用时的传入参数称为实际参数,简称实参,实参和形参的类型要一致或兼容、个数、顺序必须一致
方法体
方法体里面写完成功能的具体的语句
可以为输入、输出、变量、运算、分支、循环、方法调用,但里面不能再定义方法!
即:方法不能嵌套定义,(只是不能嵌套定义,但是方法体里可以调用其他方法)
1> 同一个类中的方法调用:直接调用即可
案例演示:myTools类中的sayOk方法调用printArr方法
public class test{ public static void main(String[] args){ myTools tools = new myTools(); //创建对象 tools.sayOk(520); } } class myTools{ //定义printArr方法 public void printArr(int num){ //无数据类型 System.out.println("你输入的数字为=" + num); } //定义sayOk方法 public void sayOk(int num){ System.out.println("sayOk方法已调用"); printArr(num); //调用同一个类的printArr方法 } }
2> 跨类中的方法A类调用B类方法:需要通过对象名调用,比如:对象名.方法名(参数);
案例演示:myTools类 的 sayHello方法 调用 herTools类 的 printArr方法
public class test{ public static void main(String[] args){ myTools tools = new myTools(); //创建对象 tools.sayHello(520); } } class myTools{ //定义sayHello方法 public void sayHello(int num){ System.out.println("sayHello方法已调用"); herTools temp = new herTools(); temp.printArr(num); //调用不同类的printArr方法 } } class herTools{ //定义printArr方法 public void printArr(int num){ //无返回数据类型 System.out.println("你输入的数字为=" + num); } }
3> 特别说明:跨类的方法调用和方法的访问修饰符相关,后面讲到访问修饰符时再细说
1> 基本数据类型,传递的是值(值拷贝),形参的任何改变不影响实参
public class test{ public static void main(String[] args){ Mytools tools = new Mytools(); int n = 10; int m = 20; tools.swap(n , m); System.out.print("main方法的:n=" + n + " m=" + m); //main方法的:n=10 m=20 } } class Mytools{ public void swap(int n , int m){ int temp = n; n = m; m = temp; System.out.println("swap方法的:n=" + n + " m=" + m); //swap方法的:n=20 m=10 } }
2> 引用类型传递的是地址(传递也是值,但值是地址),可以通过形参的改变影响实参
public class test{ public static void main(String[] args){ Mytools tools = new Mytools(); int[] array = {10,20,30}; tools.arrayChange(array); System.out.print("main方法:array[0]=" + array[0]); //main方法:array[0]=100 } } class Mytools{ public void arrayChange(int[] array){ array[0] = 100; //将数组的第一位改为100 System.out.println("arrayChange方法:array[0]=" + array[0]); //arrayChange方法:array[0]=100 } }
编写一个方法copyPerson,可以复制一个Person对象,返回复制的对象,即克隆对象
注意:要求得到新对象和原来的对象是两个独立的对象,只是他们的属性相同
public class test{ public static void main(String[] args){ Person p = new Person(); p.name = "King"; p.age = 22; Mytools tools = new Mytools(); Person p2 = tools.copyPerson(p); System.out.println("P的属性为:" + "p.name=" + p.name + " p.age=" + p.age); System.out.println("P2的属性为:" + "p2.name=" + p2.name + " p2.age=" + p2.age); System.out.println(p == p2); //判断p与p2是否属于同一对象空间 } } class Person{ String name; int age; } class Mytools{ //编写方法的思路 //1、方法的返回类型 Person,属于一个类(int 是 100 的类) //2、方法的名字 copyPerson //3、方法的形参是一个对象,对象又是Person类的实例,所以为 Person p public Person copyPerson(Person p){ Person p2 = new Person(); p2.name = p.name; p2.age = p.age; return p2; } }
递归就是方法自己调用自己,每次调用时传入不同的变量
public class test{ public static void main(String[] args){ Recursion temp = new Recursion(); temp.printWord(4); //传入初始参数为4 } } class Recursion{ public void printWord(int number){ if(number > 2){ printWord(number - 1); //自己调用自己 } System.out.println("number=" + number); } }
递归细节
1、执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
2、方法的局部变量是独立的,不会相互影响,比如上图中的number变量
3、如果方法中使用的是引用类型变量(比如数组、对象),就会共享该引用类型的数据,那么局部变量之间就会受到影响
4、递归必须向退出递归的条件逼近,否则就是无限递归,出现StackOverflowError
5、当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕(栈空间也就被回收)
//请使用递归的方式求出斐波那契数1,1,2,3,5,8,13...给你一个整数n,求出斐波那契的第n个数的值是多少? //思路分析 //1、当n = 1斐波那契数是1 //2、当n = 2斐波那契数是1 //3、当n>= 3斐波那契数是前两个数的和 //4、这里就是一个递归的思路 // public class test{ public static void main(String[] args){ Recursion temp = new Recursion(); int res = temp.fobi(7); System.out.print(res); } } class Recursion{ public int fobi(int n){ if(n == 1 || n == 2){ return 1; } else { return fobi(n - 1) + fobi(n - 2); } } }
//老鼠出迷宫 public class Maze{ public static void main(String[] args){ //打印迷宫,0表示可以走,1表示障碍物 int[][] array = new int[8][7]; for(int i = 0 ; i < array.length ; i++){ for(int j = 0 ; j < array[i].length ; j++){ if(i == 0 || i == array.length - 1){ array[i][j] = 1; } } array[i][0] = 1; array[i][6] = 1; } array[3][1] = 1; array[3][2] = 1; //打印原迷宫 System.out.println("====原迷宫如下===="); for(int i = 0 ; i < array.length ; i++){ for(int j = 0 ; j < array[i].length ; j++){ System.out.print(array[i][j] + " "); } System.out.println(); } Mytools temp = new Mytools(); //创建Mytools对象 temp.findWay(array,1,1); //打印迷宫出路 System.out.println("====迷宫出路如下===="); for(int i = 0 ; i < array.length ; i++){ for(int j = 0 ; j < array[i].length ; j++){ System.out.print(array[i][j] + " "); } System.out.println(); } } } class Mytools{ //1、findWay方法用来找迷宫的出路,如果找到返回true,否则返回false //2、接收的map就是用来表示迷宫的二维数组 //3、(i,j)用来表示小老鼠的位置,初始位置为(1,1) //4、规定递归过程中map数组中元素各个值的含义 // 0表示可以走、1表示障碍物、2表示可以走、3表示走过,但是此路不通 //5、当map[6][5] == 2 就说明找到了通路,否则就继续找 public boolean findWay(int[][] map , int i , int j){ if(map[6][5] == 2){ //说明找到了通路 return true; } else { if(map[i][j] == 0){ //当前位置等于0,说明可以走 map[i][j] = 2; //如果此路可以走通,那么2就是我们的轨迹 //使用找路策略查看此路是否真的可以走通 ////找路策略:下---右---上---左 if(findWay(map , i + 1 , j)){ //先向下走 return true; } else if(findWay(map , i , j + 1)){ //再向右走 return true; } else if(findWay(map , i - 1 , j)){ //再向上走 return true; } else if(findWay(map , i , j - 1)){ //再向左走 return true; } else{ //上下左右都走不通 map[i][j] = 3; return false; } } else return false; //map[i][j] == 1或者2或者3都返回false } } }
java 中允许在同一个类中,定义多个同名的方法,但要求形参列表不一致
重载的好处:1、减轻了起名的麻烦 2、减轻了记名的麻烦
public class test{ public static void main(String[] args){ Mytools tools = new Mytools(); System.out.println(tools.m(5)); System.out.println(tools.m(10,50)); tools.m("HK9527"); } } class Mytools{ public int m(int num){ return num * num; } public int m(int num1 , int num2){ return num1 * num2; } public void m(String str){ System.out.print(str); } }
注意事项和使用细节
1、方法名必须相同
2、形参列表必须不同(形参类型或个数或顺序,至少有一样不同,参数名无要求)
3、返回类型无要求,及相同与否都可以
4、在调用重载方法时,优先调用传入参数与方法定义参数的类型未发生类型转换的方法
java允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法,就可以通过可变参数实现
访问修饰符 返回类型 方法名(数据类型...形参名){
}
1、可变参数的实参可以为0个或任意多个
2、可变参数的实参可以为数组
3、可变参数的本质就是数组
4、可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
5、一个形参列表中只能出现一个可变参数
public class test{ public static void main(String[] args){ Mytools tools = new Mytools(); tools.showScore("HK9527",85,95,87,96,88); } } class Mytools{ public void showScore(String name , double... score){ double sum = 0; for(int i = 0 ; i < score.length ; i++){ //score.length就可以表现可变参数的本质为数组 sum += score[i]; } System.out.println("姓名:" + name); System.out.println("总成绩:" + sum); } }
1、在java编程中,主要的变量就是属性(成员变量)和局部变量
2、我们说的局部变量一般是指在成员方法中定义的变量
3、java中作用域的分类
全局变量:也就是属性,作用域为整个类体
局部变量:也就是除了属性之外的其他变量,作用域为定义它的代码块中
4、全局变量(属性)可以不赋值,直接使用,因为有默认值,局部变量必须赋值后,才能使用,因为没有默认值
注意事项和细节使用
1、属性和局部变量可以重名,访问时遵循就近原则
public class test{ public static void main(String[] args){ Mytools tools = new Mytools(); tools.var(); } } class Mytools{ String name = "ZRH"; //属性/全局变量 public void var(){ String name = "KING"; //局部变量 System.out.println(name) //输出结果:KING } }
2、在同一个作用域中,比如在同一个成员方法中,两个局部变量不能重名
3、属性生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁
局部变量,生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而销毁。
4、作用域范围不同
全局变量/属性:可以被本类使用,或其他类使用(通过对象调用)
局部变量:只能在本类对应的方法中使用
public class test{ public static void main(String[] args){ Mytools tools = new Mytools(); tools.var(); Histools htools = new Histools(); htools.temp(tools); //其他类通过传入对象参数调用 } } class Mytools{ String name = "ZRH"; //属性/全局变量 public void var(){ int age = 10; //局部变量 System.out.println(name); //在本类的方法中使用全局变量 System.out.println(age); //只能在本类对应的方法中使用 } } class Hertools{ public void temp(){ Mytools value = new Mytools(); System.out.println(value.name); //其他类通过创建对象调用 } } class Histools{ public void temp(Mytools tools){ System.out.println(tools.name); //其他类通过传入对象参数调用 } }
5、修饰符不同
全局变量/属性可以加修饰符,局部变量不可以加修饰符
class Mytools{ public String name = "ZRH"; //这样写是正确的,属性/全局变量可以加修饰符 public void var(){ public int age = 10; //这样写是错误的,局部变量不可以加修饰符,会报错 } }
构造方法又叫构造器(constructor),是类的一种特殊的方法,它的主要作用是完成对新对象的初始化
[修饰符] 方法名 (形参列表) {
方法体;
}
它有几个特点:
1> 方法名和类名相同
2> 没有返回值
3> 在创建对象时,系统会自动的调用该类的构造器完成对象的初始化
4> 参数列表和成员方法一样的规则
5> 构造器的调用,由系统完成
6> 构造器的修饰符可以默认,也可以是public、protected、private
public class test{ public static void main(String[] args){ Mytools tools = new Mytools("Zrh"); System.out.println(tools.name); //打印结果:Zrh } } class Mytools{ String name; //属性/全局变量 Mytools(String test_name){ name = test_name; } }
1> 一个类可以定义多个不同的构造器,即构造器重载
比如:我们可以再给Mytools类定义一个构造器,用来创建对象的时候只指定人名,不需要指定年龄
public class test{ public static void main(String[] args){ Mytools first_tools = new Mytools("Zrh");//只指定姓名 System.out.println(first_tools.name); //打印结果:Zrh Mytools second_tools = new Mytools("Zrh" , 22);//指定姓名和年龄 System.out.println(second_tools.name + " " + second_tools.age); //打印结果:Zrh 22 } } class Mytools{ String name; //属性/全局变量 int age; Mytools(String test_name){ name = test_name; } Mytools(String test_name , int test_age){ //构造器重载 name = test_name; age = test_age; } }
2> 构造器名和类名要相同
3> 构造器没有返回值
4> 构造器是完成对象的初始化,并不是创建对象
5> 在创建对象时,系统自动调用该类的构造方法
6> 如果程序员没有定义构造器,系统会自动给类生成一个默认无参构造器(也叫默认构造器),使用javap指令反编译可以查看
7> 一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器,除非显式的定义一下(这点很重要)
public class test{ public static void main(String[] args){ Mytools first_tools = new Mytools("Zrh");//只指定姓名 Mytools second_tools = new Mytools();//只有显示定义了无参构造器才可以继续这样使用 } } class Mytools{ String name; //属性/全局变量 int age; Mytools(String test_name){ name = test_name; } Mytools(){ //无参构造器 } }
class Person{//类Person int age=90; String name; Person(String n,int a){ //构造器 name=n; //给属性赋值 age=a; // } } Person p=new Person("小倩",20);
1> 加载Person类信息(Person.class),只会加载一次
2> 在堆中分配空间(地址)
3> 完成对象初始化
①默认初始化age=0 name=null
②显式初始化age=90,name=null
③构造器的初始化 age =20, name=小倩
4> 在对象在堆中的地址,返回给p(p是对象名,也可以理解成是对象的引用)
java虚拟机会给每个对象分配 this用来代表当前对象
1> this关键字可以用来访问本类的属性、方法、构造器
2> this用于区分当前类的属性和局部变量
3> 访问成员方法的语法: this.方法名(参数列表)
4> 访问构造器语法:this(参数列表);注意只能在构造器中使用(即只能在构造器中访问另外一个构造器且必须放在第一条语句)
public class test { public static void main(String[] args){ Mytools tools = new Mytools(); } } class Mytools{ String name; int age; Mytools(){ this("Zrh" , 22); //访问构造器语法必须放置在第一条语句 System.out.println("Mytools()构造器被调用"); } Mytools(String name , int age){ System.out.println("Mytools(String name , int age)构造器被调用"); } }
5> this 不能在类定义的外部使用,只能在类定义的方法中使用
6> this简单的说,哪个对象调用,this就代表哪个对象
public class test { public static void main(String[] args){ Mytools tools = new Mytools(); tools.print("Zrh" , 22); System.out.println(tools.name + " " + tools.age); } } class Mytools{ String name; int age; public void print(String name , int age){ this.name = name; //this表示当前对象tools this.age = age; } }
匿名对象就是没有名字的对象,使用一次即会销毁
public class temp{ int count = 9; public void count1(){ count = 10; } public static void main(String[] args){ new temp().count1(); //匿名对象存在于堆中,但是只能用一次 } }
设计类的成员变量,成员方法,可以电脑猜拳,电脑每次都会随机生成0、1、2
0表示石头、1表示剪刀、2表示布,并要可以显示输赢次数
import java.util.Scanner; class game{ public void finger(){ Scanner Myscanner = new Scanner(System.in); int i = 0; int j = 0; do{ System.out.println("请输入你的猜拳:(0=石头、1=剪刀、2=布、3=退出)"); int res = Myscanner.nextInt(); int temp = (int) (Math.random() * 3); if(res == 3) break; if (temp - 1 == res || res - 2 == temp) i++; else j++; System.out.println("电脑随机猜拳:" + temp + "\n你胜利的次数:" + i + "\t你失败的次数:" + j ); }while (true); } }
1、编码设置
2、文件保存内容介绍
3、自动导入所需要的类
4、配置运行快捷键
5、生成构造器
6、查看一个类的层级关系ctrl +H
7、将光标放在一个方法上,输入ctrl +B,可以定位到方法的位置
8、创建对象时自动分配变量名
import java.util.Scanner; public class test{ int age; String name; public static void main(String[] args){ //new Scanner(System.in).var 然后回车 Scanner scanner = new Scanner(System.in); } }
9、模板
10、去掉形参提示符
包的本质实际上就是创建不同的文件夹/目录来保存类文件
包的三大作用
1、区分相同名字的类
2、当类很多时,可以很好的管理类
3、控制访问范围
快速入门
包的命名规则
只能包含数字、字母、下划线、小圆点,但不能用数字开头,不能是关键字或保留字
命名规范
一般是小写字母+小圆点一般是 com.公司名.项目名.业务模块名
举例: com.sina.crm.user //用户模块 com.sina.crm.order//订单模块 com.sina.crm.utils //工具类
注意事项和使用细节
1、package的作用是声明当前类所在的包,需要放在类的最上面,一个类中最多只有一句package
2、 import指令位置放在package的下面,在类定义前面,可以有多句且没有顺序要求
java 提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问(范围)
1、公开级别:用public修饰,对外公开
2、受保护级别:用protected修饰,对子类和同一个包中的类公开
3、默认级别:没有修饰符号,向同一个包的类公开
4、私有级别:用private修饰,只有类本身可以访问,不对外公开
四种访问修饰符的访问范围
注意:子类可以访问父类的默认方法/属性的前提是在同一个包中
package com.first; public class A { public int num1 = 10; protected int num2 = 20; int num3 = 30; private int num4 = 40; public void Am() { System.out.println(num1 + " " + num2 + " " + num3 + " " + num4); } } package com.first; public class B { public void Bm(){ A a = new A(); System.out.println(a.num1 + " " + a.num2 + " " + a.num3); } } package com.second; public class A { public void An(){ com.first.A a = new com.first.A(); System.out.println(a.num1); } }
注意事项
1、修饰符可以用来修饰类中的属性,成员方法以及类
2、只有默认的和public才能修饰类!,并且遵循上述访问权限的特点
3、成员方法的访问规则和属性完全一样
封装(encapsulation)就是把抽象出的数据 [属性] 和对数据的操作 [方法] 封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作 [方法],才能对数据进行操作
1、封装的理解和好处
隐藏实现细节
可以对数据进行验证,保证安全合理
2、封装的实现步骤
将属性进行私有化private【使用户不能直接修改属性】
提供一个公共的(public) set方法,用于对属性判断并赋值
public void setXxx(类型参数名){//Xxx表示某个属性 //加入数据验证的业务逻辑 属性=参数名; }
3、提供一个公共的(public) get方法,用于获取属性的值
public 数据类型 getXxx(){ //可以加一些权限判断 return xx; }
4、案例
请大家看一个小程序,不能随便查看人的姓名,年龄,工资等隐私,并对设置的年龄进行合理的验证
年龄合理就设置,否则给默认年龄18,必须在1-120
年龄,工资不能直接查看,name的长度在2-6字符之间
package com.encapsulation; public class Show { public static void main(String[] args) { Person person = new Person(); person.setName("HK9527"); person.setAge(22); person.setSalary(9999.99); System.out.println(person.name); //这样会报错的! System.out.println(person.getName() + " " + person.getAge() + " " + person.getSalary()); } } class Person{ private String name; private int age; private double salary; public String getName() { return name; } public void setName(String name) { if(name.length() >= 2 &&name.length() <= 6){ this.name = name; } else { System.out.println("输入的名称长度有误"); this.name = "No One"; } } public int getAge() { return age; } public void setAge(int age) { if(age >= 0 && age <= 120){ this.age = age; } else { System.out.println("输入的年龄范围不合法"); this.age = -1; } } public double getSalary() { return salary; } public void setSalary(double salary) { //在这可以加一些权限验证之类的 this.salary = salary; } }
5、快速设置set、get方法
6、封装与构造器
package com.encapsulation; public class Show { public static void main(String[] args) { Person person = new Person(); person.setName("HK9527"); person.setAge(22); person.setSalary(9999.99); System.out.println(person.getName() + " " + person.getAge() + " " + person.getSalary()); Person person1 = new Person("ZRH",888,8888); System.out.println(person1.getName() + " " + person1.getAge() + " " + person1.getSalary()); } } class Person{ private String name; private int age; private double salary; //构造器与封装相结合 public Person(){ } public Person(String name , int age , double salary){ setName(name); //传入参数后继续调用set方法即可 setAge(age); setSalary(salary); } public String getName() { return name; } public void setName(String name) { if(name.length() >= 2 &&name.length() <= 6){ this.name = name; } else { System.out.println("输入的名称长度有误"); this.name = "No One"; } } public int getAge() { return age; } public void setAge(int age) { if(age >= 0 && age <= 120){ this.age = age; } else { System.out.println("输入的年龄范围不合法"); this.age = -1; } } public double getSalary() { return salary; } public void setSalary(double salary) { //在这可以加一些权限验证之类的 this.salary = salary; } }
继承可以解决代码复用,让我们的编程更加靠近人类思维
当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过 extends来声明继承父类即可
package com.inherit; public class A { String name; int age; double salary; public void info(){ System.out.println(name + " " + age + " " + salary); } public void cash(){ System.out.println(name + "涨工资了"); } } package com.inherit; public class B extends A{ public void test_b(){ System.out.println("小学生" + name + "正在考试"); } } package com.inherit; public class C extends A { public void test_c(){ System.out.println("大学生" + name + "正在考试"); } } package com.inherit; public class temp { public static void main(String[] args) { B b = new B(); b.name = "银角大王"; //A类的共有属性name b.age = 20; //A类的共有属性age b.salary = 5000; //A类的共有属性salary b.info(); //A类的共有方法info b.test_b(); //B类的特有方法test_b b.cash(); //A类的共有方法cash C c = new C(); c.name = "金角大王"; //A类的共有属性name c.age = 30; //A类的共有属性age c.salary = 8000; //A类的共有属性salary c.info(); //A类的共有方法info c.test_c(); //C类的特有方法test_c c.cash(); //A类的共有方法cash } }
1> 子类继承了父类所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法不能在子类直接访问,要通过父类提供的公共方法去访问
package com.details; public class father { public String name; protected int age; double height; private double weight; public void f1(){ System.out.println("public f1()方法被调用"); } protected void f2(){ System.out.println("protected f2()方法被调用"); } void f3(){ System.out.println("f3()方法被调用"); } private void f4(){ System.out.println("private f4()方法被调用"); } //定义访问 private double weight; 的public方法 public void setWeight(double weight){ this.weight = weight; } public double getWeight(){ return weight; } //定义调用 private void f4() 的public方法 public void useF4(){ f4(); } } package com.details; public class son { public static void main(String[] args) { father father = new father(); father.name = "King"; father.age = 20; father.height = 182.5; //father.weight; 这样是访问不了的 father.setWeight(65.5); System.out.println(father.getWeight()); //这样可以访问 father.f1(); father.f2(); father.f3(); //father.f4(); 这样是访问不了的 father.useF4(); //这样可以访问 } }
2> 子类必须调用父类的构造器,完成父类的初始化
package com.details; public class father { father(){ System.out.println("father()构造器被调用"); } } package com.details; public class son extends father{ son(){ //这里存在super(); System.out.println("son()构造器被调用"); } } package com.details; public class test { public static void main(String[] args) { son son = new son(); //运行结果: //father()构造器被调用 //son()构造器被调用 } }
3> 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器
如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过
package com.details; public class father { //没有指定无参构造器,如果定义了构造器,那么原来的默认无参构造器就会失效,除非显式的定义一下 father(String name , int age){ System.out.println("father(String name , int age)构造器被调用"); } } package com.details; public class son extends father{ son(String name , int age){ super("tom" , 20); //在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作 System.out.println("son(String name , int age)构造器被调用"); } } package com.details; public class test { public static void main(String[] args) { son son = new son("name" , 20); //运行结果: //father(String name , int age)构造器被调用 //son(String name , int age)构造器被调用 } }
4> 如果希望指定去调用父类的某个构造器,则显式的调用一下: super(参数列表)
package com.details; public class father { //在父类中定义了三个构造器 father(String name , int age){ System.out.println("father(String name , int age)构造器被调用"); } father(){ System.out.println("father()构造器被调用"); } father(String name){ System.out.println("father(String name)构造器被调用"); } } package com.details; public class son extends father{ //在子类中定义三个构造器,每个构造器都可以用super去指定不同父类的构造器 son(String name , int age){ super("tom" , 20); System.out.println("son(String name , int age)构造器被调用"); } son(String name){ super("tom"); System.out.println("son(String name)构造器被调用"); } son(){ super(); System.out.println("son()构造器被调用"); } } package com.details; public class test { public static void main(String[] args) { son son = new son("name" , 20); //运行结果: //father(String name , int age)构造器被调用 //son(String name , int age)构造器被调用 } }
5> super在使用时,必须放在构造器第一行(super只能在构造器中使用,不能再成员方法中使用)
6> super()和 this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
7> java所有类都是Object类的子类,Object是所有类的父类
8> 父类构造器的调用不限于直接父类!将一直往上追溯直到object类(顶级父类)
9> 子类最多只能继承一个父类(指直接继承),即 java中是单继承机制
思考:如何让A类继承B类和C类?
【A继承B,B继承C】
10> 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系
查找时要按照查找关系来返回信息
1> 首先看子类是否有该属性,如果子类有这个属性,并且可以访问,则返回信息
2> 如果子类没有这个属性,就看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息)
3> 如果父类没有就按照(2)的规则,继续找上级父类,直到Object
编写Computer类,包含CPU、内存、硬盘等属性,getDetails方法用于返回Computer的详细信息
编写PC子类,继承Computer类,添加特有属性【品牌 brand】
编写exercise类,在main方法中创建PC对象,分别给对象中特有的属性赋值,以及从Computer类继承的属性赋值,并使用方法并打印输出信息
package com.homework; public class computer { private String Cpu; private String Memory; private String Disk; public computer(String cpu, String memory, String disk) { this.Cpu = cpu; this.Memory = memory; this.Disk = disk; } public String getDetails(){ return "Cpu=" + Cpu + " Memory=" + Memory + " Disk=" + Disk; } public String getCpu() { return Cpu; } public void setCpu(String cpu) { Cpu = cpu; } public String getMemory() { return Memory; } public void setMemory(String memory) { Memory = memory; } public String getDisk() { return Disk; } public void setDisk(String disk) { Disk = disk; } } package com.homework; public class Pc extends computer{ private String brand; public Pc(String cpu, String memory, String disk, String brand) { //这里体现了继承设计的基本思想 super(cpu, memory, disk); //父类的构造器完成父类属性初始化 this.brand = brand; //子类的构造器完成子类属性初始化 } public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } public void info(){ System.out.println(getDetails() + " Brand=" + brand); } } package com.homework; public class exercise { public static void main(String[] args) { Pc pc = new Pc("i7","32GB","2T SSD","lenovo"); pc.info(); } }
super代表父类的引用,用于访问父类的属性、方法、构造器
1> 访问父类的属性,但不能访问父类的private属性
2> 访问父类的方法,不能访问父类的private方法
3> 访问父类的构造器,只能放在构造器里,且为第一句,只能出现一句!
package com.super_; public class father { public int age = 10; protected int height = 175; int salary = 8000; private int weight = 65; father(){ System.out.println("father()构造器被调用"); } father(int age){ System.out.println("father(int age)构造器被调用"); } public void f1(){ System.out.println("public void f1() 方法被调用"); } private void f2(){ System.out.println("private void f2() 方法被调用"); } } package com.super_; public class son extends father{ son(){ //访问父类构造器的语法:super(参数列表); super(20); //只能放在构造器里,且为第一句,只能出现一句! System.out.println("son()构造器被调用"); } public void s1(){ int age = super.age; //访问属性的语法:super.属性名; int height = super.height; int salary = super.salary; //super.weight 是私有属性,所以访问不了 System.out.println(age + " " + height + " " + salary); } public void s2(){ super.f1(); //访问方法的语法:super.方法名(参数列表); //super.f2()是私有方法,所以访问不了 } } package com.super_; public class run { public static void main(String[] args) { son son = new son(); son.s1(); son.s2(); } }
1> 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super;如果没有重名,使用super、this、直接访问是一样的效果
2> super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;
如果多个基类(上级类)中都有同名的成员,使用super访问遵循就近原则(根据查找的顺序),当然也需要遵守访问权限的相关规则
package com.super_; public class father { public int age = 80; protected int height = 175; int salary = 8000; private int weight = 80; public void public1(){ System.out.println("father类的public void public1()被调用"); } public void public2(){ System.out.println("father类的public void public2()被调用"); } } package com.super_; public class son extends father{ int age = 20; int height = 175; int salary = 8000; int weight = 80; public void public1(){ System.out.println("son类的public void public1()被调用"); } public void info(){ public1(); //找public1()方法时(public1()和 this.public1())顺序是: //1、先找本类,如果有,则调用 //2、如果没有,则找父类(父类如果有,并可以调用,则调用) //3、如果父类没有,则继续找父类的父类,整个规则,就是一样的,直到Object类 //提示:如果查找方法的过程中,找到了,但是不能访问(private),则报错cannot access //如果查找方法最终都没有找到,则提示方法不存在 super.public2(); //找public2()方法时(super.public2();)顺序是: //1、直接跳过本类,去找父类 //2、父类如果有,并可以调用,则调用 //3、如果父类没有,则继续找父类的父类,整个规则,就是一样的,直到Object类 //提示:如果查找方法的过程中,找到了,但是不能访问(private),则报错cannot access //如果查找方法最终都没有找到,则提示方法不存在 System.out.println(age); //查找属性的原理与方法一样 System.out.println(this.age); System.out.println(super.age); //运行结果: //son类的public void public1()被调用 //father类的public void public1()被调用 //20 //20 //80 } } package com.super_; public class run { public static void main(String[] args) { son son = new son(); son.info(); } }
方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名称、返回类型、形参列表一样,那么就说子类的这个方法覆盖了父类的方法
package com.super_; public class father { public void m1(){ //方法名称、返回类型、型参列表都一样 System.out.println("father类的public void m1()被调用"); } } package com.super_; public class son extends father{ public void m1(){ //方法名称、返回类型、型参列表都一样 System.out.println("son类的public void m1()被调用"); } }
方法重写也叫方法覆盖,需要满足下面的条件
1>子类的方法的形参列表、方法名称,要和父类方法的形参列表方法名称完全一样
2> 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类(比如父类返回类型是 Object ,子类方法返向类型是String,String是Object的子类)
package com.super_; public class father { public AAA m1(){ return null; } } class AAA{ //定义AAA类 } class BBB extends AAA{ //BBB是AAA的子类 } package com.super_; public class son extends father{ public BBB m1(){ //方法名称、返回类型BBB是AAA的子类、型参列表都一样 return null; } }
3> 子类方法不能缩小父类方法的访问权限 public > protected >默认>private
方法或对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承基础之上的
方法的多态:方法的重写和重载就体现多态
对象的多态
1> 一个对象的编译类型和运行类型可以不一致
2> 编译类型在定义对象时,就确定了,不能改变
3> 运行类型是可以变化的
4> 编译类型看定义时=号的左边,运行类型看=号的右边
5> 多态的前提是两个对象(类)存在继承关系
Animal animal = new Dog(); //animal编译类型是Animal,运行类型Dog animal = new Cat(); //animal的运行类型变成了Cat,编译类型仍然是 Animal
package com.ploy; public class Animal { private String name; public Animal(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } } package com.ploy; public class Dog extends Animal { public Dog(String name) { super(name); } } package com.ploy; public class Food { private String name; public Food(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } } package com.ploy; public class Fish extends Food { public Fish(String name) { super(name); } } package com.ploy; public class Master { private String name; public Master(String name) { this.name = name; } public void feed(Animal animal , Food food){ //体现多态 //相当于 Animal animal = new Dog("阿黄"); //相当于 Food food= new Fish("小黄鱼"); System.out.println("主人 " + name + " 喂 " + animal.getName() + " 吃 " + food.getName()); } } package com.ploy; public class run { public static void main(String[] args) { Master master = new Master("King"); Dog dog = new Dog("阿黄"); Fish fish = new Fish("小黄鱼"); master.feed(dog,fish); } }
1> 本质:父类的引用指向了子类的对象
2> 语法:父类类型 引用名 = new 子类类型();
3> 特点:编译类型看左边,运行类型看右边
可以调用父类中的所有成员(需遵守访问权限)、不能调用子类中特有成员、最终运行效果看子类的查找顺序
package com.up; public class father { public void eat(){ System.out.println("father的eat方法"); } public void run(){ System.out.println("father的run方法"); } private void cry(){ System.out.println("father的cry方法"); } } package com.up; public class son extends father { public void eat(){ //重写了father的eat方法 System.out.println("son的eat方法"); } public void drink(){ //son的特有方法 System.out.println("son的drink方法"); } } package com.up; public class run { public static void main(String[] args) { father temp = new son(); //父类的引用指向了子类的对象,叫向上转型 temp.eat(); //输出结果:son的eat方法 temp.run(); //输出结果:father的run方法 //temp.drink(); //这样是会报错的 //temp.cry(); //这样是会报错的 } }
向上转型细节
1> temp这个变量引用可以调用father类的所有属性和方法,但是要遵守权限,father类的private的方法就不能调用
2> temp不能调用son类(子类)的特有属性和方法,因为变量引用能调用哪些成员是在编译阶段,由编译类型来决定的,即与javac编译相关
3> temp调用运行方法时最终的运行效果要看son类(运行类型)的查找规则,由java这个程序运行,即调用方法时按照从子类向父类的顺序进行查找
总结一句话:调用方法看编译类型,执行顺序看运行类型
1> 语法:子类类型 引用名 = (子类类型) 父类引用;
2> 只能强转父类的引用,不能强转父类的对象
3> 要求父类的引用必须指向的是当前目标类型的对象
public class run { public static void main(String[] args) { Animal temp = cat(); Dog dog = (Dog) temp; //会报错,因为原来的temp是一只羊,你不能把temp强转成一只狗 } }
4> 当向下转型后,可以调用子类类型中所有的成员
经过向下转型之后的son指向的也是son()这个对象,此时son的编译类型是son,所以可以调用子类类型的所有成员,因为son类还继承了father类,所以son也可以调用father类的成员
1> 属性没有重写之说,属性的值看编译的类型
package com.up; public class run { public static void main(String[] args) { father temp = new son(); System.out.println(temp.age); //输出结果:50 System.out.println(((son) temp).age); //输出结果:25 } } class father{ int age = 50; } class son extends father{ int age = 25; }
2> instanceOf 比较操作符,用于判断对象的运行类型是否为XX类型或XX类型的子类型
package com.up; public class run { public static void main(String[] args) { String name = "King"; father temp = new son(); System.out.println(temp instanceof son); //输出结果:true System.out.println(temp instanceof father); //输出结果:true System.out.println(name instanceof Object); //输出结果:true } } class father{ int age = 50; } class son extends father{ int age = 25; }
3> 向上转型的特殊写法
package com.up; public class run { public static void main(String[] args) { son s_temp = new son(); father f_temp = s_temp; //向上转型,相当于father f_temp = new son();
//注意:s_temp和f_temp是指向同一个对象的 } } class father{ int age = 50; } class son extends father{ int age = 25; }
package com.up; public class run { public static void main(String[] args) { Sub s = new Sub(); System.out.println(s.count); //20 属性的值看编译的类型 s.display(); //20 Base b = s; System.out.println(b == s); //两个对象比较的是地址 true System.out.println(b.count); //10 属性的值看编译的类型 b.display(); //20 向上转型调用运行方法时最终的运行效果要看运行类型的查找规则,由java这个程序运行,即调用方法时按照从子类向父类的顺序进行查找 } } class Base{ int count = 10; public void display(){ System.out.println(this.count); } } class Sub extends Base{ int count= 20; public void display(){ System.out.println(this.count); } }
1> 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
2> 当调用对象属性时,没有动态绑定机制,哪里声明,那里使用
数组的定义类型为父类类型,里面保存的实际元素类型为子类类型
package com.ployarr; public class run { public static void main(String[] args) { Object[] array = {"King",20,52.10,'A'}; for(int i = 0 ; i < array.length ; i++){ System.out.println(array[i]); } } }
应用实例
现有一个继承结构如下:
要求创建1个Person对象、2个Student对象、2个Teacher对象,统一放在数组中,并调用每个对象的say方法,并且想办法调用Student对象和Teacher对象的私有方法
package com.ployarr; public class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String say(){ return name + "\t" + age; } } package com.ployarr; public class Teacher extends Person{ private double salary; public Teacher(String name, int age, double salary) { super(name, age); this.salary = salary; } public Double getSalary() { return salary; } public void setSalary(Double salary) { this.salary = salary; } public String say(){ //重写了父类的say方法 return "老师\t" + super.say() + "\t" + salary; } public void teach(){ //Teacher类的特有方法 System.out.println("老师" + getName() + "正在教学中……"); } } package com.ployarr; public class Student extends Person{ private double source; public Student(String name, int age, double source) { super(name, age); this.source = source; } public Double getSource() { return source; } public void setSource(Double source) { this.source = source; } public String say(){ //重写了父类的say方法 return "学生\t" + super.say() + "\t" + source; } public void study(){ //Student类的特有方法 System.out.println("学生" + getName() + "正在学习中……"); } } package com.ployarr; public class run { public static void main(String[] args) { Person[] array = new Person[5]; array[0] = new Person("zrh",20); array[1] = new Student("rxk",21,100); array[2] = new Student("ym",30,95); array[3] = new Teacher("wxn",30,8000); array[4] = new Teacher("xxn",30,7000); for(int i = 0 ; i < array.length ; i++){ System.out.println(array[i].say()); //array[i].study或者 array[i].teach是错误的 // 因为他们的编译类型中没有此方法,能调用哪些方法是看编译类型的 //所以可以使用向下转型+判断 if(array[i] instanceof Student){ ((Student)array[i]).study(); } else if(array[i] instanceof Teacher){ ((Teacher)array[i]).teach(); } else if(array[i] instanceof Person); else { System.out.println("输入的类型错误"); } } } }
方法定义的形参类型为父类类型,实参类型允许为子类类型,应用实例:前面的主人喂动物
应用实例:
定义员工类Employee,包含姓名和月工资[private],以及计算年工资getAnnual的方法
普通员工和经理继承了员工,经理类多了奖金bonus属性和管理manage方法,普通员工类多了work方法,普通员工和经理类要求分别重写getAnnual方法
测试类中添加一个方法showEmpAnnual(Employee e),实现获取任何员工对象的年工资,并在main方法中调用该方法[e.getAnnual()]
测试类中添加一个方法,testWork,如果是普通员工,则调用work方法,如果是经理,则调用manage方法
package com.ploywork; public class Employee { private String name; private double salary; public Employee(String name, double salary) { this.name = name; this.salary = salary; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } public double getAnnual(){ return salary * 12; } } package com.ploywork; public class ordinary extends Employee{ public ordinary(String name, double salary) { super(name, salary); } public void work(){ System.out.println("普通员工" + getName() + "正在工作"); } public double getAnnual(){ //重写了父类的getAnnual方法 return getSalary() * 12; } } package com.ploywork; public class manager extends Employee{ private double bonus; public manager(String name, double salary, double bonus) { super(name, salary); this.bonus = bonus; } public double getBonus() { return bonus; } public void setBonus(double bonus) { this.bonus = bonus; } public void manage(){ System.out.println(getName() + "经理的manage方法"); } public double getAnnual(){ //重写了父类的getAnnual方法 return getSalary() * 12 + bonus; } } package com.ploywork; public class run { public static void main(String[] args) { run run = new run(); Employee temp = new ordinary("rxk",5000); System.out.println(run.showEmpAunnal(temp)); run.testWork(temp); Employee temp1 = new manager("rxk",8000,2000); System.out.println(run.showEmpAunnal(temp1)); run.testWork(temp1); } public double showEmpAunnal(Employee e){ return e.getAnnual(); } public void testWork(Employee e){ if(e instanceof ordinary){ ((ordinary) e).work(); //向下转型 } else if (e instanceof manager){ ((manager) e).manage(); //向下转型 } } }
==是一个比较运算符
1、 == 既可以判断基本类型,又可以判断引用类型
2、 == 如果判断基本类型,判断的是值是否相等(示例:int i=10; double d=10.0;)
3.、== 如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象
package com.com; public class equals_ { public static void main(String[] args) { Integer num1 = new Integer(5); Integer num2 = new Integer(5); //== 如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象 System.out.println(num1 == num2); //false int num3 = 50; double num4 = 50.0; //== 如果判断基本类型,判断的是值是否相等 System.out.println(num3 == num4); //true } }
4、equals 是Object类中的方法,只能判断引用类型
5、equals默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等
//Object类的equals方法 public boolean equals(Object obj) { return (this == obj); } //String类重写了equals方法 public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String aString = (String)anObject; if (coder() == aString.coder()) { return isLatin1() ? StringLatin1.equals(value, aString.value) : StringUTF16.equals(value, aString.value); } } return false; } //Integer类重写了equals方法 public boolean equals(Object obj) { if (obj instanceof Integer) { return value == ((Integer)obj).intValue(); } return false; }
package com.com; public class equals_ { public static void main(String[] args) { Object temp = new Object(); Object test = temp; //equals默认判断的是地址是否相等 System.out.println(temp.equals(test)); //true String str = "King"; String str1 = "King"; String str2 = "mark"; //子类中往往重写该方法,用于判断内容是否相等 System.out.println(str.equals(str1)); //true System.out.println(str.equals(str2)); //false } }
备注:ctrl + B 可以查看当前的方法的JDK源码
应用实例:判断两个tools对象的内容是否相等,如果两个tools对象的各个属性值都一样,则返回true,反之false
package com.equalshomework; public class Person{ public static void main(String[] args) { tools a = new tools("zrh",20,'男'); tools b = new tools("zrh",20,'男'); System.out.println(a.equals(b)); } } class tools { private String name; private int age; private char gender; public tools(String name, int age, char gender) { this.name = name; this.age = age; this.gender = gender; } public boolean equals(Object obj){ //重写了equals方法 if(this == obj){ //== 如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象 return true; } else if(obj instanceof tools){ tools t = (tools)obj; //向下转型,为了可以拿到对象的属性值 return this.name.equals(t.name) && this.age == t.age && this.gender == t.gender; } return false; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public char getGender() { return gender; } public void setGender(char gender) { this.gender = gender; } }
1、提高具有哈希结构的容器的效率
2、两个引用,如果指向的是同一个对象,则哈希值肯定是一样的
3、两个引用,如果指向的是不同对象,则哈希值是不一样的
4、哈希值主要根据地址号来的,不能完全将哈希值等价于地址
5、在集合中hashCode如果有需要的话,会进行重写
package com.hashCode; public class hashCode { public static void main(String[] args) { test test1 = new test(); test test2 = new test(); test test3 = test1; System.out.println(test1.hashCode()); //1239731077 System.out.println(test2.hashCode()); //557041912 System.out.println(test3.hashCode()); //1239731077 } } class test{ }
基本介绍
默认返回:全类名+@+哈希值的十六进制
1、子类往往重写toString方法,用于返回对象的属性信息
2、重写toString方法,打印对象或拼接对象时,都会自动调用该对象的toString形式.
3、当直接输出一个对象时,toString方法会被默认的调用,比如System.out.printIn(monster);就会默认调用monster.toString()
//查看Object 的toString 方法 public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
package com.toString_; public class toString_{ public static void main(String[]args){ cat cat = new cat(); System.out.println(cat.toString()); //com.toString_.cat@49e4cb85 master master = new master(); System.out.println(master.toString()); //master{name='King', age=20} // 当直接输出一个对象时,toString方法会被默认的调用 System.out.println(cat); //等价于System.out.println(cat.toString()); System.out.println(master); //等价于System.out.println(master.toString()); } } class cat{ } class master{ private String name = "King"; private int age = 20; @Override public String toString() { return "master{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
1、当对象被回收时,系统自动调用该对象的finalize方法。子类可以重写该方法,做一些释放资源的操作
2、什么时候被回收:当某个对象没有任何引用时,则 jvm 就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize方法
3、垃圾回收机制的调用,是由系统来决定(即有自己的GC算法),也可以通过System.gc()主动触发垃圾回收机制
我们在实际开发中,几乎不会运用finalize,所以更多就是为了应付面试
package com.finalize_; public class finalize_ { public static void main(String[] args) { car car = new car("宝马"); car = null; //这时 car对象就是一个垃圾,垃圾回收器就会回收(销毁)对象 // 在销毁对象前,会调用该对象的finalize方法 // 程序员就可以在 finalize中,写自己的业务逻辑代码(比如释放资源:数据库连接,或者打开文件..) //如果程序员不重写 finalize,那么就会调用Object类的 finalize,即默认处理 System.gc(); System.out.println("程序执行完毕"); } } class car{ String name; public car(String name) { this.name = name; } @Override protected void finalize() throws Throwable { System.out.println("释放资源……"); } } //执行结果: //释放资源…… //程序执行完毕
F7(跳入方法内)
alt+shift+F7(强制进入)
F8(逐行执行代码)
shift+F8(跳出方法)
F9(resume,直接执行到下一个断点)
Idea debug如何进入Jdk源码
解决方法1:使用force step into : 快捷键 alt + shift + F7
解决方法2:配置一下就好了
点击Setting --> Build,Execution,Deployment --> Debugger --> Stepping
把Do not step into the classes中的java.*,javax.*取消勾选
//面向过程版本 package com.tencent.smallchange; import java.text.SimpleDateFormat; import java.util.Scanner; import java.util.Date; public class run { public static void main(String[] args) { boolean loop = true; Date date; //用于新建一个Date对象 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String details = ""; double money = 0.0; //接收收益入账金额 double cash = 0.0; //用户余额 Scanner scanner = new Scanner(System.in); do { System.out.println("--------零钱通---------"); System.out.println("\t1、零钱通明细"); System.out.println("\t2、收益入账"); System.out.println("\t3、消费支出"); System.out.println("\t4、退出程序"); System.out.print("请选择功能(1--4):"); String num = scanner.next(); date = new Date(); //新建一个Date对象 String time = sdf.format(date); //将Date的时间格式改为第9行所示 switch (num){ case "1": // 零钱通明细 System.out.print("-------零钱通明细-------"); System.out.println(details); break; case "2": //收益入账 System.out.println("--------收益入账--------"); //判断收益金额的有效性 while(true){ System.out.print("请输入收益金额:(要求:金额>0)"); money = scanner.nextDouble(); //接收收益的金额 if(money <= 0){ //如果收益的金额为负 System.out.println("金额输入错误,请核对"); } else break; } cash += money; //累加收益金额 details += "\n收益入账\t金额:+" + money + "\t时间:" + time + "\t余额:" +cash; break; case "3": //消费支出 System.out.println("--------消费支出--------"); //这里不加while的原因是因为用户可能在发现余额不足的情况下会不再进行购买,转而进行查看明细 System.out.print("请输入该项目消费金额:"); double out_money = scanner.nextDouble(); //接收消费的金额 if(out_money > cash){ System.out.println("您的余额不足,支付失败,请及时充值"); break; } System.out.print("请输入消费支出项目:"); String item = scanner.next(); cash -= out_money; //累减消费金额 details += "\n" + item + "\t金额:-" + out_money + "\t时间:" + time + "\t余额:" + cash; break; case "4": //退出程序 String log; while(true){ System.out.println("你确定要退出此程序?(y/n):"); log = scanner.next(); if(log.equals("y") || log.equals("n")){ //如果输入不是y/n,则一直循环下去 break; } } if("y".equals(log)){ loop = false; //终止程序循环 break; } break; default: System.out.println("你输入的菜单有误,请重新输入......"); } }while(loop); System.out.println("程序已退出......"); } }
//面向对象版本 package com.tencent.smallchangeOOP; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Scanner; public class changeMoney { //定义属性 boolean loop = true; //用于终结run()方法中的do while循环 Date date; //用于接收下面新建的Date对象 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String details = ""; //记录零钱明细 double money = 0.0; //接收收益入账金额 double cash = 0.0; //用户余额 Scanner scanner = new Scanner(System.in); //创建scanner对象用于接收用户输入的内容 //定义方法 public void run(){ //运行程序,程序中调用了许多方法 do { System.out.println("--------零钱通---------"); System.out.println("\t1、零钱通明细"); System.out.println("\t2、收益入账"); System.out.println("\t3、消费支出"); System.out.println("\t4、退出程序"); System.out.print("请选择功能(1--4):"); String num = scanner.next(); switch (num){ case "1": details(); break; case "2": profit(); break; case "3": consumption(); break; case "4": quit(); break; default: System.out.print("你是输入的指令有误,请重新输入:"); } } while(loop); System.out.println("程序已退出......"); } public void details(){ // 零钱通明细 System.out.print("-------零钱通明细-------"); System.out.println(details); } public void profit(){ //收益入账 date = new Date(); String time = sdf.format(date); System.out.println("--------收益入账--------"); //判断收益金额的有效性 while(true){ System.out.print("请输入收益金额:(要求:金额>0)"); money = scanner.nextDouble(); //接收收益的金额 if(money <= 0){ //如果收益的金额为负 System.out.println("金额输入错误,请核对"); } else break; } cash += money; //累加收益金额 details += "\n收益入账\t金额:+" + money + "\t时间:" + time + "\t余额:" +cash; } public void consumption(){ //消费支出 date = new Date(); String time = sdf.format(date); System.out.println("--------消费支出--------"); //这里不加while的原因是因为用户可能在发现余额不足的情况下会不再进行购买,转而进行查看明细的操作 System.out.print("请输入该项目消费金额:"); double out_money = scanner.nextDouble(); //接收消费的金额 if(out_money > cash){ System.out.println("您的余额不足,支付失败,请及时充值"); return; } System.out.print("请输入消费支出项目:"); String item = scanner.next(); cash -= out_money; //累减消费金额 details += "\n" + item + "\t金额:-" + out_money + "\t时间:" + time + "\t余额:" + cash; } public void quit(){ //退出程序 String log; while(true){ System.out.println("你确定要退出此程序?(y/n):"); log = scanner.next(); if(log.equals("y") || log.equals("n")){ //如果输入不是y/n,则一直循环下去 break; } } if("y".equals(log)){ loop = false; //终止程序循环 return; } } } package com.tencent.smallchangeOOP; public class test_run { public static void main(String[] args) { changeMoney changeMoney = new changeMoney(); changeMoney.run(); } }
工具集类为将事先用到的方法都先写好,在service类里直接调用即可,例如:接受用户输入+输入正确值判断等
我们在拿到需求进行分析的时候,应该从用户的角度去进行分析,在代码实现的时候应该从下往上去实现
package test; public class test { public static void main(String[] args) { run.A(); //因为A方法为static,所以不需要创建对象就可以直接调用 //运行结果:test包下的static的A方法 } } package test; public class run { public static void A(){ System.out.println("test包下的static的A方法"); } }
//定义一个Person类(name,age,job),初始化Person对象数组,有4个person对象,按照age从大到小进行排序,使用冒泡排序 package com.homework; public class problem01 { public static void main(String[] args) { Person[] object = new Person[4]; //这里的编译类型可以写父类,也可以写本类,千万不要被多态搞晕 object[0] = new Person("Tom", 25, "警察"); object[1] = new Person("Jerry", 20, "医生"); object[2] = new Person("hash", 21, "舞者"); object[3] = new Person("Mary", 18, "收银"); //冒泡排序代码 Person temp = null; for (int i = 0; i < object.length - 1; i++) { for (int j = 0 ; j < object.length - 1 - i ; j++){ if (object[j].getAge() > object[j + 1].getAge()) { temp = object[j]; object[j] = object[j + 1]; object[j + 1] = temp; } } } for (int i = 0; i < object.length; i++) { System.out.println(object[i]); //默认使用object[i].toString() } } } class Person{ private String name; private int age; private String job; public Person(String name, int age, String job) { this.name = name; this.age = age; this.job = job; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getJob() { return job; } public void setJob(String job) { this.job = job; } public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", job='" + job + '\'' + '}'; } }
//在BankAccount类的基础上扩展新类CheckingAccount,对每次存款和取款都收取1美元的手续费 package com.homework5; public class BankAccount { private int balance; //定义私有属性 //构造器 public BankAccount(int balance) { this.balance = balance; } //存款 public void deposit(double amount){ balance += amount; } //取款 public void withdraw(double amount){ balance -= amount; } //get、set public int getBalance() { return balance; } public void setBalance(int balance) { this.balance = balance; } } package com.homework5; public class CheckingAccount extends BankAccount{ public CheckingAccount(int balance) { super(balance); } public void deposit(double amount){ super.deposit(amount - 1); //巧妙的传入了父类的方法参数 } public void withdraw(double amount){ super.withdraw(amount + 1); //巧妙的传入了父类的方法参数 } } package com.homework5; public class run { public static void main(String[] args) { CheckingAccount checkingAccount = new CheckingAccount(5000); checkingAccount.deposit(100); System.out.println(checkingAccount.getBalance()); //5099 checkingAccount.withdraw(100); System.out.println(checkingAccount.getBalance()); //4998 } }