java基础语法
注释:不会被执行,给编程人员看的
- 单行注释://
- 多行注释:/* */
- 文档注释:/** */结尾
平时写代码,一定要注意规范
标识符
关键字
Java关键字类别 | Java关键字 | 关键字含义 |
---|---|---|
访问控制 | private | 一种访问控制方式:私用模式,访问控制修饰符,可以应用于类、方法或字段(在类中声明的变量) |
protected | 一种访问控制方式:保护模式,可以应用于类、方法或字段(在类中声明的变量)的访问控制修饰符 | |
public | 一种访问控制方式:共用模式,可以应用于类、方法或字段(在类中声明的变量)的访问控制修饰符。 | |
类、方法和变量修饰符 | abstract | 表明类或者成员方法具有抽象属性,用于修改类或方法 |
class | 声明一个类,用来声明新的Java类 | |
extends | 表明一个类型是另一个类型的子类型。对于类,可以是另一个类或者抽象类;对于接口,可以是另一个接口 | |
final | 用来说明最终属性,表明一个类不能派生出子类,或者成员方法不能被覆盖,或者成员域的值不能被改变,用来定义常量 | |
implements | 表明一个类实现了给定的接口 | |
interface | 接口 | |
native | 用来声明一个方法是由与计算机相关的语言(如C/C++/FORTRAN语言)实现的 | |
new | 用来创建新实例对象 | |
static | 表明具有静态属性 | |
strictfp | 用来声明FP_strict(单精度或双精度浮点数)表达式遵循IEEE 754算术规范 | |
synchronized | 表明一段代码需要同步执行 | |
transient | 声明不用序列化的成员域 | |
volatile | 表明两个或者多个变量必须同步地发生变化 | |
程序控制 | break | 提前跳出一个块 |
continue | 回到一个块的开始处 | |
return | 从成员方法中返回数据 | |
do | 用在do-while循环结构中 | |
while | 用在循环结构中 | |
if | 条件语句的引导词 | |
else | 用在条件语句中,表明当条件不成立时的分支 | |
for | 一种循环结构的引导词 | |
instanceof | 用来测试一个对象是否是指定类型的实例对象 | |
switch | 分支语句结构的引导词 | |
case | 用在switch语句之中,表示其中的一个分支 | |
default | 默认,例如:用在switch语句中,表明一个默认的分支。Java8 中也作用于声明接口函数的默认实现 | |
错误处理 | try | 尝试一个可能抛出异常的程序块 |
catch | 用在异常处理中,用来捕捉异常 | |
throw | 抛出一个异常 | |
throws | 声明在当前定义的成员方法中所有需要抛出的异常 | |
包相关 | import | 表明要访问指定的类或包 |
package | 包 | |
基本类型 | boolean | 基本数据类型之一,声明布尔类型的关键字 |
byte | 基本数据类型之一,字节类型 | |
char | 基本数据类型之一,字符类型 | |
double | 基本数据类型之一,双精度浮点数类型 | |
float | 基本数据类型之一,单精度浮点数类型 | |
int | 基本数据类型之一,整数类型 | |
long | 基本数据类型之一,长整数类型 | |
short | 基本数据类型之一,短整数类型 | |
null | 空,表示无值,不能将null赋给原始类型(byte、short、int、long、char、float、double、boolean)变量 | |
true | 真,boolean变量的两个合法值中的一个 | |
false | 假,boolean变量的两个合法值之一 | |
变量引用 | super | 表明当前对象的父类型的引用或者父类型的构造方法 |
this | 指向当前实例对象的引用,用于引用当前实例 | |
void | 声明当前成员方法没有返回值,void可以用作方法的返回类型,以指示该方法不返回值 | |
保留字 | goto | 保留关键字,没有具体含义 |
const | 保留关键字,没有具体含义,是一个类型修饰符,使用const声明的对象不能更新 |
标识符:类名,变量名,方法名
- 所有标识符都以字母,美元符($),或者下划线开头
- 首字母之后可以是字母,数字,下划线,美元符的组合
- 不能使用关键字
- 大小写敏感
- 可以用中文,但不建议(少用拼音)
数据类型
强类型语言:变量先定义后使用
弱类型语言:JavaScript,Python
Java的数据类型
-
基本类型
-
数值类型
-
整数类型
- byte占一个字节范围:-128–127
- short占两个字节范围:-32768–32767
- int占四个字节
- long占八个字节
-
浮点类型
-
float占四个字节
-
double占八个字节
//整数常用 int num1=10; byte num2=200;//报错,显示换成int short num3=30; long num=30L;//不加L,默认int //小数,浮点数 float num5=50.1F;//不加F默认double double num6=3.141592656846515; //字符 char name='A';//一个字符 //字符串 string name1="夏习清";//多个字符 //布尔值 boolean flag=true; boolean flag=false;
-
-
字符类型char,两个字节
-
-
boolean类型:占一位,值只有true和false
-
-
引用类型
- 类
- 接口
- 数组
位:计算机内数据存储最小的单位(bit)
字节:byte计算机数据处理的基本单位
1B=8b
字符:计算机使用的字母,数字,字和符号
1bit =1位
1024B=1KB
1024KB=1M
1024M=1G
面试问题
二进制0b
八进制0
十进制
十六进制0x:0-9,A-F
int i=10;//十进制 10
int i1=010;//八进制 8
int i2=0x10;//十六进制 16
//浮点数
//float f1=0.1f; //0.1
//double d=1.0/10; //0.1
//f==d? false
float d1=123123f;
float d2=d1+1;
d1==d2? true
//float是大约数,接近但不等于
//double
银行业务BigDecimal 大数数据类型
字符拓展
char c1='a';
char c2='中';
强制转换
system.out.print((int)c1);//97
system.out.print((int)c2);//20013
//所有的字符本质还是数字
//编码问题 Unicode,2字节 0-65536
a=97,A=65
//16进制 U0000---UFFFF,u0061=a=97
\表示转义
\b表示退格
\\斜杆
\'单引号
\"双引号
\t 制表符
system.out.print("hello\tworld")输出中间有空格
\n换行
String
string sa="hello";
string sb="hello";
string sc=new string("hello");
string sd=new string("hello");
sa=sb
sc!=sd
boolean flag=true;
if(flag=true){}
if (flag){}相等
类型转换
Java是强类型语言,一些时候需要进行类型转换
不同类型先转换成同一类型在运算
低--------高
byte,short,char->int->long->float->double
小数优先级大于整数
强制转换从高到低
- 不能对布尔值进行转换
- 不能把对象类型转成不相干类型
- 高容量转换成低容量,强制转换
- 转换可能存在内存溢出或者精度问题
自动转换从低到高
public class Day3 {
public static void main(String[] args) {
int i=128;
//byte b=i
byte c=(byte)i;//内存溢出
double d=i;
System.out.println(i);//128
//System.out.println(b);
System.out.println(c);//-128
System.out.println(d);//128.0
System.out.println((int)23.7);//23,精度丢失
System.out.println((int)-45.89f);//-45
char c1='a';
int c2=c1+1;
System.out.println(c1);//98
System.out.println((char)c2);//b
//注意溢出问题
//当操作数比较大时,注意溢出问题
//JDK7新特性,数字之间可以用下划线分割
int money = 10_0000_0000; //10亿,下划线不会被打印出来
System.out.println(money); //1000000000
int years = 20;
int total = money*years; //数据大,溢出
System.out.println(total); //-1474836480
long total2 = money*years; //默认是int,转换前就有溢出问题
System.out.println(total2); //-1474836480
long total3 = money*(long)years; //先把一个数转Long
System.out.println(total3); //20000000000
变量
变量:可以变化的量
Java每个变量使用之前必须声明变量类型
//数据类型 变量名=值;例 int a=10;
变量包含:变量名,变量类型,作用域
用,隔开声明的多个相同类型的变量名
注:
- 每个变量都有类型,可以是基本类型也可以是引用类型
- 变量名必须是合法的标识符
- 变量声明是一条完整的语句,必须用;结束
作用域
类变量:加上static(写在类里面)
实例变量:不加static(可以写在类中间)
局部/成员变量:写在方法里
pbulic class Variable{
static int allClicks=0;//类变量
String str="hello world"; //实例变量
public void method(){
int i=0;//局部变量
}
}
默认值
int是0
boolean默认false
string默认是null
除了基本类型,其他都是null
double是0.0
变量声明的位置决定变量作用域
变量作用域确定可在程序中按变量名访问该变量的区域
public class Var {
public static void main(String[] args) {
int a=0;//a的作用域是main方法
for (; a < 4; a++) {
System.out.println("hello");
}
System.out.println(a);
}
}
public class Var {
public static void main(String[] args) {
for (int a=0; a < 4; a++) {//a的作用域是for循环
System.out.println("hello");
}
System.out.println(a);//报错
}
}
public class Var {
//属性:成员变量,全局变量
int num;
String s;
//方法
public void m1(){
//局部变量:作用域是一个方法,一个语句块
//a的作用域是for循环
for (int a=0; a < 4; a++) {
System.out.println("hello");
}
int n=9;
System.out.println(n);
}
public static void main(String[] args) {
int a=0;//作用域是整个main方法
for (; a < 4; a++) {
System.out.println("hello");
}
System.out.println(a);
}
}
类里面的变量是成员变量(全局变量)
方法里面的变量是局部变量
//错误例子分析
public class Test {
int score1=88;
int score2=98;
public void calcAvg(){
int avg=(score1+score2)/2;
}
public void showAvg(){
System.out.println("平均分是:"+avg);
}
}
在使用局部变量和成员变量时的区别
- 作用域不同
- 局部变量的作用域仅限定义它的方法
- 成员变量的作用域在整个类内部都是可见的
- 初始值不同
- java会给成员变量一个初始值(默认初始值)
- java不会给局部变量赋予初始值(局部变量必须初始化后使用)
注意
- 在同一个方法中,不允许有同名局部变量
- 在不同方法中,可以有同名局部变量
- 在同一个类中,成员变量和局部变量同名时,局部变量具有更高的优先级
参数也是变量,是局部变量
public class Test {
public int num;
public void calc(int num,int num2){
System.out.println(num+num2);
}
public static void main(String [] args){
Test test=new Test();
test.num=10;
test.calc(11,3);
}
}
//输出14
常量
常量:不会变的值
关键字:final 常量名=值;
final double PI=3.14;
常量名一般使用大写字符
static,final不区分先后顺序(修饰符)
//修饰符 不存在先后顺序,static可以写final后面
static final doube PI=3.14; //类变量,该类下的全局范围
变量命名原则
所有变量,方法,类名:见名知意
变量和方法:驼峰原则
类成员变量:首字母小写+驼峰原则:monthSalary
局部变量:首字母小写+驼峰原则
常量:大写字母+下划线:MAX_VALUE
类名:首字母大写+驼峰原则:Man,GoodMan
方法名:首字母小写+驼峰原则:run(),runRun()
运算符
算术运算符:+, - ,*, /(整除), % (取余), ++, –
int a=10;
int b=20;
int c=25;
int d=25;
System.out.println(a+b);//30
System.out.println(a-b);//-10
System.out.println(a*b);//200
System.out.println(a/b); //输出是0,需要变成0.5
System.out.println(a/(double)b); //0.5
long a=12312313123123L;
int b=123;
short c=10;
byte d=8;
System.out.println(a+b+c+d);//long
System.out.println(b+c+d);//int
System.out.println(c+d);//int
System.out.println((double)c+d);//int
//int不能转换成string
//System.out.println((string) c+d);
// ++自增 --自减 单目运算符
int a = 3;
int b = a++; //b=a,a=a+1 先赋值 即b=3 a=4
int c = ++a; //a=a+1,c=a 先自增 即a=5 c=5
System.out.println(a); //5
System.out.println(b); //3
System.out.println(c); //5
赋值运算符:=
关系运算符:> ,< ,>=, <= ,==, != ,instanceof
int a=10;
int b=20;
int c=21;
System.out.println(c%a);//21/10=2....1
System.out.println(a>b);//f
System.out.println(a<b);//t
System.out.println(a==b);//f
System.out.println(a!=b);//t
String name = "张三";
boolean flag = name instanceof String; //name是String类型的,返回true
逻辑运算符:&&(与),||(或),!(非)
boolean a=true;
boolean b=false;
System.out.println("a&&b"+(a&&b));//逻辑与,两个都为真,结果为真f
System.out.println("a||b"+(a||b ));//逻辑或,一个为真,结果为真t
System.out.println("!(a&&b)"+!(a&&b));//如果为真,结果为假t
//短路运算
int c=5;
boolean d=(c<4)&&(c++<4);
System.out.println(d);//f
System.out.println(c);//5
位运算符:&,|,^,~,>>,<<,>>>(了解)
/*
A=0011 1100
B=0000 1101
A&B=0000 1100 两个都是1才得1
A|B=0011 1101 都是0就是0,否则就是1
A^B=0011 0001 相同就是0,否则就是1
~B=1111 0010 取反
面试题
2*8怎么运算最快
左移<< *2
右移>> /2
0000 0000 =0;
0000 0001 =1;
0000 0010 =2;
0000 0011 =3;
0000 0100 =4;
0000 1000 =8;
0001 0000 =16;
System.out.print(2<<3);
输出16
位运算优点:效率高
*/
条件运算符:?:
扩展赋值运算符:+=,-=,*=,/=
int a=10;
int b=20;
a+=b;//a=a+b
a-=b;//a=a-b
System.out.println(a);
//字符串连接符 +,String
System.out.println(""+a+b);
//串在前面直接拼接1020
System.out.println(a+b+"");
//串在后面先运算在拼接30
//幂运算 2^3 2*2*2=8 Math.
double pow=Math.pow(2,3);
System.out.println(pow);
//三元运算符 x?y:z
//如果x==true,结果为y,否则结果为z
int score=80;
String type=score<60?"不及格":"及格";//结果及格
System.out.println(type );
扩展面试
//扩展:笔试题 i=5 s=(i++)+(++i)+(i--)+(--i) s=?
int i=5;
int s=(i++)+(++i)+(i--)+(--i);
System.out.println(s); //24
优先级()单目>算术>位移>关系>逻辑>三目>赋值
() ,[], . ,–,++,~,!,*,/ ,%,+ ,-, <<,>>, >>>, < ,<=,>, >=,instanceof,==,!=,&,^,|,&&,||, ? : ,+=,-=,/= ,
包机制
包:为了更好的组织类,Java提供了包机制,防止命名冲突
包名一般利用公司名字倒置来命名
*通配符,导入这个包下所有的类
问题:如何存放两个名字相同的类而不冲突
使用不同的包
包的作用
- 允许类组成较小的单元(类似于文件夹),易于找到和使用相应的文件
- 有助于实施访问权限控制
- 防止命名冲突,区分名字相同的类
两种类型的包
- JDK提供基本包
- java.lang:虚拟机自动引入
- java.util:提供一些实用类
- java.io:输入,输出
- 使用IDEA创建包
- 分别创建包和类
- 创建类的过程中创建类所在的包
包名:小写字母组成,不能以圆点开头或结尾
最好加上唯一的前缀,常用组织倒置的网络域名
后缀部分,不同机构要求不一样
声明包: package 包名;
导入包:import 包名.类名;
使用包的注意事项
-
一个类同时引用了两个来自不同包同类名
- 必须通过完整的类名来区分
-
每个包都是独立的,顶层包不会包含子包的类
-
package和import的顺序是固定的
- package必须位于第一行(忽略注释行)
- 只允许有一个package语句
- 其次是import
- 接着是类的声明
Javadoc
javaDoc -encoding UTF-8 -charset UTF-8 hello.java
之后查看生成的index.html文件即可
Javadoc:@author作者,@version版本,@since指明最早使用的jdk版本,@param参数,@return返回,@throws异常
流程控制
用户交互scanner
scanner:实现程序和人的交互,用这个类获取用户的输入
Scanner s=new Scanner(System.in);
通过Scanner类的next()和nextLine()方法获取输入的字符串
next:不能输入带有空格的字符串(带有空格,后面的内容不能输出)
nextLine:可以输入带有空格的字符串(空格后面的内容不会舍弃)
关闭scanner.close()
在读取时一般使用hasNext()和hasNextLine()判断是否还有输入的数据
Scanner scanner =new Scanner(System.in);
System.out.println("用next方式接收:");
//判断用户有没有输入字符串
if (scanner.hasNext()==true){
String str=scanner.next();
System.out.println("输入的内容为"+str);
}
//属于IO流的类如果不关闭会一直占用资源,要用完就关
scanner.close();
}//输入hello word,输出 hello
Scanner scanner=new Scanner(System.in);
System.out.println("使用nextLine方式接收数据");
if(scanner.hasNextLine()){
String str=scanner.nextLine();
System.out.println("输出内容为"+str);
}
scanner.close();
}//输入hello word,输出 hello word
//输入数据
Scanner scanner = new Scanner(System.in);
int i = 0;
float f = 0.0f;
System.out.println("请输入整数:");
if(scanner.hasNextInt()){
i= scanner.nextInt();
System.out.println("整数数据:"+i);
}
else {
System.out.println("输入的不是整数数据!");
}
练习:输入多个数据,求和和平均数,没输入一个数据用回车确定,
通过输入非数字来结束输入用输出执行结果
Scanner scanner=new Scanner(System.in);
//和
double sum=0;
int m=0;//输入的个数
//循环判断是否还有输入,并对里面的数据进行求和
while (scanner.hasNextDouble()){
double x=scanner.nextDouble();
m=m+1;
sum=sum+x;
System.out.println("你输入了第"+m+"个数据,当前的和为"+sum);
}
System.out.println(m+"个数的和为"+sum);
System.out.println(m+"个数的平均值为"+sum/m);
scanner.close();
}
顺序结构
java的基本结构是顺序结构(除非特殊指明,否知按照顺序一句一句执行),顺序结构师最简单的算法
System.out.println("hello1");
System.out.println("hello2");
System.out.println("hello3");
System.out.println("hello4");
System.out.println("hello5");
选择结构
if单选择:if(布尔表达式){//将要执行的语句}
Scanner scanner=new Scanner(System.in);
System.out.println("请输入内容:");
String s=scanner.nextLine();
//equals:判断字符串是否相等
if(s.equals("Hello")){
System.out.println("s");
}
System.out.println("End");
if双选择:if(布尔表达式){//将要执行的语句}
else(布尔表达式){//将要执行的语句}
Scanner scanner=new Scanner(System.in);
System.out.println("请输入成绩:");
int i=scanner.nextInt();
if(i>60){
System.out.println("及格");
}
else {
System.out.println("不及格");
}
scanner.close();
if多选择:多种不同的情况
if(表达式){执行内容}
else if(表达式){执行内容}
else{执行内容}
if(i>60&&i<70){
System.out.println("及格");
}
else if(i>70&&i<80){
System.out.println("良好");
}
else if(i>80&&i<90){
System.out.println("好");
}
else if(i>90&&i<100){
System.out.println("优秀");
}
else {
System.out.println("不及格");
}
scanner.close();
}
if嵌套选择
if(表达式){}
if(表达式){}
if(表达式){}
Switch多选择:一般与case连接起来
Switch(expression){
case value://语句
break;
case value://语句
break;
case value://语句
break;
default://语句
}
Scanner scanner=new Scanner(System.in);
String str= scanner.nextLine();
switch (str){
case "周自珩":
System.out.println("匹配");
break;
case "夏知许":
System.out.println("亲戚");
break;
case "许其琛":
System.out.println("朋友");
break;
case "夏习清":
System.out.println("自己");
break;
default:
System.out.println("未知");
循环结构
while循环
while(表达式){循环内容}
- 表达式为真,循环一直走下去
- 一般都不会让程序一直循环
- 少部分情况下,需要一直执行,例如某些监听
- 避免死循环
- 不满足条件,不执行循环
int i=0;
int sum=0;
while(i<100){
i++;
sum=sum+i;
}
System.out.println(sum);
do ……while循环
do{
//代码语句}
while(条件);
- do……while至少执行一次
- while先判断后执行,do…while先执行后判断
int i=0;
int sum=0;
do {
sum=sum+i;
i++;
}while(i<=100);
System.out.println(sum);
for循环
for循环支持迭代的通用结构,最有效最灵活的循环结构
练习:
-
1-100之间偶数和奇数的和
int oddsum=0; int evensum=0; for (int i = 0; i < 100; i++) { if(i%2==0){ oddsum+=i; } else{ evensum+=i; } } System.out.println(oddsum); System.out.println(evensum);
-
while或for输出1-1000被5整除的数,每行输出3个
println自带换行
print不带换行
for (int i = 0; i < 1000; i++) { if(i%5==0){ System.out.print(i+"\t"); } if(i%(5*3)==0){ System.out.println(); } }
-
打印九九乘法表
for(int i=1;i<=9;i++){
for(int j=1;j<=i;j++){
System.out.print(j+"*"+i+"="+i*j+"\t");
if(i==j){
System.out.println();
}
}
}
break&continue
break:强制退出循环
continue:终止某次循环,跳出此次循环中尚未执行的语句;接着进行下一次循环
//不输出10的倍数
int i=0;
while(i<100){
i++;
if(i%10==0){
System.out.println();
continue;
}
System.out.println(i);
}
//输出1-9
int i=0;
while(i<100){
i++;
if(i%10==0){
System.out.println();
break;
}
System.out.println(i);
}
练习
-
打印三角形(5行)
for (int i = 1; i <= 5; i++) { for (int j = 5; j >=i ; j--) { System.out.print(" "); } for (int j = 1; j <=i ; j++) { System.out.print("*"); } for (int j = 1; j <i ; j++) { System.out.print("*"); } System.out.println(); } }
增强for循环
主要遍历数组和集合
for(声明语句:表达式){
//代码语句}
int[] numbers={10,20,30,40,50,60};
for(int i=0;i<6;i++){
System.out.println(numbers[i]);
}
/*
for(int x:numbers){
System.out.println(x);
}
*/
}
java方法
方法是语句的集合,放在一起执行一个功能
方法是解决一类问题的步骤的有序集合
方法包含于类或对象中
方法在程序中被创建,在其他地方被引用
方法保存:一个方法只完成一个功能,这样有利于后期的扩展
public static void main(String[] args) {
int sum =add(1,2);
System.out.println(sum);
test();
}
//加法
public static int add(int a,int b){
return a+b;
}
public static void test(){
System.out.println("hello1");
System.out.println("hello2");
}
}
方法:类似其他语言的函数,是一段用来完成特定功能的代码片段
方法定义
修饰符 返回值类型 方法名(参数类型,参数类型){
方法体
……
return 返回值;}
public static int add(int a,int b){
return a+b;
修饰符:定义了此方法的访问类型
返回值类型:可能会有返回值,没有返回值的话写void
方法名:此方法的名称
参数类型:传递值给参数,实参或形参
- 实参:调用方法时实际传给方法的数据(上述1,2是实参)
- 形参:在方法被调用时用于接收外界输入的数据(上述a,b是形参)
方法体:具体语句,定义此方法的功能
方法调用:对象名.方法名 类名.方法
命令行给main传递参数:Java 文件名 参数
方法重载
重载:一个类中,有相同的函数名称,但是形参(参数列表)不同
重载规则
- 方法名必须相同
- 参数列表必须不同(个数不同,类型不同,顺序不同)
- 返回类型可以相同,也可以不同
- 仅仅返回值类型不同,不能成为方法重载
int max=max(10,20);
double max1=max(10.0,11.1);
System.out.println(max);
System.out.println(max1);
}
public static int max(int a,int b){
int result=0;
if(a>b){
result=a;
}
else {
result=b;
}
return result;
}
public static double max(double a,double b){
double result=0;
if(a>b){
result=a;
}
else {
result=b;
}
return result;
}
实现简易计算器,分别实现两个整数,三个浮点数的加法运算
public class Calc {
//加法:两个整数
public void add(int num1,int num2){
int sum=num1+num2;
System.out.println(num1+"+"+num2+"="+sum);
}
//加法:两个浮点数
public void add(double num1,double num2){
double sum=num1+num2;
System.out.println(num1+"+"+num2+"="+sum);
}
//加法:三个浮点数
public void add(double num1,double num2,double num3){
double sum=num1+num2+num3;
System.out.println(num1+"+"+num2+"+"+num3+"="+sum);
}
public static void main(String[] args) {
Calc calc=new Calc();
calc.add(4,5);
calc.add(7.0,8.0);
calc.add(7.0,8.0,9.0);
}
}
可变参数
java支持传递同类型的可变参数给一个方法
在方法声明中,在指定参数类型后加一个省略号(…)
一个方法中只能制定一个可变参数,它必须是方法的最后一个参数。
任何普通参数都必须在它之前声明
day5_5 day=new day5_5();
test(1,2,3,4,5);
}
public static void test(int x,int... i){
System.out.println(i[0]);
//输出是2
day5_5 day=new day5_5();
test(1,2,3,4,5);
}
public static void test(int... i){
System.out.println(i[0]);
}//输出是1
递归
A方法调用A方法
简单程序解决复杂问题
递归:
- 递归头:什么时候不调用自身。如果没有递归头就陷入死循环了
- 递归体:什么时候需要调用自身
//阶乘 n! n*(n-1)*...*2*1
public static int f(int n){
if(n==1) return 1;
return n*f(n-1); //递归:调用自身
}
public static void main(String[] args) {
System.out.println(f(5)); //5!= 120
}
练习
书写一个计算器,实现加减乘除
Scanner scanner=new Scanner(System.in);
int a=scanner.nextInt();
Scanner scanner0=new Scanner(System.in);
String c=scanner0.nextLine();
Scanner scanner1=new Scanner(System.in);
int b=scanner1.nextInt();
switch (c){
case "+":add(a,b);break;
case "-":jian(a,b);break;
case "*":cheng(a,b);break;
case "/":chu(a,b);break;
}
scanner.close();
}
public static void add(int i,int j){
System.out.println(i+j);;
}
public static void jian(int i,int j){
System.out.println(i-j);;
}
public static void cheng(int i,int j){
System.out.println(i*j);;
}
public static void chu(int i,double j){
System.out.println(i/j);;
}
数组
数组简介
数组:相同数据类型的有序集合(基本和引用都可以存储)
通过下标来访问
每个数据称为一个数据元素
数组下标从0开始
使用数组先声明在使用
声明数组:
int [] nums;
int nums2[];
定义数组:int []nums=new int[10];
数组赋值:
nums[0]=1;
nums[1]=2;
nums[2]=3;
声明创建:int [] nums=new int[10];
数组长度:arrays.length
特点:
- 长度是确定的。一旦数组被创建,大小不可以改变
- 元素类型必须相同,不能混合
- 元素可以是任何数据类型包括基本类型和引用类型
- 数组变量属于引用类型,数组可以看成对象,元素相当于成员变量
数组三种初始化:
-
动态初始化(包含默认初始化)自己定长度,系统初始化
int [] nums =new int[5];
-
静态初始化,自己初始化值,系统决定长度
int [] nums =new int[]{1,2,3,4,5};
简写int [] nums={1,2,3,45};
-
默认初始化:默认值是0
数组边界:合法区间[0,length-1]
越界报错:ArraylndexOutofBounds
数组的使用
取出某个元素
int [] arr=new int[5];
int num1=arr[0];
int num2=arr[1];
int num3=arr[2];
int num4=arr[3];
int num5=arr[4];
System.out.println(num1);
System.out.println(num2);
System.out.println(num3);
System.out.println(num4);
System.out.println(num5);
//输出结果5个0
通过索引重新赋值:
num[0]=1;
num[1]=2;
num[2]=3;
数组名.length 可以获取数组长度或者元素个数
角标越界异常:ArraylndexOutOfBoundsException
数组元素的遍历
-
通过角标逐个输出
int [] arr=new int[5]; int num1=arr[0]; int num2=arr[1]; int num3=arr[2]; int num4=arr[3]; int num5=arr[4]; System.out.println(num1); System.out.println(num2); System.out.println(num3); System.out.println(num4); System.out.println(num5);
-
for循环遍历
int [] arr={1,2,3,4,5}; for(int i=0;i<arr.length;i++){ System.out.println(arr[i]); }
-
For-Each循环遍历
int [] arrays={1,2,3,4,5};
//没有下标
for (int array : arrays) {
System.out.println(array);
}//增强for循环遍历
-
数组反向遍历
int [] arr={1,2,3,4,5}; for(int i=arr.length;i>0;i--){ System.out.println(arr[i]); }
获取数组最值
-
获取最大值
int [] arr={1,2,3,4,5}; int max=0; for(int i=1;i<arr.length;i++){ if(arr[i]>max){ max=arr[i] } } System.out.println("数组中的最大值是"+max);
-
获取最小值
int [] arr={1,2,3,4,5}; int min=0; for(int i=1;i<arr.length;i++){ if(arr[i]<min){ min=arr[i] } } System.out.println("数组中的最小值是"+min);
反转数组元素
int [] arr={1,2,3,4,5};
for(int i=0;i<arr.length/2;i++){
int t=arr[i];
arr[i]=arr[arr.length-1-i];
arr[arr.length-1-i]=t;
}
数组作方法入参
//打印数组元素
public static void printArray(int[] a){
for (int i = 0; i < a.length; i++) {
System.out.print(a[i]+" ");
}
}
数组的应用
数组作返回值:反转
int [] arrays={1,2,3,4,5};
int [] reverse =reverse(arrays);
printArray(reverse);
}
//反转数组
public static int[] reverse(int[] arrays){
int [] result=new int[arrays.length];
for (int i = 0,j=result.length-1; i < arrays.length; i++,j--) {
result[j]=arrays[i];
}
return result;
}
普通的for循环:使用最多,取数据,进行下标操作
int [] arrays={1,2,3,4,5};
//打印全部数组元素
for (int i = 0; i < arrays.length; i++) {
System.out.println(arrays[i]);
}
System.out.println("======");
//计算所有元素的和
int sum=0;
for (int i = 0; i < arrays.length; i++) {
sum=sum+arrays[i];
}
System.out.println(sum);
二维数组
二维数组的定义:数据类型[] [] 变量名=new 数据类型[m] [n]
m表示二维数组有多少个一维数组
n表示每个一维数组的元素个数
int[] [] arr=new int [3] [2];
二维数组的长度为3,每个一维数组的长度是2
//动态初始化
int [][] arr=new int [3] [2];
System.out.println(arr);
System.out.println(arr[0]);
System.out.println(arr[0][0]);
//给二维数组的每个元素重新赋值
arr[0]=new int[2];
arr[1]=new int[2];
arr[2]=new int[2];
//静态初始化
int [][] arr=new int[][]{{2,3,4},{5,2,23},{45,36,14}};
System.out.println(arr[0][2]);
System.out.println(arr[2][2]);
//动态获取
System.out.println(arr[arr.length-1][arr[arr.length-1].length-1]
//简写
int [][] array={{1,2},{2,3},{3,4},{4,5}};
int []g,z[];//定义了两个数组,一个一维数组g,一个二维数组z
二维数组的遍历
- 逐个输出
- for循环
int [][] array={{1,2},{2,3},{3,4},{4,5}};
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
System.out.println(array[i][j]);
}
练习
-
销售额求和:第一季度:22,66,44;第二季度:77,33,88;第三季度:25,45,65;第四季度:11,66,99
int [][] arr={{22,66,44},{77,33,88},{25,45,65},{11,66,99}} int sum=0; for (int i = 0; i < array.length; i++) { for (int j = 0; j < array[i].length; j++) { sum+=arr[i][j]; } System.out.println("总销售额为"+array[i][j]+"万元")
-
打印杨辉三角形(行数可以键盘录入)
1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 1 5 10 10 5 1 Scanner sc=new Scanner(System.in); System.out.println("请输入行数"); int n=sc.nextInt(); //定义二维数组存储数据 int [][] arr=new int [n][n]; //把每一行的第一列和最后一列设为1 for (int i = 0; i < arr.length; i++) { arr[i][0]=1; arr[i][i]=1; } //设置中间元素 or (int i = 2; i < arr.length; i++) { for (int j = 1; j < i; j++) { arr[i][j]=arr[i-1][j-1]+arr[i-1][j]; } //遍历二维数组 //for (int i = 0; i < arr.length; i++) { // for (int j = 0; j < arr[i].length; j++) { // System.out.print(arr[i][j]+"\t"); //} //System.out.print(); //输出正方形,斜线是1,改一下 for (int i = 0; i < arr.length; i++) { for (int j = 0; j <i; j++) { System.out.print(arr[i][j]+"\t"); } System.out.print();
多维数组
多维数组:可以看成数组的数组,二维数组是特殊的一维数组,其中每个元素都是一个一维数组
查找思想
-
遍历数组按个查找
int [] arrays={1,50,2,3,4,5,50,20,11,24}; //查找元素在数组中第一次出现的索引 int index=getIndexByELE(arr,20); System.out.print("该元素第一次在数组中出现的索引是:"+index); public static int getIndexByELE(int []arr,int ele){ //遍历数组去查找 for(int i=0;i<arr.length;i++){ if(ele==arr[i]){ return i; } } return -1;//没有找到返回-1 }
-
二分查找
使用条件:数组元素必须有序
思想:每次查找中间元素,比较大小就能减少查找次数
最大索引:arr.length-1
最小索引:0
中间索引:(最小索引+最大索引)/2
查找时那目标元素和中间索引对应元素比大小
- 目标等于中间索引,返回中间索引
- 目标小于中间索引,改变最大索引,最大索引变成中间索引-1
- 目标大于中间索引,改变最小索引,最小索引变成中间索引+1
移动之后重新计算中间索引
循环去找while(最小索引<=最大索引)就找到了
public static void main(String[] args) { int [] a={1,2,5,18,45,56,77}; int index=getIndexByELE(arr,18); System.out.print("该元素出现的位置是"+index); public static void getIndexByELE(int []arr,int ele){ int maxIndex=arr.length-1; int minIndex=0; int centerIndex=(maxIndex+minIndex)/2; while(minIndex<=maxIndex){ if(ele==arr[centerIndex]){ return centerIndex; }else if(ele>arr[centerIndex){ minIndex=centerIndex+1; }else if(ele<arr[centerIndex){ maxIndex=centerIndex-1; } //重新计算中间索引 centerIndex=(maxIndex+minIndex)/2 } return -1; }
Arrays类
数组工具类:java.util.Arrays
八大排序
排序:把无序序列同归某种方式变成一个有序序列
-
冒泡排序
数组元素两两比较,交换位置,大元素往后放,经过一轮比较后,最大的元素就会出现在最大索引处
24 69 80 57 13 第一轮比较之后
24 69 57 80 13 24 69 57 13 80 第二轮比较之后
24 57 69 13 80 24 57 13 69 80 第三轮比较
24 13 57 69 80 第四轮比较
13 24 57 69 80 规律:数组有5个元素,经过了4轮比较
比较了4次,比较了3次,比较了2次,比较了1次
public static void main(String[] args) { int [] arr={24,69,80,57,13}; //第一轮比较,数组长度-1 for (int i = 0; i < arr.length-1; i++) { if(arr[i]>arr[i+1]){ int t=arr[i]; arr[i]=arr[i+1]; arr[i+1]=t; } //第二轮比较,数组长度-2 for (int i = 0; i < arr.length-1-1; i++) { if(arr[i]>arr[i+1]){ int t=arr[i]; arr[i]=arr[i+1]; arr[i+1]=t; } //第三轮比较,数组长度-3 for (int i = 0; i < arr.length-1-1-1; i++) { if(arr[i]>arr[i+1]){ int t=arr[i]; arr[i]=arr[i+1]; arr[i+1]=t; } //第四轮比较,数组长度-4 for (int i = 0; i < arr.length-1-1-1-1; i++) { if(arr[i]>arr[i+1]){ int t=arr[i]; arr[i]=arr[i+1]; arr[i+1]=t; } } //比较繁琐,可以进行优化
优化之后的代码
public static void main(String[] args) { int [] arr={24,69,80,57,13}; for (int i = 0; i < arr.length-1; i++) for (int j = 0; j < arr.length-1-j; j++){ if(arr[i]>arr[i+1]){ int t=arr[i]; arr[i]=arr[i+1]; arr[i+1]=t; } } }
-
代码比较简单,两层循环,外层冒泡轮数,里层依次比较
-
时间复杂度O(n^2)
int [] a={1,5,2,18,88,56,33}; int [] sort=sort(a);//返回一个排序后的数组 System.out.println(Arrays.toString(sort)); //冒泡排序 /* 1.数组中相邻的两个元素,如果第一个大于第二个,就交换位置 2.每一次比较都会产生一个最大数或者最小数 3.下一轮就可以减少一次排序 4.依次循环,直到结束 */ } public static int[] sort(int[] array){ int temp=0; //外层循环,判断走多少次 for (int i = 0; i < array.length-1; i++) { //内层循环,比较,如果第一大一第二,交换位置。三个元素 for (int j = 0; j < array.length-1-i; j++) { if(array[j+1]>array[j]){ temp=array[j]; array[j]=array[j+1]; array[j+1]=temp; } } } return array; }
冒泡会有很多不必要的比较,为了节省时间,进行优化
int [] a={1,5,2,18,88,56,33}; int [] sort=sort(a);//返回一个排序后的数组 System.out.println(Arrays.toString(sort)); //冒泡排序 /* 1.数组中相邻的两个元素,如果第一个大于第二个,就交换位置 2.每一次比较都会产生一个最大数或者最小数 3.下一轮就可以减少一次排序 4.依次循环,直到结束 */ } public static int[] sort(int[] array){ int temp=0; //外层循环,判断走多少次 for (int i = 0; i < array.length-1; i++) { boolean flag=false;//减少没有意义的比较 //内层循环,比较,如果第一大一第二,交换位置。三个元素 for (int j = 0; j < array.length-1-i; j++) { if(array[j+1]>array[j]){ temp=array[j]; array[j]=array[j+1]; array[j+1]=temp; flag=true; } } if(flag==false){ break; } } return array; } }
-
-
选择排序
原理:从0开始,依次和后面的元素进行比较,晓得元素往前放,经过一轮比较后,最小的元素就出现在最小索引处
int [] arr={24,69,80,57,13};
原数组
24 69 80 57 13 第一轮比较:从0开始
13 69 80 57 24 第二轮比较:从1开始
13 57 80 69 24 13 24 80 69 57 第三轮比较:从2开始
13 24 69 80 57 13 24 57 80 69 第四轮比较:从3开始
13 24 57 69 80 总结规律:数组中有5个元素,经过了4轮比较
比较了4次,比较了3次,比较了2次,比较了1次
public static void main(String[] args) { int [] arr={24,69,80,57,13}; //第一轮比较:从0开始 int index=0; for (int i = 1; i < arr.length; i++){ if(arr[index]>arr[i]){ int t=arr[index]; arr[index]=arr[i]; arr[i]=t; } } //第二轮比较:从1开始 int index=1; for (int i = 1+index; i < arr.length; i++){ if(arr[index]>arr[i]){ int t=arr[index]; arr[index]=arr[i]; arr[i]=t; } } //第三轮比较:从2开始 int index=2; for (int i = 1+index; i < arr.length; i++){ if(arr[index]>arr[i]){ int t=arr[index]; arr[index]=arr[i]; arr[i]=t; } } //第四轮比较:从3开始 int index=3; for (int i = 1+index; i < arr.length; i++){ if(arr[index]>arr[i]){ int t=arr[index]; arr[index]=arr[i]; arr[i]=t; } } //繁琐,可以进行优化
优化后的代码:
public static void main(String[] args) { int [] arr={24,69,80,57,13}; for (int index = 0; index < arr.length-1; index++){ for (int i = 1+index; i < arr.length; i++){ if(arr[index]>arr[i]){ int t=arr[index]; arr[index]=arr[i]; arr[i]=t; } } } System.out.println(Arrays.toString(arr)); }
-
插入排序
原理:最简单的排序,讲一个记录插入到一个长度为m的有序列表中,使之仍保持有序
原始数据:49,38,65,97,76,13,27
[49],38,65,97,76,13,27,从1索引开始插入
[38,49],65,97,76,13,27
[38,49,65],97,76,13,27
[38,49,65,97],76,13,27
[38,49,65,76,97],13,27
[13,38,49,65,76,97],27
[13,27,38,49,65,76,97]
public static void main(String[] args) { int [] arr={3,2,1,0,10,20,30,7,8}; //外层循环定义轮次 for (int i = 1; i < arr.length; i++){ //里层循环进行插入比较 int j=i; while(j>0&&arr[j]<arr[j-1]){ int t=arr[j]; arr[j]=arr[j-1]; arr[j-1]=t; j--; } } System.out.println(Arrays.toString(arr)); }
或者换成
public static void main(String[] args) { int [] arr={3,2,1,0,10,20,30,7,8}; for (int i = 1; i < arr.length; i++){ for(int j=i;j>0;j--){ if arr[j]<arr[j-1]){ int t=arr[j]; arr[j]=arr[j-1]; arr[j-1]=t; j--; } } } System.out.println(Arrays.toString(arr)); }
值交换方法
public static void swapValue(int []arr,int i,int j){ int t=arr[i]; arr[i]=arr[j]; arr[j]=t; }
-
希尔排序或者缩小增量排序
思想:将原表按照增量ht分组,每个子文件按照直接插入的方法排序,同样,用下一个增量ht/2将文件再分为子文件,在直接插入排序,直到ht=1时整个文件排好序
关键:选择合适的增量
原数组:46,55,13,42,17,94,05,70
步长=4(46,17)(55,94)(13,05)(42,70)
第一趟结果:17,55,05,42,46,94,13,70
步长=2(17,05,46,13)(55,42,94,70)
第二趟结果:05,42,13,55,17,70,46,94
步长=1
第三趟结果:05,13,17,42,46,55,70,94
public static void main(String[] args) { //对插入排序的优化 //直接插入排序就是增量唯一的希尔排序 int [] arr={46,55,13,42,17,94,05,70}; shelSort(arr); System.out.println(Arrays.toString(arr)); } //第一轮 public static void shellSort(int []arr){ int h=4; for (int i = h; i < arr.length; i++){ for(int j=i;j>h-1;j-=h){ if arr[j]<arr[j-h]){ swapValue(arr,j,j-h); } } } //第二轮 int h=2; for (int i = h; i < arr.length; i++){ for(int j=i;j>h-1;j-=h){ if arr[j]<arr[j-h]){ swapValue(arr,j,j-h); } } } //第三轮 int h=1; for (int i = h; i < arr.length; i++){ for(int j=i;j>h-1;j-=h){ if arr[j]<arr[j-h]){ swapValue(arr,j,j-h); } } } } public static void swapValue(int []arr,int i,int j){ int t=arr[i]; arr[i]=arr[j]; arr[j]=t; } } //繁琐,可以优化
优化之后
public static void main(String[] args) { //对插入排序的优化 //直接插入排序就是增量唯一的希尔排序 //合理选取增量 //第一次增量选取数组长度的一半 int [] arr={46,55,13,42,17,94,05,70}; shelSort(arr); System.out.println(Arrays.toString(arr)); } public static void shellSort(int []arr,int i,int j){ for(int h=arr.length/2;h>0;h/=2){ for (int i = h; i < arr.length; i++){ for(int j=i;j>h-1;j-=h){ if arr[j]<arr[j-h]){ swapValue(arr,j,j-h); } } } } } public static void swapValue(int []arr,int i,int j){ int t=arr[i]; arr[i]=arr[j]; arr[j]=t; }
增量选取
//第一次选择选取数组长度的一半还不是很好 //我们可以使用一种序列:克努特序列 int h=1; h=h*3+1; //根据克努特数列选取第一次增量 int jinage=1; while(jiange<=arr.length/3){ jiange=jiange*3+1; } 最后优化 public static void shellSort(int []arr,int i,int j){ int jinage=1; while(jiange<=arr.length/3){ jiange=jiange*3+1; } for(int h=jiange;h>0;h=(h-1)/3){ for (int i = h; i < arr.length; i++){ for(int j=i;j>h-1;j-=h){ if arr[j]<arr[j-h]){ swapValue(arr,j,j-h); } } } } }
-
快速排序
思想: 分治法:比大小,在分区
- 从数组中取出一个数,作为基准数
- 分区:将比这个数大或者等于的数放在右边小于它的数放在左边
- 在对左右区间重复第二步,知道各区间只有一个数
元素 | 5 | 3 | 9 | 1 | 6 | 7 | 2 | 4 | 0 | 8 |
---|---|---|---|---|---|---|---|---|---|---|
坑位 | 坑位1 | 坑位3 | 坑位5 | 坑位7 | 坑位6 | 坑位4 | 坑位2 | |||
0 | 3 | 4 | 1 | 2 | 5 | 7 | 6 | 9 | 8 |
public class QuickSortUtils{
public static void quickSort(int []arr,int start,int end){
if(start<end){
int index=getIndex(arr,start,end);
quickSort(arr,start,index-1);
quickSort(arr,index+1,end);
}
}
public static int getIndex(int[] arr,int start,int end){
int i=start;
int j=end;
int x=arr[i];
while(i<j){
while(i<j&&arr[j]>=x){
j--;
}
if(i<j){
arr[i]=arr[j];
i++;
}
while(i<j&&arr[j]<x){
i++;
}
if(i<j){
arr[j]=arr[i];
j--;
}
}
arr[i]=x;
return 0;
}
}
-
归并排序
利用归并思想实现排序
public static void main(String [] args){ int [] arr={4,5,7,8,1,2,3,6}; //拆分 chaifen(arr,0,arr.length-1); //归并 guiBing(arr,0,3,arr.length-1); System.out.println(Arrays.toString(arr)); } public static void cahifen(int[] arr,int startIndex,int endIndex){ //计算中间索引 int centerIndex=(startIndex+endIndex)/2; if(startIndex<endIndex){ chaifen(arr,startIndex,centerIndex); chaifenarr(arr,centerIndex+1,endIndex); guiBing(arr,startIndex,centerIndex); } } public static void guiBing(int [] arr,int startIndex,int centerIndex,int enIndex){ //定义一个临时数组 int [] tempArr=new int[enIndex-startIndex+1]; //定义左边数组的起始索引 int i=startIndex; //定义右边的起始索引 int j=centerIndex+1; //定义临时数组的起始索引 intindex=0; //比较左右两个数组的元素大小,往临时数组中方 while (i<=centerIndex&&j<=enIndex){ if(arr[i]<=arr[j]){ tempArr[index]=arr[i]; i++; }else{ tempArr[index]=arr[j]; j++; } index++; } //处理剩余数组 while(i<=centerIndex){ tempArr[index]=arr[i]; i++; index++; } while(j<=centerIndex){ tempArr[index]=arr[j]; j++; index++; } //System.out.println(Arrays.toString(tempArr)); //把临时数组中元素娶到原数组中 for(int k=0;k<tempArr.length;k++){ arr[k+startIndex]=tempArr[k]; } }
-
基数排序
不需要进行关键字的比较,只需要对关键字进行分配与手机两种操作即可
public class void main(Sting[] args){ int [] arr={2,1,5,21,31,444,23,33,47,10,903,124,987,100}; //得确定排序轮次 //获取数组最大值 //int max=getMax(arr); //基础排序 sortArray(arr); //输出排序后的数组 System.out.println(Arrays.toString(arr)); } private static void sortArray(int [] arr){ //定义二维数组,放10个桶 int[][] tempArr=new int[10][arr.length]; //定义一个统计数组 int counts=new int[10]; int max=getMax(arr); int len=String.valueOf(max).length(); //循环轮次 for(int i=1;i<len;i++,n*=10){ for(int j=0;j<arr.length;j++){ //获取每个位上的数字 int ys=arr[j]/n%10; tempArr[ys][counts[ys]++]=arr[j]; } //取出桶中元素 int index=0; for (int k=0;k<counts.length;k++){ if(counts[k]!=0){ for(int h=0;h<counts[k];h++){ //从桶中取出元素放回原数组 arr[index]=tempArr[k][h]; index++; } counts[k]=0;//清楚上一次统计的个数 } } } } private static int getMax(int[] arr){ int max=arr[0]; for(int i=1;i<arr.length;i++){ if(arr[i]>max){ max=arr[i]; } } }
-
堆排序(一种选择排序)
思想:
- 江代排序的序列构成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点
- 将其与末尾元素进行交换,此时末尾就是最大值
- 然后将剩余的n-1个元素重新构造成一个堆,这样就会得到n个元素的次小值
- 如此反复执行没变能得到一个有序序列了
大顶堆:父节点大于下面两个子节点(升序)arr[i]>=arr[2i+1]&&arr[i]>=arr[2i+2]
小顶堆:父节点小于下面两个子节点(降序)
arr[i]<=arr[2i+1]&&arr[i]<=arr[2i+2]
public class void main(Sting[] args){
//定义一个数组
int [] arr={1,0,6,7,2,3,4};
//调整成大顶堆
//开始调整的位置
int startIndex=(arr.length-1)/2;
//循环开始调整
for(int i=startIndex;i>=0;i--){
toMaxHeap(arr,arr.length,i);
}
System.out.println(Arrays.toString(arr));
//数组已经变成了大顶堆,把根元素和最后元素进行交换
for(int i=arr.length-1;i>0;i--){
//进行调换
int t =arr[0];
arr[0]=arr[i];
arr[i]=t;
//换完之后,再把剩余元素调成大顶堆
toMaxHeap(arr,i,0);
}
System.out.println(Arrays.toString(arr));
}
//调整数组,调整元素个数,从哪里开始调整
public static void toMaxHeap(int [] arr,int size,int index){
//获取左右字节的索引
int leftNodeIndex=index*2+1;
int rightNodeIndex=index*2+2;
//查找最大节点所对应的索引
int maxIndex=index;
if(arr[leftNodeIndex]>arr[maxIndex]&&leftNodeIndex<size){
maxIndex=leftNodeIndex;
}
if(arr[rightNodeIndex]>arr[maxIndex&&rightNodeIndex<size]){
maxIndex=rightNodeIndex;
}
//调换位置
if(maxIndex!=index){
int t=arr[maxIndex];
arr[maxIndex]=arr[index];
arr[index]=t;
//调换完之后可能会影响到下面的子树不是大顶堆,需要再次调换
toMaxHeap(arr,size,maxIndex);
}
}
稀疏数组
需求:编写五子棋,续盘和存储
使用条件:
- 数组中大部分元素为0或者同一个值是,可以使用稀疏数组
处理方式:
- 记录一共有几行几列,有多少不同值
- 把具有不同值的元素和行列值记录在一个小规模的数组中,从而压缩程序的规模
package arry;
public class day66 {
public static void main(String[] args) {
//1创建二维数组 11*11,0:没有棋子,1:代表黑棋,2:代表白棋
int[] []array1=new int [11][11];
array1[1][2]=1;
array1[2][3]=2;
//输出原始数组
System.out.println("输出原始数组");
for(int[] ints:array1){
for(int anInt:ints){
System.out.print(anInt+"\t");
}
System.out.println();
}
//转换成稀疏数组来保存
//获取有效值的个数
int sum=0;
for (int i = 0; i < 11; i++) {
for (int j = 0; j < 11; j++) {
if(array1[i][j]!=0){
sum++;
}
}
}
System.out.println("有效值的个数:"+sum);
//创建一个稀疏数组
int [][] array2=new int [sum+1][3];
array2[0][0]=11;
array2[0][1]=11;
array2[0][2]=11;
//遍历二维数组,将非零的值存到稀疏数组中
int count=0;
for (int i = 0; i < array1.length; i++) {
for (int j = 0; j < array1[i].length; j++) {
if(array1[i][j]!=0){
count++;
array2[count][0]=i;
array2[count][1]=j;
array2[count][2]=array1[i][j];
}
}
}
System.out.println("输出稀疏数组");
for (int i = 0; i < array2.length; i++) {
System.out.println(array2 [i][0]+"\t"+array2[i][1]
+"\t"+array2[i][2]+"\t");
}
System.out.println("=======");
System.out.println("还原");
//先读取稀疏数组
int[][]array3=new int[array2[0][0]][array2[0][2]];
//给其中的元素还原它的值
for (int i = 1; i < array2.length; i++) {
array3[array2[i][0]][array2[i][1]]= array2[i][2];
//打印
}
System.out.println("输出原始的数组");
for(int[] ints:array1){
for(int anInt:ints){
System.out.print(anInt+"\t");
}
System.out.println();
}
}
}
面向对象编程
软件开发周期:需求分析,软件设计,软件开发,软件测试,软件部署和维护
常用的开发方法:
- 结构化开发
- 面向功能划分软件结构
- 自顶而下
- 最小的子系统是方法
- 制约了软件的可维护性和可扩展性
- 面向对象开发(OOP)
- 把软件系统看成各种对象的集合
- 系统结构较稳定
- 子系统相对独立
- 软件可重用性,可维护性和可扩展性强
属性+方法=类
面向对象编程的本质:以类的方式组织代码,以对象的组织数据
三大特性:
- 封装
- 继承:子类继承父类
- 多态
类等同于类别,对象等同于个体(某一个类别下的具体的个体)
属性:对象具有的各种特征
属性值:每个对象的每个属性都拥有特定的值
属性一般可以相同,属性值一般不同
方法:对象执行的操作
对象:用来描述客观事物的一个实体,由一组属性和方法构成
可以从不同的对象中抽取出来相同属性和方法
总述类:具有相同属性和方法的一组对象的集合,类是对象的抽象,对象是类的具体描述
break和return的区别
break:跳出Switch,结束循环
return:结束方法,返回一个结果
定义类
Java程序都以类class为组织单元
关键字class定义自定义的数据类型
方法是完成某一功能
类的模版
public class 类名{
//定义属性部分
属性1类型 属性1; 成员变量
属性2类型 属性2;
······
//定义方法部分
方法1; 成员方法
方法2;
······
}
命名规范:
- 类名首字母大写
- 方法和变量:驼峰原则
例子
public class Person {
//属性(成员变量)
String name;
int age;
char sex;
//方法(成员方法)
public void eat(){
System.out.println("我吃饭");
}
public void drink(){
System.out.println("我喝水");
}
public void showInfo(){
System.out.println("我叫"+name);
}
}
public class School {
//属性(成员变量)
String schoolName;
int classNum;
int stuNum;
//方法
public void shpuSchoolInfo(){
System.out.println("学校名"+schoolName);
}
}
可以通过类图来描述类
-
用于分析和设计类
-
容易理解
Dog 类名 +health:int 属性名:类型 +:public +name:String -:private -love:int +print():void 方法名(参数类型):返回值类型
创建和使用对象
创建对象:
类名 对象名 =new 类名();
引用对象成员:使用 . 进行以下操作
引用类的属性:对象名.属性
引用类的方法:对象名.方法名()
public class TestPerson {
public static void main(String[] args) {
//创建人类对象: 类名 对象名=new 类名();
Person zhangSan=new Person();
//调用对象的成员变量和方法
zhangSan.name="张三";
zhangSan.age=18;
zhangSan.sex='男';
zhangSan.showInfo();
zhangSan.eat();
zhangSan.drink();
}
}
类和对象练习
编写学生类,输出学生具体信息
编写教师类,输出教师具体信息
//学生类
public class Student{
//属性有姓名,年龄,班级,爱好
String name;
int age;
String className;
String favor;
//方法:输出个人信息
public void showInfo(){
System.out.println(name);
System.out.println("年龄"+age);
System.out.println("就读于"+className);
System.out.println("爱好"+favor);
}
}
//教师类
public class Teacher{
//属性:姓名,专业,教授课程,教龄
String name;
String major;
String subject;
int teaAge;
//方法:输出个人信息
public void showInfo(){
System.out.println(name);
System.out.println("专业方向"+major);
System.out.println("教授课程"+subject);
System.out.println("教龄"+teaAge);
}
}
//创建学生和教师对象
public static void main(String[] args){
//创建学生对象
Student student=new Student();
student.age=19;
student.cassName="s1";
student.name="夏习清";
student.favor="画画";
student.shouInfo();
//创建教师对象
Teacher teacher=new Teacher();
teacher.major="计算机";
teacher.name="陈老师";
teacher.subject="Java oop";
teacher.teaAge=5;
teacher.shouInfo();
}
定义方法
方法定义:
- 修饰符
- 返回类型
- 方法名:注意规范,见名知意
- 参数列表:参数类型,参数名
- 抛出异常
修饰符 返回值类型 方法名(){
方法体
}
没有返回值写void,有返回值写返回值类型即可
return返回一个和返回值类型一致的值
return只返回一个值
return作用:返回值,跳出方法
方法之间允许互相调用
方法调用:递归
-
静态方法 static 类名.方法名
-
非静态方法,实例化这个类 new 类名.方法 (对象类型 对象名字=对象值)
- 一个是静态一个不是静态,不能调用,都是静态可以
- 一个静态一个动态不能调用,类实例化之后可以
-
形参和实参
-
值传递和引用传递
int a=1; System.out.println(a); day2.change(a); System.out.println(a); } public static void change(int a){ a=10; }
引用传递可以改变属性
public static void main(String[] args) { Person person=new Person(); System.out.println(person.name); day3.change(person); System.out.println(person.name); } public static void change(Person person){ //person是一个对象:指向的->Person person=new Person(); //这是一个具体的人,可以改变属性 person.name="夏习清"; } } //定义一个Person类,有一个属性:name class Person{ String name;//null }
类的方法练习
- 需求:一个景区根据游人的年龄收取不同价格的门票。请编写游人类,根据年龄段决定能够购买门票价格的输出
public class Visitor {
String name;
int age;
//方法:根据年龄判断门票价格
//18-60 20元;>60 <10 免费,10-18 半价
public void showPrice(){
if(age>0&&age<=10){
System.out.println(name+"的年龄为"+age+"岁:门票免费\n");
}
else if(age<18){
System.out.println(name+"的年龄为"+age+"岁:门票10元\n");
}
else if(age<60){
System.out.println(name+"的年龄为"+age+"岁:门票20元\n");
}
else {
System.out.println(name+"的年龄为"+age+"岁:门票免费\n");
}
public class TestVisitor {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
Visitor v=new Visitor();
boolean flag=true;//输入姓名不是n就循环,输入n就退出
do {
System.out.println("请输入姓名");
String name=input.next();
if(!name.equals("n")) {
v.name = name;//将用户输入的姓名赋值给V对象的name属性
System.out.println("请输入年龄");
int age = input.nextInt();
v.age = age;//将用户输入的年龄赋值给V对象的age属性
v.showPrice();
}
else {
flag=false;
}
} while (flag);
System.out.println("退出程序!");
}
}
- 模拟实现用户密码管理:输入旧的用户名和密码,如果正确,方法有权限更新;从键盘获取新的密码,进行更新
//管理员类
public class Admin {
//属性:用户名,密码
static String userName;
static String passWord;
//方法:输出管理员信息
public void shouInfo(){
System.out.println("管理员用户名为:"+userName+";密码为:"+passWord);
}
}
public class TestAdmin {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
//管理员信息
Admin admin=new Admin();
admin.userName="admin000";
admin.passWord="123456";
//修改管理员密码
System.out.print("请输入用户名:");
String name=input.next();
System.out.println("请输入密码:");
String pwd=input.next();
if(admin.userName.equals(name)&&admin.passWord.equals(pwd)){
System.out.println("\n请输入新密码:");
admin.passWord=input.next();
System.out.println("修改密码成,您的新密码为"+admin.passWord);
}else {
System.out.println("用户名和密码不匹配,没有权限修改信息");
}
}
}
带参方法
public void show(){}
无参方法
public int show(int i ){}
带参方法
定义带参方法 :访问修饰符 返回类型 方法名 (形式参数列表){
方法体}
调用参数方法
对象名.方法名(参数1,参数2…参数n) 实参列表
实参类型一定和形参类型一致
//榨汁例子
public class ZhaZhiJi {
//属性:样色,价格
String color;
double price;
//方法:输出信息
public void shouInfo(){
System.out.println("这是一台"+color+"的榨汁机,价格为"+price);
}
//榨汁前提:提供水果(声明参数:参数类型 参数名)
public void ZhaZhi(String fruit,int num){
System.out.println(num+"杯"+fruit+ "汁。");
}
}
public class TestZhaZhiJi {
public static void main(String[] args) {
ZhaZhiJi ji=new ZhaZhiJi();
ji.color="白色";
ji.price=243.2;
ji.shouInfo();
//ji.ZhaZhi("苹果",6);
//一直修改太麻烦,改成键盘录入
Scanner input=new Scanner(System.in);
System.out.println("你想要什么果汁?");
String fruit=input.next();
System.out.println("你想要几杯果汁?");
int num=input.nextInt();
//实际参数
ji.ZhaZhi(fruit,num);
}
}
循环实现榨汁
public class TestZhaZhiJi {
public static void main(String[] args) {
ZhaZhiJi ji=new ZhaZhiJi();
ji.color="白色";
ji.price=243.2;
ji.shouInfo();
//ji.ZhaZhi("苹果",6);
//一直修改太麻烦,改成键盘录入
Scanner input=new Scanner(System.in);
System.out.println("你想要什么果汁?");
String fruit=input.next();
if(!fruit.equals("n")) {
System.out.println("你想要几杯果汁?");
int num = input.nextInt();
//实际参数
ji.ZhaZhi(fruit, num);
}
else {
System.out.println("退出程序!");
}
}
}
方法定义处的参数叫形式参数,方法调用处传的值叫实际参数
带参方法,参数个数可以是一个,也可以是多个,多个参数之间用,隔开
带参方法,参数名字可以随意取,符合命名规则即可
形参和实参名字可以不一样,但数据类型必须一致,个数必须一致,顺序也要一致
方法有没有参数和方法有没有返回值是两个情况,毫无关系
带参方法的应用
增加学生姓名
在保存多个学生姓名的数组中,制定查找区间,查找某个学生姓名是否查询成功
//学员管理系统
public class StudentMgr {
//属性:存放学生姓名的数组
String[] names = new String[30];
//方法:增加学生姓名
public void addName(String name) {
for (int i = 0; i < names.length; i++) {
if (names[i] == null) {
names[i] = name;
break; }
}
}
//方法:固定区间,查找学生姓名
//start开始位置,end结束位置,name查找的学生名字
public boolean searchName(int start, int end, String name) {
boolean flag = false;//true是找到了
for (int i = start - 1; i < end; i++) {
if (name.equals(names[i])) {
flag = true;
break;
}
}
return flag;
}
//显示本班学生信息
public void showNames() {
System.out.println("本班学生列表");
for (int i = 0; i < names.length; i++) {
if (names[i] != null) {
System.out.println(names[i] + "\t");
}
}
}
}
public class TestStudentMgr {
public static void main(String[] args) {
StudentMgr mgr=new StudentMgr();
Scanner input=new Scanner(System.in);
for (int i = 0; i < 5; i++) {
System.out.println("请输入第"+(i+1)+"个学生姓名");
String name=input.next();
mgr.addName(name);
}
mgr.showNames();
//查找某个学生信息
System.out.println("请输入开始查找的位置");
int start=input.nextInt();
System.out.println("请输入结束查找的位置");
int end=input.nextInt();
System.out.println("请输入要查找的学生姓名");
String findName=input.next();
boolean flag=mgr.searchName(start,end,findName);
if(flag){
System.out.println("恭喜你,查到该学生");
}else {
System.out.println("对不起,没在该范围内查到该学生");
}
}
}
方法传参——基本&引用数据类型
- 掌握基本&引用传递时候的区别
public class Test {
public void calc1(int num){
num=num+1;
System.out.println(num);
}
public void calc2(Student student){
student.age=student.age+1;
}
}
public class Student {
String name;
int age;
int score;
public void showInfo(){
System.out.println(name+"的成绩是:"+score);
}
}
public class Demo {
public static void main(String[] args) {
Test test=new Test();
int n=8;
test.calc1(n);//参数类型为基本数据类型
//复制一份进行传递
Student stu=new Student();
stu.age=18;
test.calc2(stu);//参数类型为引用类型,自定义数据类型(数组,接口)
//传递地址,不进行复制
System.out.println(n+"---"+stu.age);
}
}
//num变成了9,,n没变还是8,,stu.age变成了19
总结:基本数据类型,传递的是变量的值,改变一个变量的值不会影响另一个变量的值。引用数据类型(类,接口,数组),赋值是把原对象的引用(可以理解为内存地址)传递给另一个引用。
方法传参——对象数组
- 对象数组作为参数时如何应用
使用学员对象数组作为参数,实现学员成绩修改
如果成绩小于60分,将其成绩提高2分
public class Student {
String name;
int age;
int score;
public void showInfo(){
System.out.println(name+"的成绩是:"+score);
}
}
public class ModifyScore {
//修改小于60分的学生的成绩
public void modifyStuScore(Student [] stus){
for (int i = 0; i < stus.length; i++) {
if(stus[i].score<60){
stus[i].score+=2;
}
}
}
//显示本组学生成绩信息
public void shouStus(Student [] stus){
for(Student stu:stus){
stu.showInfo();
}
}
}
public class TestModifyStu {
public static void main(String[] args) {
ModifyScore m=new ModifyScore();
//定义一个学生对象数组
Student s1=new Student();
s1.name="张三";
s1.score=43;
Student s2=new Student();
s2.name="李四";
s2.score=93;
Student s3=new Student();
s3.name="王五";
s3.score=59;
Student [] stus=new Student[3];
stus[0]=s1;
stus[1]=s2;
stus[2]=s3;
//显示学生信息
m.shouStus(stus);
m.modifyStuScore(stus);
m.shouStus(stus);
}
}
类与对象的关系
类:抽象的数据类型,对某一类事物真题描述/定义,不能代表某一具体事物
对象是抽象的具体
类是对象的抽象
创建与初始化对象
- 使用new关键字创建对象
- 使用new创建时,除了分配内存空间,还会给创建好的对象进行默认的初始化以及对类中构造器的调用
- 类中构造器也称为构造方法,是在进行创建对象的时候必须调用的,构造器有以下特点
- 必须和类名字相同
- 必须没有返回类型,也不能写void
- 一个类即使什么都不写,也会存在一个方法
作用:
- 使用New关键字本质是在调用构造器
- 初始化对象的值
注意:一旦定义了有参构造,无参构造就必须显示定义
单例模式,需要构造器私有
栈存放引用,堆存放具体的对象
alt+insert 生成构造器
构造方法
例Student s1=new Student(); 无参构造方法:每个类都有默认无参构造方法,,系统默认提供无参构造方法
访问修饰符 构造方法名(){ //初始化代码}
特殊处:无返回值类型(不写void),方法名和类名相同
作用:完成对象初始化
例public Student(String name,int score){
name =name; //第一个是属性,第二个是形参
score =score; //第一个是属性,第二个是形参}
//可以理解为这样
public Student(String n,int s){
name =n; //第一个是属性,第二个是形参
score =s; //第一个是属性,第二个是形参}
一般不这样写
一般这样写:this 指代当前对象
public Student(String name,int score){
this.name =name; //第一个是属性,第二个是形参
this.score =score; //第一个是属性,第二个是形参}
Student s1=new Student();
s1.name=“张三”;
s1.score=43;
换成
Student s1=new Student(“张三”,43);
this 关键字作用
-
调用属性
public Student(String name,int score){
this.name =name; //第一个是属性,第二个是形参
this.score =score; //第一个是属性,第二个是形参}
-
调用方法
public void showInfo(){
System.out.println(name+“的成绩是:”+score);}
public void method1(){
showInfo();
//等同于this.showInfo();}
-
调用构造方法
public Student(String name,int score){
this.name =name; //第一个是属性,第二个是形参
this.score =score; //第一个是属性,第二个是形参}
public Student(String name,int score,int age){
this.name =name; //第一个是属性,第二个是形参
this.score =score; //第一个是属性,第二个是形参
this.age=age;
//等同于this(name,score);先去调用构造方法public Student(String name,int score),之后单独给age赋值 this.age=age;
等价替换上面3行
this(name,score);
this.age=age;
}
使用this调用构造方法,如果使用,必须是构造方法的第一条语句
带参构造方法,创建学员数组对象的代码
Student s1=new Student();
s1.name="张三";
s1.score=43;
Student s2=new Student();
s2.name="李四";
s2.score=93;
Student s3=new Student();
s3.name="王五";
s3.score=59;
改成
Student s1=new Student("张三",43);
Student s2=new Student("李四",93);
Student s3=new Student("王五",59);
如果自己定义了有参构造方法,系统不再默认提供无参构造方法,需要自己定义无参构造方法
构造方法
- 作用:对象初始化
- 每个类都有默认无参构造方法
- 可以自定义带参构造方法,此时系统不再提供默认无参构造方法
this
- 对一个对象的默认引用
类与对象小结
类是模版,抽象是对象的一个具体实例
方法:定义调用
对应的引用:基本类型;对象是通过引用来操作的
属性:字段Field成员变量
-
默认初始化
数字:0,0.0
char:u000
boolean:false
引用:null
修饰符 属性类型 属性名 =属性值
对象的创建和使用
- 必须用new关键字创造对象,构造器Person kuangshen=new Person()
- 对象的属性:kuangshen.name
- 对象的方法;kuangshen.sleep()
类:
- 静态属性
- 动态行为
java三大特征:封装,继承,多态
封装
程序追求:高内聚,低耦合
高内聚:类的内部数据操作细节自己完成,不允许外部干涉
低耦合:仅暴露少量的方法给外部使用
封装(数据隐藏):通常,应禁止直接访问一个对象中数据的实际表示,而通过操作接口来访问,这称为信息隐藏
两大原则:
- 把所有属性藏起来
- 把尽可能多的东西藏起来,对外提供便捷的接口
属性私有:private,使用get获取值,set设置值(安全性判断)
实现封装的步骤
- 修改属性可见性,改成private,防止错误的修改
- 创建公有的getter/setter方法,用于属性的读写
- 在getter/setter方法中加入属性控制语句,对属性值的合法性进行判断
原本例子
public class Dog {
String name="无名氏";//昵称,默认是无名氏
int health=100;//健康值,默认是100,健康值在0-100之间,小于60 不健康
int love=0;//亲密度
String strain="聪明的拉布拉多犬";//品种
//输出狗狗信息
public void print(){
System.out.println("宠物的自白:\n我的名字叫"+this.name+"健康值是:"+this.health
+",和主人的亲密度是:"+this.love+"我是一只"+this.strain+"。");
}
public class Penguin {
String name="Q仔";//昵称,默认是Q仔
int health=100;//健康值,默认是100,健康值在0-100之间,小于60 不健康
int love=0;//亲密度
String sex="男";//品种
//输出狗狗信息
public void print(){
System.out.println("宠物的自白:\n我的名字叫"+this.name+"健康值是:"+this.health
+",和主人的亲密度是:"+this.love+"我的性别是"+this.sex+"。");
}
}
public class TsetPet {
public static void main(String[] args) {
Dog dog=new Dog();
dog.health=-1000;
dog.love=3;
dog.name="多多";
dog.strain="吉娃娃";
dog.print();
Penguin p=new Penguin();
p.health=-1000;
p.love=3;
p.name="Q仔";
p.sex="男";
p.print();
}
}
//健康值都是-1000,有问题,在类里面输入条件语句报错,修改不好改
修改之后的代码
public class Dog {
String name="无名氏";//昵称,默认是无名氏
//1.隐藏属性
private int health=100;//健康值,默认是100,健康值在0-100之间,小于60 不健康
private int love=0;//亲密度
private String strain="聪明的拉布拉多犬";//品种
//2.添加属性的setter和getter方法
//setter方法:属性赋值,属性操作(正确判断)
//getter方法:属性取值
public void setHealth(int health){
if(health<0||health>100){
System.out.println("宠物狗狗的健康值只能在0-100之间");
this.health=60;
return;
}
//获取用户给的health
this.health=health;
}
public int getHealth(){
return this.health;
}
public void setName(String name){
this.name =name;
}
public String getName(){
return this.name;
}
public void setLove(int love){
this.love =love;
}
public int getLove(){
return this.love;
}
public void setStrain(String strain){
this.strain =strain;
}
public String getStrain(){
return this.strain;
}
//输出狗狗信息
public void print(){
System.out.println("宠物的自白:\n我的名字叫"+this.name+"健康值是:"+this.health
+",和主人的亲密度是:"+this.love+"我是一只"+this.strain+"。");
}
}
public class TsetPet {
public static void main(String[] args) {
Dog dog=new Dog();
//dog.health=-1000;
dog.setHealth(-1000);
System.out.println(dog.getHealth());
//dog.love=3;
dog.setLove(3);
System.out.println(dog.getLove());
dog.setName("多多");
System.out.println(dog.getName());
//dog.name="多多";
//dog.strain="吉娃娃";
dog.setStrain("吉娃娃");
System.out.println(dog.getStrain());
dog.print();;
/* Penguin p=new Penguin();
p.health=-1000;
p.love=3;
p.name="Q仔";
p.sex="男";
p.print();*/
}
}
封装的好处
- 便于使用者争取使用系统,防止错误修改属性
- 有助于系统之间的松耦合,提高系统独立性
- 提高软件的可用性
- 降低了构建大型系统的风险
封装应用练习
public class Dog {
String name="无名氏";//昵称,默认是无名氏
//1.隐藏属性
private int health=100;//健康值,默认是100,健康值在0-100之间,小于60 不健康
private int love=0;//亲密度
private String strain="聪明的拉布拉多犬";//品种
//2.添加属性的setter和getter方法
//setter方法:属性赋值,属性操作(正确判断)
//getter方法:属性取值
public void setHealth(int health){
if(health<0||health>100){
System.out.println("宠物狗狗的健康值只能在0-100之间");
this.health=60;
return;
}
//获取用户给的health
this.health=health;
}
public int getHealth(){
return this.health;
}
public void setName(String name){
this.name =name;
}
public String getName(){
return this.name;
}
public void setLove(int love){
if(love<0||love>10){
System.out.println("宠物企鹅的亲密度只能在0-10之间");
this.love=6;
return;
}
public int getLove(){
return this.love;
}
public void setStrain(String strain){
this.strain =strain;
}
public String getStrain(){
return this.strain;
}
//输出狗狗信息
public void print(){
System.out.println("宠物的自白:\n我的名字叫"+this.name+",健康值是:"+this.health
+",和主人的亲密度是:"+this.love+",我是一只"+this.strain+"。");
}
}
public class Penguin {
private String name="Q仔";//昵称,默认是Q仔
private int health=100;//健康值,默认是100,健康值在0-100之间,小于60 不健康
private int love=0;//亲密度
private String sex="男";//品种
public void setName(String name){
this.name =name;
}
public String getName(){
return this.name;
}
public void setHealth(int health){
if(health<0||health>100){
System.out.println("宠物企鹅的健康值只能在0-100之间");
this.health=60;
return;
}
//获取用户给的health
this.health=health;
}
public int getHealth(){
return this.health;
}
public void setLove(int love){
if(love<0||love>10){
System.out.println("宠物企鹅的亲密度只能在0-10之间");
this.love=6;
return;
}
public int getLove(){
return this.love;
}
public void setSex(String sex){
this.sex =sex;
}
public String getSex(){
return this.sex;
}
//输出企鹅信息
public void print(){
System.out.println("宠物的自白:\n我的名字叫"+this.name+"健康值是:"+this.health
+",和主人的亲密度是:"+this.love+"我的性别是"+this.sex+"。");
}
}
public class TsetPet {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
System.out.println("欢迎您来到宠物店");
System.out.print("请输入你要领养的宠物的名字");
String name=input.next();
System.out.println("请输入要领养宠物的类型:1.狗狗,2.企鹅");
int typeNo=input.nextInt();
switch (typeNo){
case 1:
System.out.println("请选择狗狗的品种:1.拉布拉多,2.边牧");
int sexId1=input.nextInt();
String strain=(sexId1==1)?"拉布拉多":"边牧";
System.out.println("请输入狗狗的健康值");
int health1=input.nextInt();
System.out.println("请输入狗狗的亲密度");
int love1=input.nextInt();
Dog dog=new Dog();
dog.setHealth(health1);
dog.setLove(love1);
dog.setName(name);
dog.setStrain(strain);
dog.print();
break;
case 2:
System.out.println("请选择企鹅的性别:1.Q妹,2.Q仔");
int sexId=input.nextInt();
String sex=(sexId==1)?"Q妹":"Q仔";
System.out.println("请输入企鹅的健康值");
int health=input.nextInt();
System.out.println("请输入企鹅的亲密度");
int love=input.nextInt();
Penguin p=new Penguin();
p.setHealth(health);
p.setLove(love);
p.setName(name);
p.setSex(sex);
p.print();
break;
default:
System.out.println("暂时没有这个类型的宠物!");
break;
}
}
}
访问权限控制
类的访问修饰符:
- public修饰符:公有访问级别
- 默认修饰符:包级私有访问级别
//通过public来修饰
public class Student {
}
//默认修饰符
class Person {
}
public class Test {
public static void main(String[] args) {
//测试类既可以访问public修饰的类,也可以访问默认修饰符修饰的类
Student s=new Student();
Person p=new Person();
}
}
import cn.com.oop1.Student;
//import cn.com.oop1.Person;
public class Test2 {
public static void main(String[] args) {
//不同包的类只可以访问public修饰的类,不可以访问默认修饰符修饰的类
Student s=new Student();
//Person p=new Person();
}
}
类成员的访问控制符
同一个类中 | 同一个包中 | 子类中 | 任何地方 | |
---|---|---|---|---|
private | 可以 | 不可以 | 不可以 | 不可以 |
默认修饰符 | 可以 | 可以 | 不可以(不同包不行,同包可以) | 不可以 |
protected | 可以 | 可以 | 可以 | 不可以 |
public | 可以 | 可以 | 可以 | 可以 |
//通过public来修饰
public class Student {
//private修饰的类成员,只能在本类中访问
private int age;
//默认修饰符,本类能访问
String name;
//protected修饰的类成员,本类可以访问
protected String address;
//public修饰的类成员,本类可以访问
public char sex;
public void m1(){
System.out.println(this.age);
System.out.println(this.name);
System.out.println(this.address);
System.out.println(this.sex);
}
}
import cn.com.oop1.Student;
//import cn.com.oop1.Person;
public class Test2 {
public static void main(String[] args) {
//不同包的类只可以访问public修饰的类,不可以访问默认修饰符修饰的类
Student s=new Student();
//默认修饰符修饰的类成员,不同包的类不能访问
System.out.println(s.name);
//protected修饰的类成员,不同包的其他类可以访问
System.out.println(s.address);
//public修饰的类成员,不同包的其他类可以访问
System.out.println(s.sex);
//Person p=new Person();
}
}
public class Test {
public static void main(String[] args) {
//测试类既可以访问public修饰的类,也可以访问默认修饰符修饰的类
Student s=new Student();
//默认修饰符,本类能访问,同包的其他类也可以访问
System.out.println(s.name);
//protected修饰的类成员,同包的其他类可以访问
System.out.println(s.name);
//public修饰的类成员,同包的其他类可以访问
System.out.println(s.sex);
//其他类不能调用Student中private修饰的类成员
//s.age;
s.m1();
//把m1的修饰符换成private
Person p=new Person();
}
}
static变量
static可以用来修饰
-
成员变量
静态变量,可以直接通过类名访问(不用经过对象)
-
成员方法
静态方法,可以直接通过类名访问
-
代码块
静态代码块,当Java虚拟机加载类时,就会执行该代码块
类的成员变量包括
- 类变量(静态变量)
- 被static修饰的变量
- 类内部,可以在任何方法内直接访问
- 其他类中,可以直接通过类名访问
- 在内存中只有一个拷贝
- 能被类的所有实例共享,可作为实例之间进行交流的共享数据
- 实例变量
- 不被static修饰的变量
- 通过对象(实例)访问
- 每创建一个对象就会为实例变量分配一次内存,实例变量可以在内存中有多个拷贝,互不影响
public class StaticTest{
static int num;
public static void main(String[] args) {
num++;//1
StaticTest st1=new StaticTest();
st1.num++;//2
StaticTest st2=new StaticTest();
st2.num++;//3
StaticTest.num++;//4
System.out.println(num);
}
}
//输出结果4
static变量的作用
- 能被类的所有实例共享,可作为势力之间进行交流的共享数据
- 如果类的所有实例都包含一个相同的常量属性,可把这个属性定义为静态常量类型,从而节省内存空间
例:static final PI=3.14;
练习
模拟实现选民投票过程:一群选民进行投票,每个选民只允许头一次票,并且当投票总数达到100时,就停止投票
//选民类
public class Voter {
//目前投票数,被所有的选民实例类所共享
static int count;
//投票总数最大值,到100时,停止投票
static final int MAX_COUNT=100;
private String name;
public Voter(){}
public Voter(String name){
this.name=name;
}
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
//选民投票
public void vote(){
if(count==MAX_COUNT){
System.out.println("投票总数达到100,无法继续投票!");
return;
}else {
count++;
System.out.println(this.name+"投票成功!当前票数为:"+count);
}
}
}
public class TestVoter {
public static void main(String[] args) {
Voter v1=new Voter("张三");
v1.vote();
Voter v2=new Voter("李四");
v2.vote();
Voter v3=new Voter("王五");
v3.vote();
for (int i = 0; i < 97; i++) {
Voter v=new Voter("v"+i);
v.vote();
}
Voter v4=new Voter("张三");
v4.vote();
}
}
static方法
- 掌握static方法和实例方法的区别
- 会定义static方法
static可以用来修饰
-
成员变量
静态变量,可以直接通过类名访问(不用经过对象)
-
成员方
静态方法,可以直接通过类名访问
-
代码块
静态代码块,当Java虚拟机加载类时,就会执行该代码块
静态方法:可直接通过类名访问
- 静态方法中不能使用this和super
- 不能直接访问所属类的实例变量和实例方法
- 可直接访问类的静态变量和静态方法
实例方法:通过实例访问
- 可直接访问所属类的实例变量,实例方法,静态变量,静态方法
静态方法必须被实现:main是最常用的静态方法
public class Person {
//属性,成员变量(全局变量)--->实例变量
int age;
String name;
//静态变量 通过类名去调用-->静态变量
static String email;
public void m2() {
System.out.println("实例方法m2");
}
//成员方法:实例方法->可以调用实例变量,实例方法,静态变量,静态方法
public void m1(){
System.out.println(this.age);
System.out.println(this.name);
System.out.println(this.email);
m2();
m3();
}
//静态方法
public static void m3(){
//System.out.println(age);静态方法中不能调用实例变量
//m1();在静态方法中不能调用实例方法
//System.out.println(this.age);报错,不能使用this
System.out.println("静态方法m3");
System.out.println(email);//在静态方法中可以直接访问静态变量
m4();//静态方法可以直接调用静态方法
}
public static void m4(){
System.out.println("静态方法m4");
}
}
public static void main(String[] args) {
//调用类的成员变量,创建对象,通过对象去访问--->实例变量
Person p=new Person();
p.age=18;
p.name="张三";
p.email="zhangsan@163.com";
Person.email="zhangsan@163.com";
//通过实例调用实例方法
p.m1();
//通过类名调用静态方法
Person.m3();
Math.random();//静态方法,通过类名直接调用
}
投票修改输出投票数
//选民类
public class Voter {
//目前投票数,被所有的选民实例类所共享
static int count;
//投票总数最大值,到100时,停止投票
static final int MAX_COUNT=100;
private String name;
public Voter(){}
public Voter(String name){
this.name=name;
}
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
//选民投票
public void vote(){
if(count==MAX_COUNT){
System.out.println("投票总数达到100,无法继续投票!");
return;
}else {
count++;
System.out.println(this.name+"投票成功!当前票数为:"+count);
}
}
//显示当前投票总数
public static void showCount(){
System.out.println("当前的投票总数为"+count);
}
}
public class TestVoter {
public static void main(String[] args) {
Voter v1=new Voter("张三");
v1.vote();
Voter v2=new Voter("李四");
v2.vote();
Voter v3=new Voter("王五");
v3.vote();
System.out.println("============");
Voter.showCount();
for (int i = 0; i < 97; i++) {
Voter v=new Voter("v"+i);
v.vote();
}
System.out.println("============");
Voter.showCount();
Voter v4=new Voter("张三");
v4.vote();
System.out.println("============");
Voter.showCount();
}
}
找错
class Dog{
prviate String name="旺财";
......
public void play(int n){
static int localv=5;//实例方法中可以访问静态变量,不能定义静态变量
health=health-n;
System.out.println(name+" "+localv+" "+health+" "+love);
}
public static void main(String [] args){
Dog d=new Dog();
d.play(5);
}
}
static代码块
static可以用来修饰
-
成员变量
静态变量,可以直接通过类名访问(不用经过对象)
-
成员方
静态方法,可以直接通过类名访问
-
代码块
静态代码块,当Java虚拟机加载类时,就会执行该代码块
如果有多个静态代码块,按顺序加载
每个静态代码块只会执行一次
public class staticTest {
static int num=100;
static {
num+=100;
System.out.println(num);
}
static {
num+=100;
System.out.println(num);
}
public static void main(String[] args) {
staticTest st1=new staticTest();
staticTest st2=new staticTest();
System.out.println(staticTest.num);
}
}
//结果200 300 300
继承
继承本质:对某一批类的抽象,从而实现对现实世界更好的建模
extends:扩展,子类是父类的扩展
java中只有单继承,没有多继承
继承是类与类之间的关系。除此之外,类和类之间的关系还有依赖,组合,聚合等
继承关系的两个类,一个是子类(派生类),一个是父类(基类)。子类继承父类使用关键字extends来表示
子类和父类之间,从意义上讲应该具有is a的关系
使用继承
-
编写父类
访问修饰符 class Pet{//公共的属性和方法}
-
编写子类,继承父类
访问修饰符 class 子类名 extends 父类名{//子类特有的属性和方法}
继承是Java中实现代码重用的重要手段之一。Java中只支持单根继承
一个子类只能有一个父类,一个父类可以有多个子类
public class Dog extends Pet{
private String strain="聪明的拉布拉多犬";//品种
public void setStrain(String strain){
this.strain =strain;
}
public String getStrain(){
return this.strain;
}}
public class Penguin extends Pet{
private String sex="男";//品种
public void setSex(String sex){
this.sex =sex;
}
public String getSex(){
return this.sex;
}}
public class Pet {
String name="无名氏";//昵称,默认是无名氏
//1.隐藏属性
private int health=100;//健康值,默认是100,健康值在0-100之间,小于60 不健康
private int love=0;//亲密度
public void setHealth(int health){
if(health<0||health>100){
System.out.println("宠物的健康值只能在0-100之间");
this.health=60;
return;
}
//获取用户给的health
this.health=health;
}
public int getHealth(){
return this.health;
}
public void setName(String name){
this.name =name;
}
public String getName(){
return this.name;
}
public void setLove(int love) {
if (love < 0 || love > 10) {
System.out.println("宠物的亲密度只能在0-10之间");
this.love = 6;
return;
}
this.love=love;
}
public int getLove(){
return this.love;
}
public void print(){
System.out.println("宠物的自白:\n我的名字叫"+this.name+",健康值是:"+this.health
+",和主人的亲密度是:"+this.love+"。");
}
}
public class TsetPet {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
System.out.println("欢迎您来到宠物店");
System.out.print("请输入你要领养的宠物的名字");
String name=input.next();
System.out.println("请输入要领养宠物的类型:1.狗狗,2.企鹅");
int typeNo=input.nextInt();
switch (typeNo){
case 1:
System.out.println("请选择狗狗的品种:1.拉布拉多,2.边牧");
int sexId1=input.nextInt();
String strain=(sexId1==1)?"拉布拉多":"边牧";
System.out.println("请输入狗狗的健康值");
int health1=input.nextInt();
System.out.println("请输入狗狗的亲密度");
int love1=input.nextInt();
Dog dog=new Dog();
dog.setHealth(health1);
dog.setLove(love1);
dog.setName(name);
dog.setStrain(strain);
dog.print();
break;
case 2:
System.out.println("请选择企鹅的性别:1.Q妹,2.Q仔");
int sexId=input.nextInt();
String sex=(sexId==1)?"Q妹":"Q仔";
System.out.println("请输入企鹅的健康值");
int health=input.nextInt();
System.out.println("请输入企鹅的亲密度");
int love=input.nextInt();
Penguin p=new Penguin();
p.setHealth(health);
p.setLove(love);
p.setName(name);
p.setSex(sex);
p.print();
break;
default:
System.out.println("暂时没有这个类型的宠物!");
break;
}
}
}
子类能继承父类的什么
- 继承public和protected修饰的属性和方法,不管是否在同一个包中
- 继承默认权限修饰符的属性和方法,必须在同一个包中
package cn.com.oop1;
public class Person {
private int age;
protected String name;
char sex;
public String address;
}
package cn.com.oop1;
//通过public来修饰
public class Student extends Person{
public void m1(){
//子类可以继承父类的protected成员,不管父子类是否在同一个包
System.out.println(name);
//报错,父类私有。子类无法继承父类的private成员
//System.out.println(age);
System.out.println(sex);
System.out.println(address);
}
}
package cn.com.oop2;
public class Teacher extends Person{
public void m1(){
System.out.println(name);
//报错,父类私有。子类无法继承父类的private成员
//System.out.println(age);
//同包子类可以继承父类的默认修饰符修饰的成员,不同包不能继承
//System.out.println(sex);
System.out.println(address);
}
}
子类不能继承父类的什么
- private成员
- 子类和父类不在同一个包里,默认修饰符不能继承
- 构造方法,但是可以调用
object类(祖宗)
super(父)——this(当前的)
注
- super调用父类的构造方法,必须在构造方法的第一个
- super必须只能出现在子类的方法或者构造方法中
- super和this不能同时调用构造方法
Vs this
- 代表的对象不同
this:本身调用者这个对象
super:代表父类对象的应用
-
前提
this:没有继承也可以使用
super:只能在继承条件下使用
-
构造方法
this:本类的构造
super:父类的构造
想要宠物实现自己的独有属性?
- 子类分别自己进行输出
- 在子类分别定义方法,输出自己独有的信息
- 子类重写父类方法
方法重写或覆盖
- 子类根据需求从父类继承的方法进行重写
- 重写时,可以用super.方法的方式来保留父类的方法
- 构造方法不能被重写
- 有继承关系的类之间,同名方法
public class Dog extends Pet{
private String strain="聪明的拉布拉多犬";//品种
public void setStrain(String strain){
this.strain =strain;
}
public String getStrain(){
return this.strain;
}
public void print(){
//调用父类的print()
super.print();
System.out.println("我是一只:"+this.strain);
}
}
public class Penguin extends Pet{
private String sex="男";//品种
public void setSex(String sex){
this.sex =sex;
}
public String getSex(){
return this.sex;
}
public void print(){
//调用父类的print()
super.print();
System.out.println("我是一只:"+this.sex);
}
}
//Student是子类,Person是父类
public class Student extends Person{
public Student(){
System.out.println("Student无参执行了")
}
private String name="qinjiang";
public void print(){
System.out.println("Student");
}
public void test(String name){
System.out.println(name);//秦疆
System.out.println(this.name);//qinjaing
System.out.println(super.name);//kuangshen
}
public void test2(){
print();
this.print();//自己的print
super.print();//父类的print
}
//父类
public class Person/*extends Object */{
public Person(){
System.out.println("Person无参执行了")
}
public String name="kuangshen";
//public改成private,后面的super.print就会出问题
//显示私有的无法被继承
public void print(){
System.out.println("Person");
}
}
public class Application{
public static void main(String[] args){
Student student=new Student();
student.test=("秦疆");
}
}
//输出秦疆 qinjaing kuangshen
public class Application1{
public static void main(String[] args){
Student student=new Student();
student.test2();
}
}
//输出 Student Student Person
//Student是子类,Person是父类
public class Student extends Person{
public Student(){
//隐藏代码:调用了父类的无参构造super();
//super();必须放在子类构造器的第一行,否则会报错
//如果父类是有参构造,子类不能调用无参,必须在父类中先定义一个无参,子类可以直接调用父类的有参
System.out.println("Student无参执行了")
}
private String name="qinjiang";
public void print(){
System.out.println("Student");
}
public void test(String name){
System.out.println(name);//秦疆
System.out.println(this.name);//qinjaing
System.out.println(super.name);//kuangshen
}
public void test2(){
print();
this.print();//自己的print
super.print();//父类的print
}
//父类
public class Person/*extends Object */{
public Person(){
System.out.println("Person无参执行了")
}
protected String name="kuangshen";
//public改成private,后面的super.print就会出问题
//显示私有的无法被继承
public void print(){
System.out.println("Person");
}
}
public class Application{
public static void main(String[] args){
Student student=new Student();
//student.test=("秦疆");
//student.test2();
}
}
//输出结果 Person无参执行了 Student无参执行了
方法重写
//子类是A,父类是B
//重写是方法的重写,和属性无关
public class B{
public static void test(){
System.out.println("B=>test()");
}
}
public class A extends B{
public static void test(){
System.out.println("A=>test()");
}
}
public class Application{
public static void main(String[] args){
A a=new A();
a.test;
}
}
//输出结果A=>test()
public class Application{
public static void main(String[] args){
//静态方法:方法的调用只和左边有关,定义的数据类型
A a=new A();
a.test;//A
//父类的引用指向了子类
B b=new A;
b.test;//B
}
}
//输出结果 A=>test() B=>test()
public class B{
public void test(){
System.out.println("B=>test()");
}
}
public class A extends B{
@Override//单词本身是重写的意思。 注解:有功能的注释
public void test(){
System.out.println("A=>test()");
//变成
//super.test();
}
}
public class Application{
public static void main(String[] args){
//静态方法和非静态的方法区别很大
//静态方法:方法的调用只和左边有关,定义的数据类型
//非静态方法可以选择重写
A a=new A();
a.test;
B b=new A;//子类重写了父类的方法
b.test;
}
}
//输出结果 A=>test() A=>test()
重写注:条件:需要有继承关系,子类重写父类的方法
- 方法名必须相同
- 参数列表必须相同
- 修饰符:范围可以扩大不能缩小 public>protected>default>private
- 抛出异常:范围可以缩小不能扩大ClassNotFoundException–>Exception(大)
- 重写子类和父类必须一致,方法体不同
为什么需要重写:
- 父类功能子类并不一定需要或者不一定满足
- Alt+Insert; override
super介绍
- 了解super的作用
- 借助super能实现子类访问父类成员
子类访问父类成员
-
子类访问父类方法(必须是非private方法)
super.print();
-
访问父类属性;不可以调用private属性,可以调用非private属性
super.name;
-
访问父类构造方法
super();调用父类无参构造方法
super(name);调用父类有参构造方法
super关键字来访问父类的成员
-
使用super关键字,super代表父类对象
-
super只能出现在子类的方法和构造方法中
-
super调用构造方法时,super只能是第一句
-
super不能访问父类的private成员
区别 this super 访问属性 访问本类属性,如果没有就从父类找 访问父类的属性 访问方法 访问本类方法,如果没有就从父类找 访问父类中的方法 访问构造 访问本类构造,放在构造方法首行 调用父类构造,放在子类构造方法首行
访问父类的父类的成员,用什么方式?
也是通过super就可以
java支持单根继承,可以有多个间接父类
//父类:人类
public class Person extends Animal{
private String name;
public String getName(){
return name;
}
public void setSex(String name){
this.name=name;
}
}
//孙子类
public class Student extends Person{
private String sid;
public String getSid(){
return sid;
}
public void setSid(String sid){
this.sid=sid;
}
public void print(){
super.print();
}
public static void main(String[] args) {
Student s=new Student();
s.print();
}
}
//爷爷类
public class Animal {
private int age;
private String sex;
public int getAge(){
return age;
}
public void setAge(int age){
this.age=age;
}
public String getSex(){
return sex;
}
public void setSex(String sex){
this.sex=sex;
}
public void print(){
System.out.println("这是爷爷类的方法");
}
}
比较项 | 位置 | 方法名 | 参数表 | 返回值 | 访问修饰符 |
---|---|---|---|---|---|
方法重写 | 子类 | 相同 | 相同 | 相同或是其子类 | 不能比父类严格 |
方法重载 | 同类 | 相同 | 不相同 | 无关 | 无关 |
多态
多态:同一方法可以根据发送对象的不同采用多种不同的行为方式。
一个对象的实际类型是确定的,但可以指向对象的引用类型有很多,不确定
多态条件:
-
有继承关系
2. 子类重写父类 2. 父类引用指向子类对象
public class Student extends Person{
}
public class Person{
public void run(){
System.out.println("run");
}
}
//重写
public class Person{
@Override
public void run(){
System.out.println("son");
}
public void eat(){
System.out.println("ear");
}
}
public class Application{
//一个对象的实际类型是确定的
//new Student();
//newPerson();
//可以指向的引用类型就不确定了
//Student能调用的方法都是自己的或者继承父类的
Student s1=new Student();
//Person付类型,可以指向子类,到时不能调用子类独有的方法
Person s2=new Student();//父类
String s3=new Student();//报错
Object 上=new Student();//object是祖宗类
s2.run();//子类重写了父类的方法,执行子类的方法
s1.run();
}
public class Application{
//一个对象的实际类型是确定的
//new Student();
//newPerson();
//可以指向的引用类型就不确定了
sStudent s1=new Student();
Person s2=new Person();//父类
String s3=new Student();//报错
Object 上=new Student();//object是祖宗类
//能执行哪些方法主要看左边的类型,和右边关系不大
s2.eat();//报错,改成((Student)s2).eat();强制转换
s1.eat();
}
注意
-
多态是方法的多态,属性没有多态
-
父类和子类有联系,(类型转换异常)ClassCastException!(类型转换异常)
-
存在条件:继承关系,方法需要重写,父类引用指向子对象! father f1=new son();
不能重写,不能实现多态
- static方法,属于类,不属于实例
- final常量
- private
instanceof和类型转换
instanceof:判断两个类之间是否存在父子关系,引用类型(判断一个对象是什么类型)
public class Application{
public static void main(String[] args) {
//object>person>teacher
//object>person>student
//object>string
Object object=new Student();
System.out.println(object instanceof Student);//true
System.out.println(object instanceof Person);//true
System.out.println(object instanceof Object);//true
System.out.println(object instanceof Teacher);//false
System.out.println(object instanceof String);//false
Person person=new Student();
System.out.println(person instanceof Student);//true
System.out.println(person instanceof Person);//true
System.out.println(person instanceof Object);//true
System.out.println(person instanceof Teacher);//false
System.out.println(person instanceof String);//报错
Student student=new Student();
System.out.println(student instanceof Student);//true
System.out.println(student instanceof Person);//true
System.out.println(student instanceof Object);//true
System.out.println(student instanceof Teacher);//报错
System.out.println(student instanceof String);//报错
}
}
public class Person{
public void run(){
System.out.println("run");
}
}
public class Student extends Person{
public void go(){
System.out.println("go");
}
public class Teacher extends Person{
}
System.out.println(X instanceof Y);//能不能通过编译
类型之间的转换
//类之间的转化:父 子
//高 低
public class Person{
public void run(){
System.out.println("run");
}
}
public class Student extends Person{
public void go(){
System.out.println("go");}
}
public class Application{
public static void main(String[] args) {
Person obj=new Student();
student.go();//报错,需要转换
//将student对象转换成Student,就可以使用了
(Student)student=(Student) obj;
student.go();//上面两行换成((Student)obj).go();
}
}
public class Person{
public void run(){
System.out.println("run");
}
}
public class Student extends Person{
public void go(){
System.out.println("go");
}
}
public class Application{
public static void main(String[] args) {
//子类转换成父类,可能会丢失一些方法
Student student=new Student();
student.go();
Person person=student;
person.go();//自动换成((Student)person).go();
}
}
条件
- 父类引用指向子类对象
- 把子类转换成父类,向上转型
- 把父类转换成子类,向下转型;强制转换
- 方便方法的调用,减少重复的代码,简洁
static总结
public class Student{
private static int age;//静态变量 多线程
private double score;//非静态变量
public static void main(String[] args) {
Student s1=new Student();
System.out.println(Student.age);
System.out.println(Student.score);//报错,
System.out.println(s1.age);
System.out.println(s1.score);
}
}
public class Student{
private static int age;//静态变量 多线程
private double score;//非静态变量
public void run(){
}
public static void go(){
}
public static void main(String[] args) {
Student.go();
//go();也可以
run();//报错
}
public class Person{
//2
{
System.out.println("匿名代码块");
}
//1
static{
System.out.println("静态代码块");
}
//3
public Person(){
System.out.println("构造方法");
}
public static void main(String[] args){
Person person=new Person();
}
}
//输出 静态代码块 匿名代码块 构造方法
public class Person{
//2,可以用这种方法赋初始值
{
System.out.println("匿名代码块");
}
//1
static{
//只执行一次
System.out.println("静态代码块");
}
//3
public Person(){
System.out.println("构造方法");
}
public static void main(String[] args){
Person person=new Person();
System.out.println("========");
Person person1=new Person();
}
}
//输出 静态代码块 匿名代码块 构造方法 ========匿名代码块 构造方法
public class Test{
public static void main(String[] args){
System.out.println(Math.random());
}
}
//修改
import static java.lang.Math.random;
import static java.lang.Math.PI;
public class Test{
public static void main(String[] args){
System.out.println(random());
System.out.println(PI);
}
}
被final修饰的类不能被继承
抽象类
abstract修饰符可以用来修饰方法,也可以用来修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那么该类就是抽象类
抽象类中可以没有抽象方法,但是抽象方法的类一定要声明为抽象类
抽象方法,只有方法的声明,没有方法的实现,他是用来让子类实现的
子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类
抽象类特点:
-
抽象类不能用new关键字来创建对象,是用来让子类继承的;约束
-
抽象类可以写普通的方法
-
抽象方法必须在抽象类中
//抽象类的所有方法,继承了它的子类,都必须实现它的方法
//除非子类也是抽象类
public abstract class A extends stat_ic{
@Override
public void doSomething() {
}
}
//abstract抽象类:类 extends 单继承 多继承
//接口可以多继承
public abstract class stat_ic {
//约束,有人帮我们实现
//abstract 抽象方法,只有方法名字,没有方法的实现
public abstract void doSomething(){
}
}
思考
抽象类存在构造器吗
抽象类存在的意义:提供开发效率,扩展性强
异常
异常:软件程序运行过程中,非常可能遇到的异常问题,英文Exception
异常指程序运行中出现的不期而至的各种情况:文件找不到,网络连接失败,非法参数等
异常发生在程序运行期间,他影响了正常的程序执行流程
异常分类:
- 检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,在编译时不能被忽略
- 运行时异常:可能被程序员避免的异常,可以在编译时被忽略
- 错误ERROR:不是异常,而是脱离程序员控制的问题。在代码中通常被忽略,编译时也可能检查不出来
异常结构图
ERROR:ERROR类对象由Java虚拟机生成并抛出
Exception异常
- 数组下标越界ArraylndexOutOfBoundsException
- 运行时异常 RuntimeException
- 空指针异常 NullPointerException
- 算术异常 ArithmeticException
- 丢失资源 MissingResourcesException
- 找不到类 ClassNotFoundException
ERROR异常
- 栈溢出StackoVERfLOW
- 内存溢出OutOfMenory
两者区别:
ERROR通常是灾难性的致命错误,是城西湖无法处理的控制的
Exception通常可以被程序处理的,应该尽可能处理这些异常
异常处理机制
抛出异常
捕获异常
异常处理的五个关键字:try {},catch() ,finally{}, throw内部手动抛出异常 ,throws方法抛出
public static void main(String[] args) {
int a=1;
int b=0;
try {
//try可以监控区域
System.out.println(a/b);
} catch (Exception e) {//catch捕获异常
System.out.println("程序出现异常,变量b不能为0");
}finally {//处理善后工作
System.out.println("finally");
}
//try和catch必须存在,可以不要finally
//IO流,finally可以用来进行资源关闭
}
//输出程序出现异常,变量b不能为0
//finally
public static void main(String[] args) {
int a=1;
int b=0;
try {
//try可以监控区域
new exception().a();
} catch (Exception e) {//catch捕获异常
System.out.println("程序出现异常,变量b不能为0");
}finally {//处理善后工作
System.out.println("finally");
}
//try和catch必须存在,可以不要finally
//IO流,finally可以用来进行资源关闭
}
public void a(){
b();
}
public void b(){
a();
}
}
//输出结果finally+报错
public static void main(String[] args) {
int a=1;
int b=0;
try {
//try可以监控区域
new exception().a();
} catch (Throwable e) {//catch捕获异常
System.out.println("程序出现异常,变量b不能为0");
}finally {//处理善后工作
System.out.println("finally");
}
//try和catch必须存在,可以不要finally
//IO流,finally可以用来进行资源关闭
}
public void a(){
b();
}
public void b(){
a();
}
}
//输出结果程序出现异常,变量b不能为0
//finally
catch( ),里面是想要捕获的异常类型。最高Throwable,其次是ERROR和Exception
//捕获多个异常
public static void main(String[] args) {
int a=1;
int b=0;
try {
//try可以监控区域
//要捕获多个异常,从小到大
System.out.println(a/b);
} catch (Error e) {//catch捕获异常
System.out.println("Error");
}
catch (Exception e) {//catch捕获异常
System.out.println("Exception");
}
catch (Throwable e) {//catch捕获异常
System.out.println("Throwable");
} finally {//处理善后工作
System.out.println("finally");
}
//try和catch必须存在,可以不要finally
//IO流,finally可以用来进行资源关闭
}
//输出Exception finally
Ctrl+alt+T快捷键代码块
//程序结束代码
System.exit(0);
//主动抛出异常,一般在方法中使用
public class exception {
public static void main(String[] args) {
new exception().test(1,0);
}
//假设这个方法中处理不了异常,方法上抛出异常
public void test(int a,int b){
if (b == 0) {
throw new ArithmeticException();
}
}
}
//假设这个方法中处理不了异常,方法上抛出异常
public void test(int a,int b) throws ArithmeticException{
}
自定义异常
Java内置的异常类可以处理大多数异常,此外,用户还可以自定义异常
用户自定义异常,只需要继承Exception类即可
自定义异常步骤
- 创建自定义异常类
- 通过throw关键字抛出异常对象
- 在当前抛出异常方法中处理异常,用try-catch语句捕获,否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作
- 在出现异常方法的调用者捕获并处理异常
static void test(int a) throws ai{
System.out.println("传递的参数为"+a);
if(a>10){
throw new ai(a);
}
System.out.println("OK");
}
public static void main(String[] args) {
try {
test(11);
} catch (ai e) {
System.out.println("ai=>"+e);
}
}
}
//输出传递的参数为11 ai=>ai{detail=11}
总结
- 处理运行时异常,采用逻辑去合理规避同时辅助try -catch处理
- 多重catch块后面,可以加一个catch(Exception)来处理可能遗漏的异常
- 不确定的,加上try -catch,处理潜在的异常
- 尽量去处理异常,切忌只是简单的调用printStackTrace()去打印输出
- 根据不同的业务需求和异常类型去处理
- 尽量添加finally语句块去释放占用的资源