Java基础
Dos命令行学习
打开cmd的方式
- 1、在任意的文件夹下面,按住shift+鼠标右键点击,在此处打开命令行
- 2、资源管理器的地址栏前面加上cmd路径
管理员方试运行:选择以管理员方式运行
常用的Dos命令
# 盘符切换 E:
# 查看当前目录下所有文件 dir
# 切换目录 cd /d E:\idea
# 返回上一级目录 cd ..
# 进入同级目录下的下一级目录 cd tmp(该目录下的文件名)
# 清屏 cls (clear screen)
# 退出终端 exit
# 查看电脑当前IP地址 ipconfig
# 打开计算器 calc
# 打开画图 mspaint
# 新建记事本 notepad
# 在当前目录新建文件夹 md test(文件夹名)
# 新建文件 cd> a.txt(文件名)
# 删除文件 del a.txt(文件名)
# 删除目录 rd test(目录名)
# ping命令(复制链接进入Dos直接单击鼠标右键粘贴)
ping www.baidu.com
在DOS里新建文件
copy con Demo.java
class Demo{
public static void main(String[] args){
System.out.println("Hello Java");
}
}
Ctrl+Z 保存
type Demo.java
编译运行:
javac Demo.java
java Demo
dir 列出当前目录下的文件以及文件夹
md 创建目录
rd 删除目录
cd 进入指定目录
cd.. 退回到上一级目录
cd/ 退回到根目录
del 删除文件
exit 退出dos命令行
JDK
的卸载与安装
卸载JDK
- 删除Java安装目录
- 删除环境变量JAVA_HOME
- 删除path下关于JAVA的目录
- cmd下面运行 java -version
安装JDK
-
百度搜索JDK8,找到下载地址
-
同意协议
-
下载电脑对应的版本
-
双击安装JDK
-
记住安装的路径
-
配置环境变量 在系统变量中配置
- JAVA_HOME F:\Java\jdk1.8.0_111
- CLASSPATH .;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar
- path %JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;
IDEA
同义词 IDE(集成开发环境)一般指集成开发环境
集成开发环境(IDE,Integrated Development Environment )是用于提供程序开发环境的应用程序,一般包括代码编辑器、编译器、调试器和图形用户界面等工具。集成了代码编写功能、分析功能、编译功能、调试功能等一体化的开发软件服务套。所有具备这一特性的软件或者软件套(组)都可以叫集成开发环境。如微软的Visual Studio系列,Borland的C++ Builder、Delphi系列等。该程序可以独立运行,也可以和其它程序并用。IDE多被用于开发HTML应用软件。例如,许多人在设计网站时使用IDE(如HomeSite、DreamWeaver等),因为很多项任务会自动生成。
Java基础
注释
单行注释
可以注释一行文字 //注释
多行注释
可以注释一段文字 /* 注释 */
文档注释
// JavaDoc:文档注释
/**
*@Description HellowWorld
*@Author 狂神说Java
*/
标识符和关键字
标识符
- 所有的标识符都应该以字母(A-Z或者a-z),美元符($),或者下划线(_)开始
- 首字符之后可以是字母(A-Z或者a-z),美元符($),下划线(_)或数字的任何字符组合
- 不能使用关键字作为变量名或方法名
- 标识符是大小写敏感的
- 合法标识符举例:age、$salary、_value、___1_value
- 非法标识符举例:123abc、-salary、#abc
- 可以使用中文命名,但是一般不建议这样去使用,也不建议使用拼音,很Low。
关键字
数据类型
数据类型的讲解
- 强类型语言(安全性高、速度相对比较慢)
Java是强类型语言:要求变量的使用严格符合规定,所有变量都必须先定义后才能使用
- 弱类型语言(速度就比较快,安全性就没那么高)
可以不符合变量的使用规定,所有变量都必须先定义后才能使用,比如JavaScript等
- Java的 数据类型分为两大类
- 基本数据类型(primitive type)
- 引用数据类型(reference type)
什么是字节
- 位(bit):是计算机 内部数据 储存的最小单位 ,11001100是一个八位二进制数
- 字节(byte):是计算机中 数据处理 的基本单位,习惯上用大写B来表示
1B
(byte,字节) = 8 bit (位)- 字符:是指计算机中使用的字母,数字,字和符号
拓展(面试)
进制
- 二进制
0b
开头 - 十进制
- 八进制 0 开头
- 十六进制
0x
开头
整型拓展
//整数扩展: 二进制0b 八进制0 十进制 十六进制0x
int i = 10
int i2 = 010; //八进制0
int i3 = 0x10; //十六进制0x 0~9 A~F 16
浮点型拓展
//float 美[floʊt] 浮点数表现的字长是有限的,也是离散的,舍入误差,大约,接近但不等于
//double 美[ˈdʌbl]
//最好完全避免使用浮点数进行比较
//最好完全避免使用浮点数进行比较
//最好完全避免使用浮点数进行比较
//少去使用浮点数进行比较
//BigDecimal 数学工具类 可用于银行业务表示
//面试题:银行业务字母怎么表示钱? BigDecimal(数学工具类)
//float double是有问题的,最好避免使用浮点数进行比较
float f = 0.1f; //0.1
double d = 1.0/10; //0.1
System.out.println(f==d); //false
//浮点数 位有限,舍入误差,大约
//最好避免使用浮点数进行比较
float f1 = 23131313131f;
float f2 = f1+1;
System.out.println(f1==f2); //true
字符拓展
//所有字符本质还是数字
char c1 = 'a';
char c2 = '中';
//我们通过单引号来表示字符常量
System.out.println(c1); //a
System.out.println((int)c1);//强制类型转换,97
System.out.println(c2); //中
System.out.println((int)c2);//强制类型转换,20013
//强制转换可以把字符转换成数字,所有的字符本质还是数字
//char类型会涉及到一个编码问题
// Unicode 编码表:97=a 65=A (本质上还是数字)
// 2字节 65536字符 Excel最长有2的16次方=65536
// U0000 UFFFF
char c3='\u0061';
System.out.println(c3);//a
//转义字符:
//数字编码:‘0’:48,‘1’: 49…
//英文编码:‘A’:65,‘B’:66…
//小写英文:‘a’ : 97,‘b’:98…
布尔值扩展
bolean flag = true;
if (flag == true){} //新手
if (flag){} //老手
//Less is More 代码要精简易读
数据类型的类型转换
由于Java是强类型语言,所以要进行有些运算的时候,需要用到类型转换。
容量高–>低:
常量、字符串等是可以混合运算的,char本质还是一个数字
低→→→→→→→→→→→→→→→→→→→→→→→→→→高
byte,short,char——-int——-long——-float———-double
*** 运算中,不同类型的数据先转化为同一类型,然后进行运算。***
- 强制类型转换(高到低需要强制转换)
- 自动类型转换(低到高就不需要强制转换)
-
强制转换 (类型)变量名 由高到低(容量)
注意点:
-
不能对布尔值进行转换
-
不能把对象类型转换为不相干的类型
-
在把高容量转换到低容量的时候,强制转换
-
转换的时候可能存在内存溢出,或者精度问题
System.out.println((int)23.7); //23 System.out.println((int)-45.89f); //-45
-
-
自动转换 由低到高(容量)
public class Demo02 { public static void main(String[] args) { int i=128; byte b=(byte)i;//内存溢出,byte的最大值是127,这个时候就导致内存溢出。 //在赋值前面加上一个英文的括号() 里面加上转换类型,就是强制转换,高转低的时候才会强制转换。(类型)变量名 /* 强制类型转换(高到低需要强制转换) 自动类型转换(低到高就不需要强制转换) 注意点: 1.不能对布尔值进行转换 2.不能把对象类型转换为不相干的类型 3.在把高容量转换到低容量的时候,需要强制转换 4.转换的时候可能存在内存溢出,或者精度问题 */ System.out.println(i);//输出128 System.out.println(b);//输出-128 System.out.println("==============================="); System.out.println((int)23.7);//输出结果23,Double类型,高转低(强制转换) System.out.println((int)-45.89f);//输出结果-45,float类型,高转低(强制转换) System.out.println("==============================="); char c='a'; int d=c+1; System.out.println(d);//输出98 System.out.println((char) d);//输出b,a后面+1就是b,“ASCII编码表” } }
小知识
//操作比较大的数的时候,注意溢出问题
//JDK7新特性,数字之间可以用下划线分割 eg:int money = 10_0000_0000;
//浮点型:float a = 3.1415gf; 末尾用f来表示float和double的区别;
变量、常量、作用域
- 变量是什么:就是可以变化的量
- Java是一种强类型语言,每个变量都必须声明其类型
- Java变量是程序中最基本的存储单元,要素包括变量名,变量类型和作用域
//数据类型 变量名 = 值;
type varName [=value][{,varName[=value]}];
//可以使用逗号隔开同多个类型的变量,但不建议在一行定义多个变量
- 注意事项:
- 每个变量都有类型,类型可以是基本类型,也可以是引用类型。
- 变量名必须是合法的标识符。
- 变量声明是一条完整的语句,因此每一个声明都必须以分号结束。
变量作用域
-
类变量(static)
在类里,整个类里可以使用,前面必须加static;“static int a = 666;”
-
实例变量
在类里,从属于对象;“int age;//0”
如果不自行初始化,这个类型的默认值 0,0.0
布尔值:默认是false;除了基本类型,其他的默认值都是null;
-
局部变量
在方法中,使用时必须声明和初始化值;“int cba = 666;”
public class Variable{
static int allClicks = 0; //类变量
String str = "hello world"; //实例变量
public void method(){
int i=0; //局部变量
}
}
常量
- 常量(Constant):初始化(initialize)后不能再改变值!不会变动的值。
- 所谓常量可以理解成一种特殊的变量,他的值被设定后,在程序运行过程中不允许被改变。
//常量一般用大写字符
final 常量名=值;
final double PI=3.14;
//修饰符 不存在先后顺序,static可以写final后面
static final doube PI=3.14; //类变量,该类下的全局范围
- 常量名一般使用大写字符。
变量的命名规范
- 所有变量、方法、类名:见名知意
- 类成员变量:首字母小写+驼峰原则:lastName
- 局部变量:首字母小写+驼峰原则
- 常量:大写字母和下划线:MAX_VALUE
- 类名:首字母大写+驼峰原则:Man,GoodMan
- 方法名:首字母小写+驼峰原则:run(),fastRun()
运算符
- Java语言支持如下运算符:优先级:最好使用()
- 算术运算符:+,-,*,/,%(余),++(自增),–(自减);
- 赋值运算符:=;
- 关系运算符:> , < , >= , <= , == , != (不等于的意思), instanceof;
- 返回的结果:正确,错误,布尔值
- 计算结果一定是true or false;以后会和if一起用
* 逻辑运算符:&& (与), ||(或), !(非);
* 计算结果一定是true or false - 位运算符:&,|,^,~,>>,<<,>>>;
- 条件运算符:?,:;
* 扩展赋值运算符:+=,-=,*=,/=;
int a=10;
int b=20;
System.out.println(a/b); //0
System.out.println((double)a/b); //0.5
long c=12300000000;
System.out.println(a+b); //int a = 6
System.out.println(a+c); //long 自动转换式子中容量大的数据类型
算术(一元)运算符
++(自增)
a++: ++在后时,先赋值,再自增;
++a: ++在前时,先自增,再赋值;
–(自减)同理
- 注意事项:
- 算术运算符使用中,有一个为long或者double类型,其结果为long或者double类型,不然为int类型。
- 很多运算java,会使用一些很多工具类来操作
// ++自增 --自减 单目运算符
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
//幂运算 2^3 2*2*2=8
double pow = Math.pow(2,3); // (底数,指数)double型
System.out.println(pow); //8.0
//扩展:笔试题 i=5 s=(i++)+(++i)+(i--)+(--i) s=?
int i=5;
int s=(i++)+(++i)+(i--)+(--i);
System.out.println(s); //24
逻辑运算符
- && 逻辑与运算:两个变量都为真,结果为true
- || 逻辑与运算:两个变量有一个为真,结果为true
- ! 取反,真变为假,假变为真
// 与(snd) 或(or) 非(取反)
boolean a = true;
boolean b = false;
System.out.println(a&&b); // false
System.out.println(a||b); // true
System.out.println(!(a&&b)); // true
//短路运算
int c=5;
boolean d = (c<5)&&(c++<5); //第一个值为false,后面就不进行判定了
System.out.println(d); //false
System.out.println(c); //5 c++未执行
短路运算
当前面为“false”时,后面的将不执行,只有“true”才会运行,这就叫短路运算。
int z = 5;
boolean x = (z<4)&&(z++<4);//由于(z<4)不成立为false,(z++<4)就不运行
System.out.println(x);//(false)
System.out.println(z);//(5)由于(z++<4)没有运算,所以z没有自增
位运算
& (1):全部为1才为1,否者为0
| (0):全部为0才为0,否者为1
^(同0):相同为0,否者为1
~(取反):取二进制相反的数
/*
A = 0011 1100
B = 0000 1101
A&B 0000 1101 按位与
A|B 0011 1101 按位或
A^B 0011 0001 异或
~B 1111 0010 非
面试题:2*8 怎么算最快? 2<<3
<<左移 *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.println(2<<3); // 16
扩展运算符
+=,-=,*=,/=;()
int a = 5, b = 10;
a+=b;//a=a+b(15)
a-=b;//a=a-b(-5)
a*=b;//a=a*b(50)
a/=b;//a=a/b(0.5)
字符串连接符
int a = 10;
int b = 20;
a+=b; // a = a+b
a-=b; // a = a-b
System.out.println(a); //10
//字符串连接符 + ,转化为String类型,然后拼接 注意!!
System.out.println(""+a+b); //1020
System.out.println(a+b+""); //30 先进行运算,再转为String拼接
System.out.println(a+b+"str"); //30str
&和&&还有|和||的区别
-
&和|可用于位操作,即二进制运算,而||不能。
-
在逻辑运算中,||被称为短路逻辑运算符,它的意思是先判断左边的逻辑值。
如果你能决定结果,你就不会浪费时间去判断右边的逻辑值。
例如(2<3) || (a*5+b/3-c>5),因为(2<3)为真,右边为真或假,结果为真,所以右侧不再被判定。
而|总是由双方决定,称为非短路逻辑运算符
在逻辑运算时,&& 和 || 叫做short-circuit logical operator, 意思是先判定左侧的逻辑值,如果可以决定结果则不再浪费时间去判定右侧的逻辑值。例如(2<3) || (a*5+b/3-c>5),因为(2<3)是true,无论右侧是true or false,结果都是true, 所以右侧将不再进行判定。而& 和 | 则总会对两侧进行判定,称为non-short-circuit logical operator.
三元运算符
// x ? y : z
//如果x为真,则结果为y,否则为z
//if(x) y; else z;
int score = 80;
String type = score<60?"及格":"不及格";//必须掌握,精简代码
System.out.println(type); //及格
包机制
-
为了更好地组织类,Java提供了包机制,由于区分类名的命名空间
-
包的语法格式:
package pkg1[.pkg2[.pkg3...]];
-
一般利用公司域名倒置作为包名;com.kuangstudy.www
-
为了能够使用一个包的成员,需要在Java程序中导入该包
import package1[.package2...].(className|*); //通配符* 表示:导入包下所有的类
- 参考:阿里巴巴Java开发手册
JavaDoc生成文档
-
javadoc命令是用来生成自己API文档的
-
参数信息
- @author 作者名
- @version 版本号
- @since 指明最早用的jdk版本
- @param 参数名
- @return 返回值情况
- @throws 异常抛出情况
JavaAPI文档地址
https://docs.oracle.com/javase/8/docs/api/
/**
* @author Kuangshen
* @version 1.0
* @since 1.8
*/
public class Demo05 {
String name;
/**
* @author kuangshen
* @param name
* @return
* @throws Exception
*/
public String test(String name) throws Exception{
return name;
}
}
- 打开某个类所在文件夹下的cmd命令行
- 输入:javadoc -encoding UTF-8 -charset UTF-8 Doc(类名).java
- 会自动生成该类有关的API文档,查看文件夹发现多了一些文件
- 打开 index.html(首页)查看文档注释
IDEA如何配置后生成javadoc文档
解释一哈:
- 选择是整个项目还是模块还是单个文件
- 文档输出路径
- Locale 选择地区,这个决定了文档的语言,中文就是zh_CN
- 传入JavaDoc的参数,一般这样写 -encoding UTF-8 -charset UTF-8 -windowtitle “文档HTML页面标签的标题” -link http://docs.Oracle.com/javase/7/docs/api
可以看到控制台的输出:
然后找到生成的html:
看一下效果:
Java流程控制
Scanner对象
- 之前我们学的基本语法并没有实现程序和人的交互,Java给我们提供了一个工具类,可以获取用户的输入java.util.Scanner是Java5的新特征,我们通过Scanner类来获取用户的输入。
- 基本语法
Scanner s = new Scanner(System.in);
- 通过Scanner类的 next()与 nextLine()方法获取用户的字符串,读取前一般用hasNext()与hasNextLine()判断是否还有输入的数据。
//创建一个扫描器对象
Scanner scanner = new Scanner(System.in);
System.out.println("使用next方式接收");
//判断用户有没有输入字符串
if(scanner.hasNext()){ //使用hasNext()会接收一行 "hello word"
String str = scanner.next(); //使用next方式接收
System.out.println("输入的内容为:"+str);
//input: hello word
//输入的内容为:hello
}
//凡是属于IO流的类如果不关闭会一直占用资源
scanner.close();
next()
- 一定要读取到有效字符才可以结束输入
- 对输入有效字符之前遇到的空白,next()方法会将其去掉
- 只有输入有效字符后才将其后面输入的空白作为结束符
- next()不能得到带有空格的字符串
nextLine()
- 以Enter作为结束符,即返回输入回车之前所有的字符
- nextLine()可以获取空白
public class Demo03 {
public static void main(String[] args) {
Scanner ssxxz = new Scanner(System.in); //创建一个扫描器对象,用于接受键盘数据
System.out.println("请输入:");
String str = ssxxz.nextLine();//使用nextLine方式接收
System.out.println("输出为:"+str);//程序会等待用户输入完毕
//凡是属于IO流的类,如果不关闭会一直占用资源,要养成好习惯用完就关闭掉
ssxxz.close();
}
}
import java.util.Scanner;
public class Demo04 {
public static void main(String[] args) {
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("输入的不是整数数据!");
}
System.out.println("请输入小数数据:");
if (scanner.hasNextFloat()){
f = scanner.nextFloat();
System.out.println("小数数据:"+f);
}else{
System.out.println("输入的不是小数数据!");
}
scanner.close();
}
}
练习:
public class Demo05 {
public static void main(String[] args) {
//我们可以输入多个数字,并求其总和与平均数,每输入一个数字用回车确认,通过输入非数字来结束输入并输出执行结果。
Scanner scanner = new Scanner(System.in);
//和
double sum = 0;
//计算输入了多少个数字
int m = 0;
System.out.println("请输入数据:");
//通过循环判断是否还有输入,并在里面对每一次进行求和和统计
while(scanner.hasNextDouble()){
double x = scanner.nextDouble();
m = m +1;//m++
sum = sum+x;
System.out.println("当前是第:"+m+"个,sun总数为:"+sum);
}
System.out.println(m+"个数的和为:"+sum);
System.out.println(m+"个数的平均值是:"+(sum/m));
scanner.close();
}
}
顺序结构
- Java的基本结构就是顺序结构,除非特别指明,否则就按语句一条一条执行。
- 顺序结构是最简单的算法结构。
- 语句语句之间是按从上到下执行的,它是由若干个依次执行的处理步骤组成的,它是如何一种算法都离不开的一种基本算法结构。
选择结构
- if单选择结构 if( )
- if双选择结构 if( ){ }else
- if多选择结构 if( ){ }else if{ }else{}
- 嵌套的if结构 if( )
- switchi多选择结构
if单选择结构
- 我的很多时候需要去判断一个东西是否可行,然后我们才去执行,这样一个过程在程序中用if语句来表示。
- 语法:
if(布尔表达式){
//如果布尔表达式为true将执行的语句
}
- 语句:
import java.util.Scanner;
public class ifDemo01 {
public static void main(String[] args) {
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");
scanner.close();
}
}
if双选择结构
-
语法
if(布尔表达式){ //如果布尔表达式的值为true }else{ //如果布尔表达式的值为false }
-
语句:
import java.util.Scanner; public class IfDemo02 { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.println("请输入你的成绩:"); int c = scanner.nextInt(); if (c>=60){ System.out.println("及格"); }else{ System.out.println("不及格"); } scanner.close(); } }
if多选择结构
-
我们发现刚才的代码不符合时间情况,真实的情况还可能存在ABCD,存在区间多级判断,比如90-100就是A,80-90就是B…等等,在生活中我们很多时候的选择也补仅仅只有两个,所以我们需要一个多选择结构来处理这类问题!
-
语法
if(布尔表达式1){ //如果布尔表达式1的值为true执行代码 }else if(布尔表达式2){ //如果布尔表达式2的值为true执行代码 }else if(布尔表达式3){ //如果布尔表达式3的值为true执行代码 }else{ //如果以上布尔表达式都不为true执行代码 }
if 语句最多有1个 else 语句,else 语句在所以 else if 语句之后。
if 语句可以有若干个 else if 语句,它们必须在 else 语句之前。
一旦其中一个 else if 语句检测为 true, 其它的 else if 以及 else 语句都将跳过执行。
嵌套的if结构
-
使用嵌套的 if…else 语句是合法的。也就是说你可以在另一个 if 或者 else if 语句中使用 if 或者 else if 语句。你可以像 if 语句一样嵌套 else if…else。
-
语法:
if(布尔表达式 1){ //如果布尔表达式 1的值为true执行代码 if(布尔表达式 2){ //如果布尔表达式 2的值为true执行代码 } }
int a = 80;
if(a>60){
System.out.println("及格");
if(a>80) System.out.println("且优秀");
}else if(a>0){
System.out.println("不及格");
}else {
System.out.println("缺考");
}
switch多选择结果
-
多选择结构还有一个实现方式就是 switch case 语句。
-
switch case 语句判断一个变量与一个系列值中某个值是否相等,每个值称为一个分支。
//switch 匹配一个具体的值 switch(expression){ case value : //语句 break://可选,主要功能防止case穿透 case value: //语句 breck:// //你可以有任意数量的case语句 default://可选,当出现无法匹配任何case时,可以用default表达 //语句 }
public static void main(String[] args){ //case 穿透 //switch:匹配一个具体的值 char grade = 'B'; //JDK7新特性 表达式结果可以是字符串 //(字符本质还是数字) //反编译 java---class(字节码文件)---反编译(IDEA) switch (grade){ case 'A': System.out.println("优秀"); break; case 'B': System.out.println("及格"); break; case 'C': System.out.println("一般"); break; default://默认,以上值没匹配到 System.out.println("未知等级") }
-
switchi 语句中的变量类型可以是:
-
byte、short、int 或者 chat.
-
从 Java SE 7开始
-
switch 支持字符串 String 类型
-
同时 case 标签必须为字符串常量或字符量
IDEA反编译方法
IDEA反编译之后.class文件与源代码对比
循环结构
- while 循环
- do…while 循环
- for 循环
- 在Java5中引入了一种主要用于数组的增强型for循环。
while循环
-
while是最基层的循环,它的结构为
while(布尔表达式){ //循环内容 }
-
只有布尔表达式为 true,循环就会一直执行下去。
-
我们大多数情况是会让循环停止下来的,我们需要一个让表达式失败的方式来结束循环。
-
少部分情况需要循环一直执行,比如服务器的请求响应监听等。
-
循环条件一直未true就会造成无限循环【死循环】,我们正常的业务编程中应该尽量避免死循环,会影响程序性能或者造成程序卡死崩溃!
计算1+2+3+…+100=?
public class whileDemo01 {
public static void main(String[] args) {
int i = 0;
int sum = 0;
while(i<=100){
sum = sum+i;
i++;
System.out.println(sum);
}
System.out.println(sum);
}
}
do … while 循环
-
对于 while 语句而言,如果不满足条件,则不能进入循环。但有时候我们需要即使不满足条件,也至少执行一次。
-
do … while 循环和 while 循环相似,不同的是, do … while 循环至少会执行一次。
do{ //代码语句 while(布尔表达式) }
//先执行后判断,至少执行一次
do{
i++;
sum+=i;
}while(i<100) //跟上面效果一样
-
While 和 do-While 的区别:
- while先判断后执行。dowhile是先执行后判断!
- Do … while 总是保证循环体会被至少执行一次!这是他们的主要差别。
public class whileDemo01 { public static void main(String[] args) { int i = 0; int sum = 0; do { sum = sum+i; i++; } while(i>100); { System.out.println("sum="+sum); System.out.println("i="+i); } System.out.println("最终结果:"+sum); } }
For循环
-
虽然所有循环结构都可以用 while 或者 do…while 表达,但是Java提供了另外一种语句 ——for循环,使一些循环结构变得更加简单。
-
for循环语句是支持送代的一种通用结构,是最有效,最灵活的循环结构。
-
for循环执行的次数是在执行前就确定的。
-
语法
for(初始化;布尔表达式;更新){ //代码语句 }
关于for循环的几点说明:
- 最先执行初始化步骤,可以声明一种类型,但可初始化一个或者多个循环控制变量,可以是空句。
- 然后,检测布尔表达式的值,如果是true,循环体被执行,如果为false,循环终止,开始执行循环体后面的语句。
- 执行一次循环后,更新循环控制变量(送代因子控制循环变量的增减)。
- 再次检查布尔表达式,循环执行上面的过程
-
练习1:计算0到100之间的奇数和偶数的和
-
练习2:用while或者for循环输出1-1000之间能被5整除的数,并且每行输出3个。
-
练习3:打印九九乘法表
//计算0到100之间的奇数和偶数的和
public class ForDemo01 {
public static void main(String[] args) {
int oddSum = 0;
int evenSum = 0;
for (int i = 0; i <=100; i++) {
if (i%2!=0){
oddSum=oddSum+i;
}else {
evenSum=evenSum+i;
}
}
System.out.println("偶数合计为:"+oddSum);
System.out.println("奇数合计为:"+evenSum);
}
}
//练习:输出1-1000能被5整除的数,每行输出3个
//方法一:for循环嵌套
public class ForDemo02 {
public static void main(String[] args) {
System.out.println("5的倍数:");
for (int i = 1; i <=1000; i++) {
if (i%5==0){
System.out.print(i+" ");//可以(i+"\t")
}
if (i%(5*3)==0){
System.out.println();//可以print("\n")
}
}
}
}
//方法二:用while
public static void main(String[] args) {
int i = 1;
while (i<=100){
if (i%5==0){
System.out.print(i+" ");
}
if(i%15==0){
System.out.println();
}i++;
}
}
}
//练习2:输出九九乘法表
for(int i=1;i<=9;i++){
for(int j=1;j<=i;j++){
System.out.print(j+"*"+i+"="+i*j+"\t");
}
System.out.println();
}
增强for循环
-
这里我们先只是见一面,做个了解,之后数组我们重点使用。
-
Java5 引入了一种主要用于数组或集合的增强型 for 循环。
-
Java 增强 for 循环语法格式如下:
for(声明语句:表达式) { //代码句子 }
-
声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时数组元素的值相等。
-
表达式:表达式是要访问的数组名,或者是返回值为数组的方法。
public static void main(String[] args) {
int[] numbers = {10,20,30,40,50};//定义了一个数组
for (int i = 0; i < 5; i++) {
System.out.println(numbers[i]);
}
System.out.println("===============================");
for(int x:numbers){
System.out.println(x);
}
}
break & continue
-
break可用在任何循环的主体部分,由于强行退出循环,也可以用在switch语句。
public static void main(String[] args) { int i = 0 ; while (i<100){ i++; System.out.println(i); if (i==30){ break; } System.out.println("break:"+i);//break强制结束,虽然程序还未到达条件(i<100),但是循环已结束。 } System.out.println("程序结束!");//循环外的程序还可以执行 } //输出到30
-
continue用于循环语句中,终止某次循环过程,跳过剩余语句,之间进行下一次循环条件判断。
public static void main(String[] args) { int i = 0 ; while (i<100){ i++; if (i%10==0){ System.out.println(); continue;//直接跳出本循环,继续下次循环,不执行后面的语句 } System.out.print(i); } System.out.println("程序结束!"); }
-
关于 goto 关键字
-
goto 关键字很早就在程序设计语言中出现,尽管 goto 仍是 Java 的一个保留字,但并未在语言中得到正式使用; Java没有 goto 。然而,在 break 和 continue 这两个关键字的身上,我们仍然能看出一些 goto 的影子—带标签的 break 和 continue。
-
“标签”是指后面跟一个冒号的标识符,例如: label:
-
对 Java 来说唯一用到标签的地方是在循环语句之前。而在循环之前设置标签的唯一理由是:我们希望在其中嵌套另一循环,由于 break 和 continue 关键字通常只中断当前循环,但若随同标签使用,它们就会中断到存在标签的地方。
//打印101-150之间所有的质数
int count = 0;
//outer标签
outer:for(int i=101;i<=150;i++){
for (int j=2;j<i/2;j++){
if(i%j==0)
continue outer; //满足条件后回到 outer 记录位置从新运行。//不建议使用标签
}
System.out.print(i+" ");
}
打印三角形
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();
}
打印等腰空心三角形
//打印等腰空心三角形
/* 例如:输入为4时
*
* *
* *
* * * *
*/
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt(); //n为三角形高
for(int i=1;i<=n;i++){
for(int j=1;j<=2*n-1;j++){
if(i!=n){ //若不为最后一行
if(i+j==n+1)
System.out.print("*"); //三角形左腰边
else if(i+j==n+2*i-1)
System.out.print("*"); //三角形右腰边
else System.out.print(" ");
}
else if(j%2!=0){ //最后一行,底边
System.out.print("*");
}else {
System.out.print(" ");
}
}
System.out.println(); //换行
}
Debug的使用
自己查去
Java方法详解
[方法的加深](# 回顾方法及加深) (ps
:按ctrl
键点击跳转)
- 何谓方法
- 方法的定义
- 方法的调用
- 方法的重载
- 命令行传参
- 可变参数
- 递归
何谓方法?
-
System.out.println(),那么它是什么呢?
//System"类".out“输出对象”.println()“方法”
-
Java方法是语句的集合,它们在一起执行一个功能。
- 方法是解决一类问题的步骤的有序组合
- 方法包含于类或对象中
- 方法在程序中被创建,在其他地方被引用
-
设计方法的原则:方法的本意是功能块,就是实现某个功能的语句块的集合。我们设计方法的时候,最好保持方法的原子性,就是一个方法只完成1个功能,这样利于我们后期的扩展。
-
maim尽量干净简洁,把公共模块提取到外面,利用方法调用。
回顾:方法的命名规则?
加法练习:
public static void/*代表空类型*/ main(String[] args) {
//实际参数,实际调用传输给他的参数
int sum = add(1,2);
System.out.println(sum);
}//形式参数,用来定义作用的
public static int/*返回类型*/ add(int a,int b){
return a+b;//返回值
}
方法的定义
方法的定义
-
Java的方法类似与其他语言的函数,是一段用来完成特定功能的代码片段。
-
方法包含一个方法头和一个方法体。
- 修饰符:可选,定义了方法的访问类型,告诉编译器如何调用该方法。
- 返回值类型:方法可能会返回值。returnValueType是方法返回值的数据类型。有些方法没有返回值,则returnValueType为关键字void。return就代表着结束此方法了
- 方法名:是方法的实际名称,方法名与参数表共同构成方法签名。
- 参数类型:像一个占位符。方法被调用时,传递值给参数,该值称为实参或变量。参数列表是指方法的参数类型、顺序和参数个数。参数是可选的,方法可以不包含任何参数。
- 形式参数:在方法被调用时用于接收外界输入的数据。
- 实参:调用方法时实际传给方法的数据。
- 方法体:方法体包含具体的语句,定义该方法的功能。
修饰符 返回值类型 方法名(参数类型 参数名,...){ 方法体... return 返回值;//return就代表着结束此方法了 }
-
return
public static void main(String[] args){ max(20,20); } public static int max(int num1, int num2){ int result = 0; if (num1==num2){ System.out.println("数据相等"); return 0;//终止方法 } if (num1>num2){ result = num1; System.out.println(result); } if (num2>num1){ result = num2; System.out.println(result); } return result; }
方法的调用
-
调用方法:对象名.方法名(实参列表)。
-
Java支持两种调用方法的方式,根据方法是否返回值来选择。
-
当方法返回一个值的时候,方法调用通常被当成一个值。
int larger = max(30,40);
-
如果方法返回值是void,方法调用一定是一条语句。
System.out.println("Hello,kuangshen!");
-
课后拓展了解:值传递(Java)和引用传递。
- 值传递:使用两个不同的存储单元,执行中,形式参数值改变不会影响实际参数值。
- 引用传递:实际为引用地址,实际参数和形式参数指向同一地址,执行中形式参数会影响实际参数。
方法的重载(Overloading)
-
重载就是在一个类中,有相同的函数名称,但形参不同的函数。
-
方法重载的规则:
- 方法名称必须相同。
- 参数列表必须不同(个数不同、或类型不同、参数排列顺序不同等)。
- 方法的返回类型可以相同也可以不相同。
- 仅仅返回类型不同不足以成为方法的重载。
-
实现理论:
- 方法名称相同时,编译器会根据调用方法的参数个数,参数类型等去逐个匹配,以选择对应的方法,如果匹配失败,则编译器报错。
public static void main(String[] args) { max(11,11,11); } //二整数比大小 public static int max(int max1,int max2){ int result = 0; if (max1==max2){ System.out.println("两值相等"); return 0; }if (max1>max2){ result = max1; System.out.println(result); }if (max2>max1){ result = max2; System.out.println(result); }return result; } //二小数比大小 public static double max(double max1,double max2){ double result = 0; if (max1==max2){ System.out.println("两值相等"); return 0; }if (max1>max2){ result = max1; System.out.println(result); }if (max2>max1){ result = max2; System.out.println(result); }return result; } //三小数比大小 public static double max(double max1,double max2,double max3){ double result = 0; if (max1==max2 && max2==max3){ System.out.println("两值相等"); return 0; }if (max1>max2 && max1>=max3){ result = max1; System.out.println(result); } else if (max1>max3 && max1>=max2){ result = max1; System.out.println(result); } else if (max2>max1 && max2>=max3){ result = max2; System.out.println(result); }else if (max2>max3 && max2>=max1){ result = max2; System.out.println(result); }else if (max3>max1 && max3>=max2){ result = max3; System.out.println(result); }else if (max3>max2 && max3>=max1){ result = max3; System.out.println(result); }return result; }
重载与重写的区别
重写(Overriding) | 重载(Overloading) | |
---|---|---|
类的数量 | 父子类、接口与实现类 | 本类 |
方法名称 | 一致 | 一致 |
参数列表 | 一定不能修改 | 必须修改 |
返回类型 | 一定不能修改 | 可以修改 |
异常 | 可以减少或删除,但不能扩展 | 可以修改 |
重载发生在本类,方法名相同,参数列表不同,与返回值无关,只和方法名,参数列表,参数的类型有关.
重载(Overload):首先是位于一个类之中或者其子类中,具有相同的方法名,但是方法的参数不同,返回值类型可以相同也可以不同。
(1):方法名必须相同
(2):方法的参数列表一定不一样。
(3):访问修饰符和返回值类型可以相同也可以不同。
其实简单而言:重载就是对于不同的情况写不同的方法。 比如,同一个类中,写不同的构造函数用于初始化不同的参数。
重写(Override)发生在父类子类之间,比如所有类都是继承与Object类的,Object类中本身就有equals,hashcode,toString
方法等.在任意子类中定义了重名和同样的参数列表就构成方法重写.
重写(override):一般都是表示子类和父类之间的关系,其主要的特征是:方法名相同,参数相同,但是具体的实现不同。
重写的特征:
(1):方法名必须相同,返回值类型必须相同
(2):参数列表必须相同
(3):访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为public,那么在子类中重写该方法就不能声明为protected。
(4):子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为private和final的方法。
(5):构造方法不能被重写,
简单而言:就是具体的实现类对于父类的该方法实现不满意,需要自己在写一个满足于自己要求的方法。
命令行传参
-
有时候你希望运行一个程序时候再传递给他消息。这要靠传递命令行参数给main()函数实现。
public static void main(String args[]){ for(int i=0;i<args.length;i++){ System.out.println("args["+i+"]:"+agrs[i]) } }
package com;
import java.util.Scanner;
public class Demo01 {
public static void main(String[] args) {
//创建一个扫描器对象
Scanner scanner = new Scanner(System.in);
System.out.println("使用next方式接收");
//判断用户有没有输入字符串
if(scanner.hasNext()){ //使用hasNext()会接收一行 "hello word"
String str = scanner.next(); //使用next方式接收
System.out.println("输入的内容为:"+str);
//input: hello word
//输入的内容为:hello
}
//凡是属于IO流的类如果不关闭会一直占用资源
scanner.close();
}
}
IDEA命令行:
Doc命令行:
找到当前类的文件夹,打开cmd.
可变参数
- JDK 1.5 开始,Java支持传递同类型的可变参数给一个方法。
- 在方法声明中,在指定参数类型后加一个省略号(…)。
- 一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明
public static void main(String[] args) {
printMax(34,3,3,2,56.5);
printMax(new double[]{1,2,3});
}
public static void printMax( double... numbers){
if (numbers.length ==0){
System.out.println("No argument passed");
return;
}
double result = numbers[0];
//排序
for (int i = 1; i < numbers.length;i++){
if (numbers[i] > result){
result = numbers[i];
}
}
System.out.println("The max value is " + result);
}
递归
-
A 方法调用 B 方法,我们很容易理解!
-
递归就是:A 方法调用 A 方法!就是自己调用自己
-
利用递归可以用简单的程序来解决一些复杂的问题。它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可貌似出解决过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。
-
递归结构包括两个部分:
- 递归头:什么时候不调用自身方法。如果没有头,将陷入死循环。
- 递归体:什么时候需要调用自身方法。
-
递归适合小计算,如果太大量计算容易内存崩溃,死机。
public static void main(String[] args){ System.out.println(f(5)); } public static int f(int n){ if (n==1){ return 1; }else { return n*f(n-1); } }
数组
- 数组概述
- 数组声明创建
- 数组使用
- 多维数组
- Arrays 类
- 稀疏数组
数组的定义
- 数组是相同类型数据的有序集合。
- 数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成。
- 其中,每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问它们。
数组声明创建
-
首先必须声明数组变量,才能在程序中使用数组。下面是声明数组变量的语法:
dataType[] arrayRefVar; // 首选的方法 或 dataType arrayRefVar[]; // 效果相同,但不是首选方法
-
Java 语言使用 new 操作符来创建数组,语法如下:
ataType[] arrayRefVar = new dataType[arraySize];
-
数组的元素是通过索引访问的,数组索引从0开始
-
获取数组长度:
arrays.length
练习:
public static void main(String[] args){
int[] nums; //1.首选声明一个数组
nums = new int[10]; //2.创建一个数组
//3.给数组元素中赋值
nums[0] = 1;
nums[1] = 2;
nums[2] = 3;
nums[3] = 4;
nums[4] = 5;
nums[5] = 6;
nums[6] = 7;
nums[7] = 8;
nums[8] = 9;
nums[9] = 10;
//计算所有元素的和
int sum = 0;
//获取数组长度:arrays.length
for (int i = 0; i < nums.length ; i++){
sum = sum + nums[i];
}
System.out.println("总和:"+sum);
}
内存分析
-
Java内存分析:
数组的三种初始化
-
静态初始化
//静态初始化:创建 + 赋值 int[] a = {1,2,3}; Man[] mans = {new Man(1,1),new Man(2,2)};
-
动态初始化
//动态初始化:包含默认初始化,未赋值前为0。 int[] a = new int[2]; a[0] = 1; a[1] = 2;
- 数组的默认初始化
- 数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方法被隐式初始化。
数组的四个基本特点
-
其长度是确定的。数组一旦被创建,它的大小就是不可以改变的。
-
其元素必须是相同类型,不允许出现混合类型。
-
数组中的元素可以是任何数据类型,包括基本类型和引用类型。
-
数组变量属引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量。
数组本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中的。
数组边界
-
下标的合法区间:[0,length-1],如果越界就会报错;
public static void main(String[] args){ int[] a=new int[2]; //a长度为2,下标表示为a[0],a[1]. System.out.println(a[2]);//a[2]以超出设定值 }
-
ArraylndexOutOfBoundsException
:数组下标越界异常!
- 小结:
- 数组是相同数据类型(数据类型可以为任意类型)的有序集合
- 数组也是对象。数组元素相当于对象的成员变量
- **数组长度的确定的,不可变的。如果越界,则报:
ArrayindexOutofBounds**
数组的使用
- 普通的For循环
- For-Each 循环
- 数组作方法入参
- 数组作返回值
普通的For循环
public static void main(String[] args) {
int[] arrays = {11,12,13,14,15};
//打印全部的数组元素
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="+sum);
System.out.println("=================");
//查找数组内最大元素
int max = arrays[0] ;
for (int i = 1; i < arrays.length; i++) {
if (max < arrays[i]){
max = arrays[i];
}
}
System.out.println("max="+max);
}
For-Each (增强型的For循环)方法
public static void main(String[] args) {
int[] arrays ={1,2,3,4,5};
//打印全部的数组元素 JDK1.5 没有下标(取不到下标)
//用arrays(数组).for,调用,相当与遍历数组,适合打印输出
//(int array : arrays)后面的arrays相当于数组,前面的的变量int array是每个元素的值
for (int array : arrays) {
System.out.print(array+" ");
}
System.out.println();
System.out.println("打印数组");
printArray(arrays);
System.out.println("反转数组:");
int[] reverse= reverse(arrays);
printArray(reverse);
}
//反转数组
public static int[] reverse(int[] arrays){
int[] reverse =new int[arrays.length];
//反转的操作
for (int i = 0, j=reverse.length-1; i <arrays.length; i++,j--) {
reverse[j] = arrays[i];
}
return reverse;
}
//打印数组元素
public static void printArray(int[] arrays){
for (int i = 0; i <arrays.length ; i++) {
System.out.println(arrays[i]+" ");
}
}
多维数组
-
多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组。
-
二维数组
int a[][] =new int[2][5];
-
解析:以上二维数组 a 可以看成一个两行五列的数组。
-
思考:多维数组的使用?
num[1][0];
int[][] array = {{1,2},{3,4},{5,6}}; //打印二维数组所有元素 for (int i = 0; i < array.length; i++) { //arrays.length=3 for (int j = 0; j < array[i].length; j++) { System.out.print(array[i][j]+" "); } System.out.println(); }
Arrays 类
-
数组的工具类java.uti.Arrays
-
由于数组对象本身并没有什么方法可以供我们调用,但是
API
中提供了一个工具类 Arrays 供我们使用,从而可以对数据对象进行一些基本的操作。 -
查看
JDK
帮助文档 -
Arrays 类中的方法都是 static 修饰的静态方法,在使用的时候可以直接使用类名进行调用,而“不用”使用对象来调用(注意:是“不用”而不是“不能”)
-
具有以下常用功能:
- 给数组赋值:通过 fill 方法。
- 对数组排序:通过 sort 方法,按升序。
- 比较数据:通过 equals 方法比较数组中元素值是否相等。
- 查找数组元素:通过
binarySearch
方法能对排序好的数组进行二分查找法操作。
public static void main(String[] args) { int[] a = {3,54,8,2,9,6,57,12,87}; //打印数组元素,Arrays.toString System.out.println(Arrays.toString(a)); //数组排序操作 Arrays.sort(a); System.out.println(Arrays.toString(a)); //数组填充 Arrays.fill(a,7); System.out.println(Arrays.toString(a)); //选择填充 Arrays.fill(a,2,6,0); System.out.println(Arrays.toString(a)); }
冒泡排序
- 冒泡排序无疑是最为出名的排序算法之一,总共有八大排序!
- 冒泡的代码还是相当简单的,两层循环,外层冒泡轮数,里层依次比较,江湖中人人尽皆知。
- 我们看到嵌套循环,应该立马就可以得出这个算法的时间复杂度为o(n2)。
//冒泡排序
//1.比较数组中两个相邻的元素,如果第一个数大于第二个数,交换它们位置
//2.每一次比较,都会产生一个最大或最小的数字(升序为最大数)
//3.下一轮则可以少一次排序
//4.依次循环,直到结束
public static void main(String[] args) {
int[] a={8,1,35,47,19,-2};
int[] sort = sort(a);
System.out.println(Arrays.toString(sort)); //[-2, 1, 8, 19, 35, 47]
}
public static int[] sort(int[] array){
int temp=0;//可将temp 定义在if语句内,减少内存
//外层循环,次数length-1
for (int i = 0; i < array.length-1; i++) {
//内层循环:如果第一个数大于第二个数,交换它们位置
for (int j = 0; j < array.length-1-i; j++) {
if(array[j]>array[j+1]){
temp=array[j];
array[j]=array[j+1];
array[j+1]=temp;
}
}
//可优化为:
// for (int j = i+1; j < array.length; j++) {
// if(array[i]>array[j]){
// int temp=array[i];
// array[i]=array[j];
// array[j]=temp;
// flag = true;
// }
}
return array;
}
稀疏数组
- 需求:编写五子棋游戏中,有存盘退出或续上盘的功能。
- 分析问题:因为该二维数组的很多值是默认值0,因此记录了很多没有意义的数据。
- 解决:稀疏数组
稀疏数组介绍
- 当一个数组中大部分元素为0,或者为同一值得数组时,可以使用稀疏数组来保存该数组。
- 稀疏数组的处理方式是:
- 记录数组一共有几行几列,有多少个不同值。
- 把具有不同值的元素和行列及值记录在一个小规模的数组中,从而缩小程序的规模
import java.util.Arrays;
public class Demo01 {
public static void main(String args[]) {
int[][] array1 = new int[11][11];//0代表空,1代表黑子,2代表白子
array1[1][2] = 1;//给黑子定位
array1[2][3] = 2;//给白子定位
array1[3][3] = 2;//给白子定位
//输出原始数据
System.out.println("原始数据:");
for (int[] ints : array1) {
for (int anInt : ints) {
System.out.print(anInt+" ");
}
System.out.println();
}
System.out.println("======================");
//转换为稀疏数组保存
//1.求和有效值的个数
int sum = 0;
for (int i = 0; i < array1.length; i++) {
for (int j = 0; j < array1.length; j++) {
if (array1[i][j]!=0){
sum++;//array[][] 有值的话才会sum++;
}
}
}
System.out.println("稀疏数组:");
//2.创建一个稀疏数组的数组
int[][] array2 = new int [sum+1][3];//确定第一条稀疏数组的数据
array2[0][0]= array1.length; // 行
array2[0][1]= array1.length; // 列
array2[0][2]= sum;
//3.将非零的值,存放在稀疏数组中
int count = 0;
for (int i = 0; i <array1.length ; i++) {
for (int j = 0; j <array1.length ; j++) {
if (array1[i][j] != 0){
count++; //array[][] 里面有值的话才会count++;
array2[count][0] = i; //获取到值得行数,并赋给array2的第一列
array2[count][1] = j; //获取到值得列数,并赋给array2的第二列
array2[count][2] = array1[i][j]; //将arry1的值赋给,array2第三列
//一次累加
}
}
}
System.out.println("序 "+" 行 "+" 列 "+" 值 ");//打印数组
int xu = 0;
for (int[] ints : array2) {
System.out.print(xu+" ");
for (int anInt : ints) {
if (anInt<array1.length-1){System.out.print(" "+anInt+" ");}
else { System.out.print(" "+anInt+" ");}
}
xu++;
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][1]];//确认原始数组整体大小
for (int i = 1; i <array2.length ; i++) {
array3[array2[i][0]][array2[i][1]]=array2[i][2];//将数组的数据赋值
}
for (int[] ints : array3) {
for (int anInt : ints) {
System.out.print(anInt+" ");
}
System.out.println();
}
}
}
面向对象编程
Java的核心思想就是oop
目录:
- 初始面向对象
- 方法回顾和加深
- 对象的创建分析
- 面向对象三大特性
- 抽象类和接口
- 内部类及OOP实战
初识面向对象
面向过程&面向对象
-
面向过程思想
- 步骤清晰简单,第一步做什么,第二部做什么…
- 面向过程适合处理一些较为简单的问题
-
面向对象思想
- 物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索。
- 面向对象适合处理复杂的问题,适合处理需要多人协作的问题!
-
对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。
什么是面向对象
-
面向对象编程(Object-Oriented Programming, OOP)
-
本质:以类的方式组织代码,以对象的组织(封装)数据。
-
抽象
-
三大特性
- 封装
- 继承
- 多态
-
从认识论的角度考虑是先有对象后有类。对象是具体的事物,类是对象的抽象。
-
从代码运行角度考虑是先有类后有对象。类是对象的模板。
回顾方法及加深
往期java
方法详解 (ps
:按ctrl
键点击跳转)
-
方法的定义
-
修饰符
-
返回类型
//main方法 public static void main(String[] args) { } /* 修饰符 返回值类型 方法名(...){ //方法体 return 返回值; //ruturn也代表着方法的结束 } */ public String sayHellp(){ return "hello,world"; } public int max(int a ,int b ){ return a>b ? a : b ;//三元运算符! }
-
break 和 return 的区别(break:跳出switch,结束循环。return:结束循环,返回值)
-
方法名(注意规范,见名知意)
-
参数列表(参数类型,参数名)
-
异常抛出
-
-
方法的调用:递归
静态方法
可以通过类直接调用,在类加载时加载,不能调用非静态方法
public class Demo01Static {
public static void main(String[] args) {
Student.say();
}
}
public class Student {
public static void say(){
System.out.println("学生说话了");
}
}
非静态方法
通过实例化对象调用
/*静态方法 stutic
输出:类名+方法名
*/
/*非静态方法
1.实例化这个类 new+类名+.方法名
2.对象类型 对象名 = 对象值;
类名+.方法名
*/
public static void main(String[] args) {
Student student = new Student();
student.say();
}
public class Student {
public void say(){
System.out.println("学生说话了");
}
}
public static void a(){
b()//不可以调b
}//因为static静态方法是和类一起加载,在创建的时候就已经有了
public void b(){
a();//可以调a
}//而非静态需要类实例化以后(new)才存在
-
形参和实参
值传递和引用传递
引用传递
public class Demo02 { //引用传递:对象,本质还是值传递 public static void main(String[] args) { Perosn perosn = new Perosn(); System.out.println(perosn.name);//null Demo03.change(perosn); System.out.println(perosn.name);//上下没中 } public static void change(Perosn erosn){ //perosn 是一个对象:指向的 ---> Perosn perosn = new Perosn(); // 这是一个具体的人,可以改变属性 erosn.name = "上下没中";//它赋值的是 } } //定义一个perosn类,有一个属性:name class Perosn{ String name;
- this关键字(代表当前这个类)
类与对象的关系
-
类是一种抽象的数据类型,它是对某一类事物整体描述/定义,但并不能代表某一个具体的事物。
- 动物、植物、手机、电脑…
- Person类、Pet类、Cat类等,都是用来描述/定义某一具体的事物应该具备的特点和行为。
-
对象是抽象概念的具体实例,如张三是人的一个具体实例、张三家里的狗旺财就是狗的一个具体实例。
创建与初始化对象
-
使用new来创建对象
-
使用new关键字创建的时候,除了分配内存之外,还会给创建好的对象进行默认的初始化,以及对类中构造器的调用。
一个类里面只有属性和方法。
//类是抽象的,必须用new实例化
//类实例化后会返回一个自己的对象
//返回后的对象就是抽象类的具体实例
-
类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的。并且构造器有以下俩个特点:
-
必须和类的名字相同
-
必须没有返回类型,也不能写void
//一个类即使什么都不写,它也会存在一个方法(无参构造) //显示的定义构造器 String name; int age; //1.使用new关键字,本质是在调用构造器 //2.无参构造器,用来初始化值 public Demo02(){ } //一旦定义了有参构造,无参就必须显示定义 public Demo02(String name){ this.name = name; } //alt + insert 生产构造器
-
构造器
public class Person {
//一个类即使什么都不写,也会存在一个默认的无参构造方法
//显示地定义构造器
String name;
//作用:1. 使用new关键字,本质是在调用构造器
//2. 用来初始化对象的值
public Person(){} //无参构造
//有参构造 3.一旦定义了有参构造,无参就必须显示定义
public Person(String name){
this.name=name;
}
//Alt+insert 快捷键插入构造方法
}
构造器:
- 和类名相同
- 没有返回值
作用:
- 使用new关键字, 本质是在调用构造方法
- 用来初始化对象的值
注意点: 定义有参构造之后如果想使用无参构造,显示的定义一个无参构造
对象的初始化过程(毕老师)
class Fu
{
Fu()
{
super(); //Object
//显示初始化
//构造代码块初始化
System.out.println("fu contructor run");
show();
}
void show()
{
System.out.println("he he");//被覆盖,运行子类show
}
}
class Zi extends Fu
{
int num = 9;
{
System.out.println("contructor code..."+num);
}
num = 10;
Zi() //**1
{
super();//**2
//显示代码块 **4
//构造代码块初始化 **5
System.out.println("zi constructor"+num);//**6
}
void show()//**3
{
System.out.println("show..."+num);
}
}
//运行结果
fu contructor run
show...0
constructor code... 9
zi constructor...10
new一个子类对象先进行:
- 默认初始化
- 构造器
- 走父类(super)
- 显示初始化
- 构造代码块
- 构造函数具体初始化
内存分析
//定义一个宠物类
public class Pet {
public String name; //默认 null
public int age; //默认 0
//无参构造
public void shout(){
System.out.println("叫了一声");
}
}
//应用类,创建调用对象
public class Application {
public static void main(String[] args) {
Pet dog = new Pet();
dog.name = "旺财";
dog.age = 3;
dog.shout();
}
}
- 对象通过引用类型来操作:栈 – – ->堆
近期总结
-
类与对象
类是一个模板、抽象的;对象是一个具体的实例。
-
方法
定义与调用!
-
对应的引用
引用类型:
基本类型(8)
对象是通过引用来操作的:栈—>堆
-
属性:字段Field 成员变量
默认初始化:
数字: 0 0.0
char : u0000
boolean : false
引用 : null
修饰符 属性类型 属性名 = 属性值!
-
对象的创建和使用
- 必须使用 new 关键字创造对象,构造器 Person sxmz = new person();
- 对象的属性 sxmz.name
- 对象的方法 sxmz.sleep()
-
类:
静态的属性 属性
动态的行为 方法
类里只写这两个。
面向对象三大特性
封装
-
该露的露,该藏得藏
- 我们程序设计要追求 “高内聚,低耦合”。高内聚就是将类的内部数据操作细节自己完成,不允许外部干涉;
- 低耦合:尽量暴露少量的方法给外部使用。
-
封装(数据的隐藏)
- 通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏。
-
作用
- 提高程序的安全性,保护数据
- 隐藏代码的实现细节
- 统一接口
- 系统可维护性增加了
-
属性私有,private ; get / set ;
public class Student { //类 private:属性私有 private String name; private int age; private char sex; //提供一些可以操作这个属性的方法 //public 的 get/set 方法 //get 获得这个数据 public String getName(){ return this.name;} /* Student s1 = new Student(); String name = s1.getName(); name = "小白"; System.out.println(name); */ //set 给这个数据设置值 public void setName(String name){ this.name = name;} /* Student s1 = new Student(); s1.setName("小黑"); System.out.println(s1.getName()); */ //快捷键 alt + insert 自动生成构造器
继承
-
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模。
-
extends 的意思是“扩展”。子类是父类的扩展。
-
Java中类只有单继承,没有多继承!一个儿子只有一个爹
-
继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等。
-
继承关系的俩个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends 来表示。
-
子类和父类之间,从意义上讲应该具有“is a”的关系。
//学生类(子类)继承 人类(父类)
class Application{
Student student = new Student();
}
public class Student extends Person{ /*Person extends Object*/
public Student(){
super();
System.out.println("Student无参执行了0");
}
}
public class Person{
public Person(){
System.out.println("Person无参执行了0");
}
}
输出的话是:
Person无参执行了0
Student无参执行了0
- 子类继承了父类,就会拥有父类的全部方法,而private私有属性及方法无法继承。
- 在Java中,所有类,都默认直接或间接继承Object类 (Ctrl+H 可以查看类关系)
- 被final修饰的类,无法被继承(断子绝孙)。
public //公共的
protected //受保护的
default //默认的
private //私有的,无法被继承
//ctrl+h 继承树
super & this
public static void test(){
super(); //隐藏代码,默认调用了父类的无参构造,要写只能写第一行
}
-
object类
在 Java 中,所有的类,都默认、直接或者间接继承object
-
super—this
this 调用当前类,super 调用父类。
super注意点:
- super()调用父类的构造方法,必须在构造方法的第一个
- super必须只能出现在子类的方法或构造方法中
- super()和this()不能同时调用构造方法,因为this也必须写在第一行
-
super与this的区别:
super代表父类对象的引用,只能在继承条件下使用;
this调用自身对象,没有继承也可以使用。
super Vs this:
代表的对象不同:
this:**本身调用者这个对象**
super:**代表父类对象的应用**
前提:
this:**没有继承也可以使用**
super:**只能在继承条件才可以使用**
构造方法
this():本类的构造
super():父类的构造
方法的重写 (Override)
重写都是方法的重写,和属性无关。
重写值和非静态方法有关,静态static没用,只能 Public 。
需要有继承关系,子类重写父类的方法!
- 方法名必须相同
- 参数列表必须相同
- 修饰符:范围可以扩大,但不能缩小;public > protected > Default > private
- 抛出的异常: 范围,可以被缩小但不能扩大;ClassNotFoundException —>Exception(大)
重写,子类的方法和父类必须一致,但方法体不同!
为什么需要重写:
-
父类的功能,子类不一定需要,或者不一定满足!
Alt + Insert : override;
静态的方法和非静态的方法区别很大
多态
父类的引用可以指向子类,但不能调用子类独有的方法。
方法的调用只和左边定义的数据类型有关,和右边关系不大。
动态编译:类型:可扩展性更强
- 即同一方法可以根据发送对象的不同而采用多种不同的行为方式。
- 一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多(父类,有关系的类)
- 多态存在的条件
- 有继承关系
- 子类重写父类方法
- 父类引用指向子类对象
//子类能调用的方法都是自己的或继承父类的
Student s1 = new Student();//子类
//父类可以指向子类,但是不能调用子类独有的方法
Person s2 = new Student();//父类
Object s3 = new Student();
/*一个对象的实际类型是确定的
可指向的引用类型不确定,父类的引用指向子类
*/
//但是子类重写父类方法后,指向子类的方法
s2.run();//父类有,子类没有,子类继承父类方法
s1.run();//子类重写后,执行子类方法
s1.eat();//子类独有方法
((Student) s2).eat();
//父类不能调用子类独有方法,会被强制转换为子类
class Person{
void run(){}
}
class Student extends Person{
void run(){}
void eat(){}
}
-
多态的注意事项:
-
多态是方法的多态,属性没有多态性
-
父类和子类,有联系才能转换,不然会异常!类型转换异常:
ClassCastException
-
存在条件:继承关系,方法需要重写,父类引用指向子类对象!
Father f1 = new son();
不能重写的方法:
- static 方法,属于类,它不属于实例
- final 常量 ,被final修饰的无法修改,属于常量池
- private 私有方法,不能被重写
-
instanceof
和类型转换
instanceof
-
“instanceof 判断一个对象是什么类型。(类型转换—引用类型之间的转换)`
-
instanceof
引用类型比较,判断一个对象是什么类型System.out.println(x instanceof y); :true or false
(能不能编译通过,看x所指向的实际类型是不是y的子类型,是不是父子关系)public static void main(String[] args) { //比较 X instanceof Y 之间是否存在父子关系 //Object>String //Object>Person(父类)>Teacher(另一子类) //Object>Person(父类)>Student(子类) 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 //Student类在Object类之内存在的关系, // Student,Person,Object,Teacher,String都属于Object类内 //Teacher和String与Student不存在父子关系所以显示false System.out.println("==================================="); 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类在Peron类范围内存在的关系; // 由于String在Peron类之外,没有直接联系,所以提示错误 System.out.println("==================================="); 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);编译错误 //Student类在Student类范围内存在的关系, //由于String和Teacher在Student类之外,所以提示错误 }
转换的概念:
1. 父类引用指向子类的对象,不可以子类引用指向父类。
2. 把子类转换为父类,向上转型;
3. 把父类转换为子类,向下转型,强制转换(可能会丢失方法)
4. 方便方法的调用,减少重复的代码,简洁。
//类型之间的转化 : 父---子
//高 低
Person s1 = new Student();
//高转低可以直接转;低转高,需要强制转
//
Student s2 = (Student) s1;
s2.go();
//或((Student) s1).go();
static关键字详解
静态方法在类的时候就已经加载了
Static加上方式上叫静态方式,加上属性上叫静态属性。
静态属性
//静态属性
public class Student {
private static int age;//静态的变量,可以被类中共享,多线程比较常用
private double score;//非静态变量
public static void main(String[] args) {
Student s1 = new Student();
System.out.println(s1.age);//通过方法可以正常调用
System.out.println(s1.score);
System.out.println(Student.age);//静态变量,可以直接用类名进行调用
//System.out.println(Student.score);//非静态变量不可以。
}
}
静态方法
//静态方法
public class Student {
public void run(){
Student.go();
}
public static void go(){
}
public static void main(String[] args) {
Student.go();//静态方法不需要 new 可以直接调用
//由于静态方法在类生成的时候就已经存在,所以可以调用静态的
//Student.run(); 但不能调用非静态的,非静态方法需要 new 出来
new Student().run();
}//注解和反射
}
代码块
{
//代码块(匿名代码块)
}//创建对象的时候就已经创建了,在构造器之前
static {
//静态代码块
}//在类一加载就已经执行,而且只加载一次,也在构造器之前
排序,static只执行一次
public class Demo03 {
//第二加载;适用于赋初值
{
System.out.println("匿名代码块");
}
//第一加载;只在第一次执行
static{
System.out.println("静态代码块");
}
//第三加载
public Demo03() {
System.out.println("构造方法");
}
public static void main(String[] args) {
Demo03 s1 = new Demo03();
System.out.println("==========");
Demo03 s2 = new Demo03();
//第二次执行static不在执行
}
}
输出:
静态代码快
匿名代码块
构造方法
===========
匿名代码块 //第二次执行static不在执行
构造方法
静态导入包
静态导入包后可以直接调用其方法;
//静态导入包
import static java.lang.Math.random;
import static java.lang.Math.PI;
public static void main(String[] args) {
System.out.println((int)(Math.random()*50));
//random()随机值,整数,范围(0-50)
//使用静态导入包后可以直接System.out.println(random());
System.out.println(PI);
}
}
final修饰的类不能被继承,final之后就段子绝孙了
public static class Demo03 {}//Demo3不能被继承了
抽象类
- abstract 修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那么该类就是抽象类。
- 抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类。
- 抽象类,不能使用 new 关键字来创建对象,它是用来让子类继承的。
- 抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的。
- 子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法;否者该子类也要声明为抽象类,然后由子子类实现抽象方法。
“Abstract method in non-abstract class”
//abstract 抽象类 类 extends,单继承; (接口可以多继承)
public abstract class Action {
//约束~有人帮我们实现
//abstract,抽象方法,只有方法名字,没有方法的实现!
public abstract void doSomething();
}
//抽象类的所有方法,必继承了它的子类,都必须要实现它的方法
public class A extends Action {
//除非子类也是抽象方法,那就由子子类实现
@Override
public void doSomething() {
}
}
- 不能 new 抽象类,只能靠子类去实现它;只是约束
- 抽象类中可以写普通方法
- 抽象方法必须在抽象类中
报错“Missing method body, or declare abstract”
abstract class Demo
{
abstract /*抽象*/ void show();
}
class DemoA extends Demo
{
void show()
{
System.out.println("demoashow");
}
}
class DemoB extends Demo
{
void show()
{
System.out.println("demobshow");
}
}
思考题:
-
抽象类存在构造器吗?.
抽象类可以有构造方法,只是不能直接创建抽象类的实例对象而已。在继承了抽象类的子类中通过super()或super(参数列表)调用抽象类中的构造方法。
-
抽象类存在的意义
-
抽象类可以不定义抽象方法吗?
可以的。 但是很少见,目的就是不让该类创建对象。AWT的适配器对象就是这种类。
通常这个类中的方法有方法体,但是却没有内容。
-
抽象关键字不可以和那些关键字共存?
private 不行原因:
抽象方法是不是要被覆盖,你这一私有子类就不知道,就不能覆盖
static 不行
如果成员变静态,就不需要对象,抽象类正好不用创建对象吗,我不需要对象你干吗要创建啊,我直接类名就调用这个方法了,抽象方法运行有意义吗,没有意义,连方法体都没有,做什么运算都不知道,所以也不能共存
final 不行
final在继承中又是不是覆盖的,然儿也定义抽象方法就又是用来覆盖的
-
抽象类和一般类的异同点。
相同点:
抽象类和一般类都是用来描述事物的,都在内部定了成员。
不同:
1,一般类有足够的信息描述事物。
抽象类描述事物的信息有可能不足。
2,一般类中不能定义抽象方法,只能定非抽象方法。
抽象类中可定义抽象方法,同时也可以定义非抽象方法。
3,一般类可以被实例化。
抽象类不可以被实例化。
-
抽象类一定是个父类吗?
是的。因为需要子类覆盖其方法后才可以对子类实例化。
接口
-
普通类:只有具体实现
-
抽象类:具体实现和规范(抽象方法)都有!
-
接口:只有规范!自己无法写方法,专业的约束!约束和实现分离:面向接口编程~
-
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是…则必须能…”的思想。
“如果你是天使,则必须能飞。如果你是汽车,则必须能跑。”
-
接口的本质是契约,就像我们人间的法律一样。制定好后大家都遵守。
-
OO
的精髓,是对对象的抽象,最能体现这一点的就是接口,为什么我们讨论设计模式都只针对具备了抽象能力的语言(比如c++、java
、c#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。声明类的关键字是class,声明接口的关键字是interface
//interface 接口定义的关键字;接口都需要实现类
public interface UserSerbice {
// public void ss(){ } 报错;接口内不能写方法
//接口中的所有定义其实都是抽象的,默认 public abstract
public abstract void run();
void add(String name);
void delete(String name);
//接口中定义的所有属性,都是常量
//接口还可以定义变量,所有定义的属性都是静态的常量
public static final int AGE = 99;
int ABC = 99;
}
作用:
- 约束,规范
- 定义一些方法,让不同的人实现。多个人完成共同的工作。
- 接口中所有默认的方法public abstract
- 所有常量默认public static final (接口中定义的所有属性,都是常量)
- 接口不能被实例化,接口中没有构造方法。
- 可以实现多个接口
- 必须要重写接口中的方法。
- 声明接口interface,实现接口implements,可以实现多个方法
练习而已:
public class Application {
public static void main(String[] args) {
xiaduan sxmz = new xiaduan();
sxmz.mingzi("上下没中");
sxmz.nianling(31);
sxmz.shengao(175.8);
sxmz.zhuzhi("内蒙古");
}
}
/*
//抽象类:extende
//类 可以实现接口 implements 接口
//实现了接口的类,就需要重写接口中的方法
public class xiaduan implements jiekou,jiekou2 {
//利用接口实现多继承(jiekou,jiekou2)
//继承类可以方法的实现,但接口只有方法的定义
@Override//jiekou
public void mingzi(String name) {
System.out.println("名字:"+name);
}
@Override//jiekou
public void nianling(int nl) {
System.out.println("岁数:"+nl);
}
@Override//jiekou
public void shengao(double sg) {
System.out.println("身高:"+sg);
}
@Override//jiekou2
public void zhuzhi(String zz) {
System.out.println("住宅:"+zz);
}
}
*/
抽象与接口的区别
抽象类和接口的异同点:
相同点:
都是不断向上抽取而来的。
不同点:
-
抽象类需要被继承,而且只能单继承。
接口需要被实现,而且可以多实现。
-
抽象类中可以定义抽象方法和非抽象方法,子类继承后,可以直接使用非抽象方法。
接口中只能定义抽象方法,必须由子类去实现。
-
抽象类的继承,是is a关系,在定义该体系的基本共性内容。
Is a 就是所属关系,什么是什么的一种,
接口的实现是like a 关系,在定义体系额外功能。
就是举个例子,学员啊学员有学习功能吧,没有抽烟功能,学习是学员的基本功能吧,抽烟不是,抽烟是不是学员的额外功能啊,那些学员抽烟就去实现这个抽烟接口呗
//在不同的问题领域中,有不同的分析方式。
//犬按功能分:有导盲犬,搜爆犬。
abstract class 犬
{
abstract void 吼叫();
}
//abstract class 导盲
interface 导盲
{
abstract void 导盲();
}
class 导盲犬 extends 犬 implements 导盲
{
public void 吼叫(){}
public void 导盲(){}
}
面对对象:接口的应用:
广意的来说,对外提供的我们就称为接口
例子还是以笔记本为例子讲
/*
笔记本电脑使用。
为了扩展笔记本的功能,但日后出现什么功能设备不知道。
定义一个规则,只要日后出现的设备都符合这个规则就可以了。
规则在java中就是接口。
*/
interface USB// 暴露的规则。
{
public void open();
public void close();
}
class BookPC
{
public static void main(String[] args)
{
useUSB(new UPan());//功能扩展了。
useUSB(new UsbMouse());
}
//使用规则。
public static void useUSB(USB u)//接口类型的引用,用于接收(指向)接口的子类对象。//USB u=new UPan();
{
{
if(u!=null)
{
u.open();
u.close();
}
}
}
//一年后。------------------------------
//实现规则。
//这些设备和电脑的耦合性降低了。
class UPan implements USB
{
public void open()
{
System.out.println("upan open");
}
public void close()
{
System.out.println("upan close");
}
}
class UsbMouse implements USB
{
public void open()
{
System.out.println("UsbMouse open");
}
public void close()
{
System.out.println("UsbMouse close");
}
}
class Mouse{}
class BookPC
{
public static void main(String[] args)
{
useMouse(new Mouse());
}
public static void useMOuse(Mouse m)
{
m.open();
}
public static void useMOuse(Mouse m)
{
m.open();
}
}
内部类(扩展知识)
内部类的定义:将一个类定义在另一个类的里面 对里面的那个类就称为内部类(内置类、嵌套类),内部类也是一种封装。
访问特点:
-
内部类可以直接访问外部类中的成员 包括私有成员
之所以可以直接访问外部类中的成员,是因为内部类中持有了一个外部类的引用,格式 : 外部类 名.this
-
而外部类要访问内部类中的成员 必须要建立内部类的对象
/*
内部类的访问规则:
1,内部类可以直接访问外部类中的成员,包括私有。
之所以可以直接访问外部类中的成员,是因为内部类中持有了一个外部类的引用,格式 外部类名.this
2,外部类要访问内部类,必须建立内部类对象。
*/
class Outer
{
private int x = 3;
class Inner//内部类
{
//int x = 4;
void function()
{
//int x = 6;
System.out.println("innner :"+Outer.this.x);
}
}
/**/
void method()
{
Inner in = new Inner();
in.function();
}
}
class InnerClassDemo
{
public static void main(String[] args)
{
Outer out = new Outer();
out.method();
// 直接访问内部类中的成员。
Outer.Inner in = new Outer().new Inner();
in.function();
}
}
一般用于类的设计
分析事物时 发现该事物的描述中还有事物 而且这个事物还在访问被描述事物的内容 这时就把还有的事物定义成内部类来描述
如果内部类是静态的 相当于一个外部类
如果内部类中定义了静态成员 该内部类也必须是静态的
为什么内部类能直接访问外部类中的成员呢?
那是因为内部类持有了外部类的引用 外部类名.this
内部类可以存放在局部位置上
内部类在局部位置上 只能访问局部中被final修饰的局部变量
/*
内部类的访问规则:
1,内部类可以直接访问外部类中的成员,包括私有。
之所以可以直接访问外部类中的成员,是因为内部类中持有了一个外部类的引用,格式 外部类名.this
2,外部类要访问内部类,必须建立内部类对象。
访问格式:
1,当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类中。
可以直接建立内部类对象。
格式
外部类名.内部类名 变量名 = 外部类对象.内部类对象;
Outer.Inner in = new Outer().new Inner();
2,当内部类在成员位置上,就可以被成员修饰符所修饰。
比如,private:将内部类在外部类中进行封装。
static:内部类就具备static的特性。
当内部类被static修饰后,只能直接访问外部类中的static成员。出现了访问局限。
在外部其他类中,如何直接访问static内部类的非静态成员呢?
new Outer.Inner().function();
在外部其他类中,如何直接访问static内部类的静态成员呢?
uter.Inner.function();
注意:当内部类中定义了静态成员,该内部类必须是static的。
当外部类中的静态方法访问内部类时,内部类也必须是static的。
当描述事物时,事物的内部还有事物,该事物用内部类来描述。
因为内部事务在使用外部事物的内容。
class Body
{
private class XinZang
{
}
public void show()
{
new XinZang().
}
}
*/
class Outer
{
private static int x = 3;
static class Inner//静态内部类
{
static void function()
{
System.out.println("innner :"+x);
}
}
static class Inner2
{
void show()
{
System.out.println("inner2 show");
}
}
public static void method()
{
//Inner.function();
new Inner2().show();
}
}
class InnerClassDemo2
{
public static void main(String[] args)
{
Outer.method();
//Outer.Inner.function();
//new Outer.Inner().function();
//直接访问内部类中的成员。
// Outer.Inner in = new Outer().new Inner();
// in.function();
}
}
}
局部内部类
内部类定义在局部时,
- 不可以被成员修饰符修饰
- 可以直接访问外部类中的成员,因为还持有外部类中的引用。
但是不可以访问它所在的局部中的变量。只能访问被final修饰的局部变量。
匿名内部类
-
匿名内部类:就是内部类的简写格式
-
使用定义匿名内部类必须有前提:内部类必须继承或者实现一个外部类或者接口
所以匿名内部类:其实就是一个匿名子类对象
-
匿名内部类的格式: new 父类或者接口()
-
其实匿名内部类就是一个匿名子类对象。而且这个对象有点胖。 可以理解为带内容的对象。
-
匿名内部类中定义的方法最好不要超过3个。
通常的使用场景之一:
当函数参数是接口类型时 而且接口中的方法不超过三个 可以用匿名内部类作为实际参数进行传递
因为匿名内部类这个子类对象被向上转型为了Object类型 这样就不能再使用子类的特有方法了
格式:new 父类or接口(){子类内容}
- 内部类就是在一个类的内部在定义一个类,比如A类中定义一个B类,那么B累相对A类来说就称为内部类,而A类相对B类来说就是外部类了。
public class Outer {//外部类
private int id = 10;
public void out(){
System.out.println("这是外部类的方法!");
}
public class Inner{//内部类
public void in(){
System.out.println("这是内部类的方法!");
}
}
//内部类可以获得外部类的私有属性
public void getID(){
System.out.println(id);
}
}
public static void main(String[] args) {
//外部类通过 new 获取
Outer outer = new Outer();
//内部类通过 外部类 . new 内部类 获得
Outer.Inner inner = outer.new Inner();
}
-
成员内部类;类中加类
//先实例化外部类,再用外部类实例化内部类 Outer outer = new Outer();//new外部 Outer.Inner inner = outer.new Inner();//new内部 inner.in();
- 内部类可以获得外部类的私有属性
- 静态内部类;不能直接访问非静态的外部类属性(因为static先于非静态类生成)
- 局部内部类;
public void method(){ class inner{ public void in(){ } } }
-
匿名内部类;没有名字去初始化类,不用将实例保存到变量中
//不起名直接使用 new Apple().eat(); //new接口 class Test{ public static void main(String[] args) { new UserService(){ }; } } interface UserService{ }
一个java类中,可以有多个 class 类,但只能有一个 public class 类
//面试
**//1**
new Object(){
void show(){
System.out.println("show run");
}
}.show();
**//2**
Object obj = new Object(){
void show(){
System.out.println("show run");
}
};
obj.show();
1和2的写法正确吗?有区别吗?说出原因。
写法是正确,1和2都是在通过匿名内部类建立一个Object类的子类对象。
区别:
第一个可是编译通过,并运行。
第二个编译失败,因为匿名内部类是一个子类对象,当用Object的obj引用指向时,就被提升为了Object类型,而编译时检查Object类中是否有show方法,所以编译失败。编译看左边
—————————————————————————————————————————————————————
class InnerClassDemo6 {
static class Inner{
void show(){}
}
public void method(){
this.new Inner().show();//可以
}
public static void main(String[] args) {//static不允许this
this.new Inner().show();//错误,Inner类需要定义成static
}
}
——————————————————————————————————————————————————————
interface Inter{
void show();
}
class Outer{//通过匿名内部类补足Outer类中的代码。
public static Inter method(){
return new Inter(){
public void show(){}
};
}
}
class InnerClassDemo7 {
public static void main(String[] args) {
Outer.method().show();
}
}
Outer.method():意思是:Outer中有一个名称为method的方法,而且这个方法是静态的。
Outer.method().show():当Outer类调用静态的method方法运算结束后的结果又调用了show方法,意味着:method()方法运算完一个是对象,而且这个对象是Inter类型的。
function(
new Inter(){
public void show(){}
}); //匿名内部类作为方法的参数进行传递。
}
public static void function(Inter in){
in.show();
}
}
异常机制
目录:
- 什么是异常
- 异常体系结构
- Java异常处理机制
- 处理异常
- 自定义异常
- 总结
什么是异常
- 实际工作中,遇到的情况不可能是非常完美的。比如:你写的某个模块,用户输入不一定符合你的要求;你的程序要打开某个文件,这个文件可能不存在或者文件格式不对;你要读取数据库的数据,数据可能是空的等;我们的程序再跑着,内存或硬盘可能满了。等等。
- 软件程序在运行过程中,非常可能遇到刚刚提到的这些异常问题,我们叫异常,英文是:Exception,意思就是例外。这些,例外情况,或者叫异常,怎么让我们写的程序做出合理的处理,而不至于程序崩溃。
- 异常值程序运行中出现不期而至的各种状况,如:文件找不到,网络连接失败,非法参数等。
- 异常发现在程序运行期间,它影响了正常的程序执行流程。
简单分类
-
要理解 Java 异常处理是如何工作的,你需要掌握以下三种类型的异常:
-
检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。
例如:打卡一个不存在文件时,一个异常就会发生了,这些异常在编译时不能被简单地忽略。
-
运行时异常:运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
-
错误 ERROR:错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。编译时不容易被发现。
例如:当栈溢出时,一个错误就发生了,它们在编译也检查不到的。
-
-
异常的分类:
-
编译时被检测异常:只要是Exception和其子类都是,除了特殊子类RuntimeException体系。
这种问题一旦出现,希望在编译时就进行检测,让这种问题有对应的处理方式。这样的问题都可以针对性的处理。
-
编译时不检测异常(运行时异常):就是Exception中的RuntimeException和其子类。
这种问题的发生,无法让功能继续,运算无法进行,更多是因为调用者的原因导致的而或者引发了内部状态的改变导致的。
那么这种问题一般不处理,直接编译通过,在运行时,让调用者调用时的程序强制停止,让调用者对代码进行修正。
-
异常有两种:
-
编译时被检测异常
-
该异常在编译时,如果没有处理(没有抛也没有try),编译失败。
-
该异常被标识,代表这可以被处理。
-
-
运行时异常(编译时不检测)
-
在编译时,不需要处理,编译器不检查。
-
该异常的发生,建议不处理,让程序停止。需要对代码进行修正。
-
异常体系结构
- Java 把异常当做对象来处理,并定义一个基础类
java.lang.Throwable
作为所有异常的超类。 - 在 Java
API
中已经定义了许多异常类,这些异常类分为两大类,错误Error和异常Exception 。
Error错误
- Error 类对象有 Java 虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关。
- Java 虚拟机运行错误(Virtual MachineError),当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时, Java 虚拟机(JVM)一般会选择线路终止。
- 还有发生在虚拟机试图执行应用时,如类定义错误(NoClassDefFoundError)、链接错误(LinkageError)。这些错误是不可查的,因为它们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的情况。
Exception异常
- 在 Exception 分支中有一个重要的子类 RuntimeException (运行时异常)
ArrayIndexOutOfBoundsException
(数组下标越界)NullPointerException
(空指针异常)ArithmeticException
(算术异常)MissingResourceException
(丢失资源)ClassNotFoundException
(找不到类)等异常,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。
- 这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发现;
- Error 和 Exception 的区别:Error 通常是灾难性的致命错误,是程序无法控制和处理的,当出现这些异常时,Java 虚拟机(JVM)一般会选择终止线程;Exception 通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这类异常。
异常体系的特点:
异常体系中的所有类以及建立的对象都具备可抛性。
也就是说可以被throw和throws关键字所操作。
只有异常体系具备这个特点。
异常处理机制
-
抛出异常
-
捕获异常
-
异常处理五个关键字
- 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 (Error e) { //catch(想要捕获的异常类型!)捕获异常
System.out.println("Error");
}catch (Exception e){//“e”,代表异常消息
System.out.println("Exception");
}catch (Throwable t){ //最高级,放在最后面
System.out.println("Throwable");
}finally { //处理善后工作,不管报不报异常,都会执行。
System.out.println("finally");
}
//finally 可以不用,但是catch必须有。finally假设Io,资源,关闭工作
//快捷键:选中需要包裹的代码,ctrl+alt+T
/*
try {
System.out.println(a / b);
} catch (Exception e) {
System.exit(0);//程序结束
e.printStackTrace();//打印错误的栈信息
} finally {
}
*/
}
throwable的方法
throws 和throw的用法
区别:
- throw定义在函数内,用于抛出异常对象。
- throws定义在函数上,用于抛出异常类,可以抛出多个用逗号隔开。
注意:
当函数内容有throw抛出异常对象,并未进行try处理。必须要在函数上声明,否则编译失败。
注意,RuntimeException
除外。也就说,函数内如果抛出的RuntimeExcpetion
异常,函数上可以不用声明。
如果函数声明了异常,调用者需要进行处理。处理方法可以throws可以try。
[异常的两种分类:](# 异常有两种:) (ps
:按ctrl
键点击跳转)
-
编译时被检测异常
-
该异常在编译时,如果没有处理(没有抛也没有try),编译失败。
-
该异常被标识,代表这可以被处理。
-
-
运行时异常(编译时不检测)
-
在编译时,不需要处理,编译器不检查。
-
该异常的发生,建议不处理,让程序停止。需要对代码进行修正。
-
throw
try {
if (b==0){
throw new ArithmeticException();//主动的抛出异常
}
throws
public static void main(String[] args) {
try {
new linshi().test(1,0);
} catch (ArithmeticException e) { //算术异常
e.printStackTrace();
} finally {
}
}
//假设这个方法中,处理不了这个异常。方法上抛出异常throws,由上一级捕获。
public void test(int a,int b)throws ArithmeticException{
if (b == 0) {
throw new ArithmeticException();//throw 主动的抛出异常,一般在方法内
}
System.out.println(a / b);
}
try catch finally三种组合
try
{
//需要被检测异常的代码
}
catch(异常类 变量) //该变量用于接收发生的异常对象
{
//处理异常的代码
}
finally
{
//一定会被执行的代码
}
//记住一点:catch是用于处理异常。如果没有catch就代表异常没有被处理过,如果该异常是检测时异常。那么必须声明。
//try finally 异常无法直接catch处理,但资源需要关闭
注意:
1,finally中定义的通常是 关闭资源代码。因为资源必须释放。
2,finally只有一种情况不会执行。当执行到System.exit(0);fianlly不会执行。
第一个格式:
try
{
}
catch ()
{
}
第二个格式:
try
{
}
catch ()
{
}
finally{
}
第三个格式:
try
{
//开启资源。
throw new Exception();
}
finally
{
//关闭资源
}
//记住一点:catch是用于处理异常。如果没有catch就代表异常没有被处理过,如果该异常是检测时异常。那么必须声明。
//try finally 异常无法直接catch处理,但资源需要关闭
注意:
1,finally中定义的通常是 关闭资源代码。因为资源必须释放。
2,finally只有一种情况不会执行。当执行到System.exit(0);fianlly不会执行。
问题如果你可以处理就try catch,处理不了就抛
多个catch情况
package com.company;
class FuShuIndexException extends Exception{
FuShuIndexException(){}
FuShuIndexException(String msg){
super(msg);
System.out.println(msg);
}
}
class Demo{
public int method(int[] arr,int index) throws FuShuIndexException {
if (arr == null)
throw new NullPointerException("没有任何数组实体");
if (index < 0)
throw new FuShuIndexException("数组角标不能为负数");
return arr[index];
}
}
public class Main {
public static void main(String[] args){
int[] arr = new int[3];
Demo a = new Demo();
try{
int num = a.method(arr,-1);
System.out.println("num+"+num);
} catch (FuShuIndexException e) {
// e.printStackTrace();//jvm默认的异常处理机制就是调用异常对象的这个方法
System.out.println(e.toString());
System.out.println("数组角标不能为负数,大哥");
} catch (NullPointerException e){
// e.printStackTrace();
// System.out.println(e.getMessage());
} catch (Exception e){
e.printStackTrace();
//多catch父类的catch Exception放在最下面否则编译失败
}
System.out.println("done");
}
}
e.printStackTrace();
//jvm
默认的异常处理机制就是调用异常对象的这个方法
finally代码块
/*
finally代码块:定义一定执行的代码。
通常用于关闭资源。
*/
class FuShuException extends Exception
{
FuShuException(String msg)
{
super(msg);
}
}
class Demo
{
int div(int a,int b)throws FuShuException
{
if(b<0)
throw new FuShuException("除数为负数");
return a/b;
}
}
class ExceptionDemo5
{
public static void main(String[] args)
{
Demo d = new Demo();
try
{
int x = d.div(4,-1);
System.out.println("x="+x);
}
catch (FuShuException e)
{
System.out.println(e.toString());
return;
//System.exit(0);//系统,退出。jvm结束。
}
finally
{
System.out.println("finally");//finally中存放的是一定会被执行的代码。
}
System.out.println("over");
}
}
class NoException extends Exception
{
}
public void method()throws NoException
{
/*
连接数据库;
数据操作;//throw new SQLException();
关闭数据库;//该动作,无论数据操作是否成功,一定要关闭资源。
try
{
连接数据库;
数据操作;//throw new SQLException();
}
catch (SQLException e)
{
会对数据库进行异常处理;
throw new NoException();
}
finally
{
关闭数据库;
}
*/
}
异常-异常处理原则
-
函数内部如果抛出需要检测的异常,那么函数上必须要声明。
否则必须在函数内用
trycatch
捕捉,否则编译失败。 -
如果调用到了声明异常的函数,要么
trycatch
要么throws,否则编译失败 -
什么时候catch,什么时候throws 呢?
功能内部可以解决,用catch。
解决不了,用throws告诉调用者,由调用者解决。
-
一个功能如果抛出了多个异常,那么调用时,必须有对应多个catch进行针对性的处理。
内部有几个需要检测的异常,就抛几个异常,抛出几个,就catch几个。
自定义异常
-
使用 Java 内置的异常类可以描述在编程时出现的大部分异常情况。除此之外用户还可以自定义异常。用户自定义异常类,只需继承 Exception 类即可。
-
在程序中使用自定义异常类,大体可分为以下几个步骤:
- 创建自定义异常类。
- 在方法中通过 throw 关键字抛出异常对象。
- 如果在当前抛出异常的方法中处理异常,可以使用 try-catch 语句捕获并处理;否则在方法的声明处通过 throws 关键字指明要抛出给方法调用者的异常,继续进行下一步操作。
- 在出现异常方法的调用者中捕获并处理异常。
自定义异常类
自定义异常:
定义类继承Exception或者RuntimeException-
为了让该自定义类具备可抛性。
-
让该类具备操作异常的共性方法。
当要定义自定义异常的信息时,可以使用父类已经定义好的功能。
异常异常信息传递给父类的构造函数。
//自定义异常类 //假设传递数字>10异常 private int tishi;//创建一个提示信息 public Demo01(int a) {//创建一个构造器传递消息 this.tishi = a; } //toString打印信息:异常的打印信息 @Override public String toString() { return "异常{" + "tishi=" + tishi + '}'; }
throws抛出方法捕获
//创建一个可能会存在异常的方法 static void test(int a) throws Demo01 { System.out.println("传递的参数为:"+a); if (a>10){ throw new Demo01(a); } System.out.println("ok"); } public static void main(String[] args) { try { //赋值并捕获 test(11); } catch (Demo01 e) { // if( ){ } 可以增加一些处理异常的代码块 System.out.println("注意:"+e); } }
throw方法内捕获
//创建一个可能会存在异常的方法 static void test(int a) { System.out.println("传递的参数为:"+a); if (a>10){ try { throw new Demo01(a); } catch (Demo01 e) { System.out.println("注意:"+e);; } } System.out.println("ok"); } public static void main(String[] args) { test(15); //赋值 }
异常-异常的应用
/*
毕老师用电脑上课
问题领域中涉及两个对象
毕老师、电脑。
分析其中的问题
比如电脑蓝屏了,冒烟啦
*/
package com.company;
/*
毕老师用电脑上课
问题领域中涉及两个对象
毕老师、电脑。
分析其中的问题
比如电脑蓝屏了,冒烟啦
*/
class LanPingException extends Exception
{
LanPingException(String msg) {
super(msg);
}
}
class MaoYanException extends Exception
{
MaoYanException(String msg) {
super(msg);
}
}
//课时进度无法完成
class NoPlanException extends Exception{
public NoPlanException(String msg) {
super(msg);
}
}
class Teacher{
private String name;
private Computer comp;
public Teacher(String name) {
this.name = name;
comp = new Computer();
}
public void prelect() throws NoPlanException
{
try{
comp.run();
System.out.println(name+"讲课");
}
catch (LanPingException e) {
System.out.println(e.toString());
comp.reset(); //电脑重启
prelect(); //重启后,讲课
}
catch (MaoYanException e) {
System.out.println(e.toString());
test();
//可以对电脑进行维修
// throw e; 进行一个异常转换,发现冒烟异常抛出,课时进度无法完成 给调用者,让调用者解决
// 内部异常处理的对外异常转换(异常的封装。)
// 要点:不该暴露的问题,就没有必要暴露出去,因为暴露出去对方也处理不了
throw new NoPlanException("课时进度无法完成,原因:"+e.getMessage());
}
}
private void test() {
System.out.println("大家练习");
}
}
class Computer{
private int state = 2; //定义电脑状态
public void run() throws LanPingException,MaoYanException
{
if (state == 1)
throw new LanPingException("电脑蓝屏了!!!");
if (state == 2)
throw new MaoYanException("电脑冒烟了!!!");
System.out.println("电脑运行");
}
public void reset(){
state = 0;
System.out.println("电脑重启!");
}
}
public class ExceptionTestDemo {
public static void main(String[] args) {
Teacher t = new Teacher("毕老师");
try {
t.prelect();
} catch (NoPlanException e) {
System.out.println(e.toString()+"///////////.........");
System.out.println("换人");
// e.printStackTrace();
}
}
}
/*
class NoAddException extends Exception
{}
void addData(Data d) throws NoAddException
{
连接数据库
try{
添加数据 出现异常SQLException();
}
catch(SQLException e){
// 处理代码。
throw new NoAddException();
}
finally{
关闭数据库。
}
}
内部异常处理的对外异常转换(异常的封装。)
要点:不该暴露的问题,就没有必要暴露出去,因为暴露出去对方也处理不了
*/
异常-注意事项
异常在子父类覆盖中的体现;
-
子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或者该异常的子类。
class FU { void show() throws A { } } class Test { void method(Fu f) //Fu f = new Zi(); //父类引用传向子类对象,而在调用show()的时候,编译看左边,运行看右边。 //所以运行的是Zi里面的show();而Zi中抛的是B异常,Test中没有catch,所以程序挂了。 { try{ f.show(); } catch(A a) { } } } class Zi extends Fu { void show() throws B } class { public static void main(String[] args) { Test t = new Test(); t.show(new Zi()); } }
-
如果父类方法抛出多个异常,那么子类只能抛出父类异常的子集
简单说:子类覆盖父类只能抛出父类的异常或者子类,或者子集。
注意:如果父类或者接口的方法中没有异常抛出,那么子类在覆盖方法时,也不可以抛出异常。
如果子类方法发生了异常。就必须要进行try处理。绝对不能抛。
interface Inter
{
void function();
}
class D implements Inter{
public void function();//throws Exception
}
实际应用中的经验总结
常见异常:
- 脚标越界异常(
IndexOutOfBoundsException
)包括数组、字符串; - 空指针异常(
NullPointerException
) - 类型转换异常:
ClassCastException
- 没有这个元素异常:
NullPointerException
- 不支持操作异常;
异常要尽量避免,如果避免不了,需要预先给出处理方式。比如家庭备药,比如灭火器。
处理运行时异常
,采用逻辑去合理规避同时辅助 try-catch 处理- 在多重 catch 块后面,可以加一个 catch(Exception)来处理可能被遗漏的异常
- 对于不确定的代码,也可以加上 try-catch ,处理潜在的异常
- 尽量去处理异常,切忌只是简单地调用 printStackTrace()去打印输出
- 具体如何处理异常,要根据不同的业务需求和异常类型去决定
- 尽量添加 finally 语句块去释放占用的资源
Object类
object :所有类的根类
object是不断抽取而来的,具备着所有对象都具备的共性内容
equal方法
其实Object类的equals方法也是比较的对象的地址,和==这个功能是一样的,对于对象而言
class Person{
private int age;
Person(int age){
this.age = age;
}
}
public class ObjectDemoEquals {
public static void main(String[] args) {
Person p1 = new Person(20);
Person p2 = new Person(20);
Person p3 = p1;
System.out.println(p1 == p2); // 比较的是地址 所以为false
System.out.println(p1.equals(p3)); // ture
//System.out.println(p1.equeals(p2))输出结果是:false;
}
}
Object类的源代码
equals方法覆盖
上面的equals方法我不写是不是也可以,这样是不是有点多余也,本身==就已经够了,不用再使用equals方法来进行比较,
下面也我们重写object类中的equals方法,我不想比较对象的地址值,我想比较的是内容是否相等our
class Person{
private int age;
Person(int age){
this.age = age;
}
/*
一般都会覆盖此方法,根据对象的特有内容,建立判断对象是否相同的依据;
*/
public boolean equals(Object obj) //多态:Object obj = p2;向上转型为obj,隐藏子类的特有内容
{
//向下强转,Object接收的范围很广,而我用的只有一个Person,所以要做健壮性判断
// instanceof 判断类型
if (!(obj instanceof Person))
{
// return false;
throw new ClassCastException("类型错误");// 类型转换异常类
}
//多态:Object obj = p2;向上转型为obj,隐藏子类的特有内容
//向下强转,Object接收的范围很广,而我用的只有一个Person,所以要做健壮性判断
//如果是Person就进行转型,如果不是就不能做任何事情。
Person p = (Person)obj;
return this.age == p.age;
}
}
public class ObjectDemoEquals {
public static void main(String[] args) {
Person p1 = new Person(20);
Person p2 = new Person(20);
Person p3 = p1;
System.out.println(p1 == p2);
System.out.println(p1.equals(p3));
}
}
你只要是定义类,它就具备equals方法,,这个equals只判断地址是没有意义的,所以我们一般都会重写这个方法,根据类的特有属性去判断是否是同一个对象,去判断的内容是否相同
hashCode
方法
如果要判断一个对象真真的相等,一般是先判断对象的内容,然后还要判断对象在内存中的地址即就是hoshcode
值,是否相等,其实hashCode
就是在内存中的一个地址,我们也可以重写Object中的hashCode
方法,来生成hashCode
值
public class ObjectDemoEquals {
public static void main(String[] args) {
Person p1 = new Person(20);
Person p2 = new Person(20);
System.out.println(p1);
System.out.println(p1.hashCode());
System.out.println(Integer.toHexString(p1.hashCode()));
//将十进制转为十六进制,得到相应的hashCode值。
}
}
输出结果为:
com.Object.Person@1540e19d
356573597
1540e19d
getClass
方法
例如:p1.gerClass()
;其实拿到的就是p1
类所属的字节码文件,对象其实说白了就是一个当前运行类的一个字节码文件
getClass()
当前对象所属的字节码文件对象
如果读到Person.class
文件,第一步就是在堆内存中产生了一个对象,这个对象就是Person.class
对象(字节码文件对象,内存中只有一份),先把他加载进来封装成对象,将他们的内容进行空间分布完以后,我们在产生new的时候,根据字节码文件在内存中产生了Person对象。
所以下面的两个对象都是根据字节码文件对象,Person.class
产生的,他们都有自己所属的字节码文件Person.class
getClass()
方法就是获取他们所属的字节码文件
// Class clazz1 = p1.getClass();
// Class clazz2 = p2.getClass();
// System.out.println(clazz1==clazz2);true
// System.out.println(clazz1.getName());
toString()
Person p = new Person();
当我们使用System.out.println(p);
输出一个对象的时候就会调用对象的toString()
方法
System.out.println(p);
这个时候就会调用toString()
方法,这个toString
方法父类默认返回的就是一个对象名称@hashCode
值,如果我们想返回特定的值,可以重写父类的toString
方法,当我们再要输出对象的时候也就会调用我们自己的toString
方法
//System.out.println(p1); //这里默认调用toString()方法
//System.out.println(p1.getClass().getName()+"$"+Integer.toHexString(p1.hashCode()));
输出结果:
com.Object.Person@1540e19d
com.Object.Person$1540e19d
Package
包-概述
定义包用package关键字。
1:对类文件进行分类管理。
2:给类文件提供多层名称空间。
如果生成的包不在当前目录下,需要最好执行classpath,将包所在父目录定义到classpath变量中即可。
一般在定义包名时,因为包的出现是为了区分重名的类。所以包名要尽量唯一。怎么保证唯一性呢?可以使用url域名来进行包名称的定义。
package pack;//定义了一个包,名称为pack。 注意:包名的写法规范:所有字母都小写。
//package cn.itcast.pack.demo;
类的全名称是 包名.类名
编译命令:javac –d
位置(.当前路径) java
源文件 (就可以自动生成包)
包是一种封装形式,用于封装类,想要被包以外的程序访问,该类必须public;
类中的成员,如果被包以外访问,也必须public;
package mypack
class PackageDemo
{
public static void main(String[] args)
{
System.out.println("Hello Package");
}
}
包-包之间的访问-protected
包与包之间访问可以使用的权限有两种:
- public
protected
:只能是不同包中的子类可以使用的权限。
包与包之间的权限,只有public和protected两个。
总结java
中的四种权限:
范围 | public | protected | default | private |
---|---|---|---|---|
同一个类中 | √ | √ | √ | √ |
同一个包中 | √ | √ | √ | |
子类 | √ | √ | ||
不同包中 | √ |
PackageDemo.java:8: packa.DemoA在 packa 中不是公共的;无法从外部软件包中对其进
行访问
packa.DemoA d = newpacka.DemoA();
^
PackageDemo.java:8:packa.DemoA 在 packa 中不是公共的;无法从外部软件包中对其进
行访问
packa.DemoA d = newpacka.DemoA();
^
2 错误
--------------------------------------
PackageDemo.java:9:show() 在 packa.DemoA 中不是公共的;无法从外部软件包中对其进
行访问
d.show();
^
1 错误
总结:
包与包之间的类进行访问,被访问的包中的类必须是public的,被访问的包中的类的方法也必须是public的。
包-导入import
导包就是导入包中的类。
//import packa.DemoA; //导入了packa包中的Demo类。
//import packa.DemoAA;
//import packa.DemoAAA;
/*
import packa.* //导入了packa包中的所有的类。不包含子包。
import packa.abc.*;//导入了packa包中的子包abc下的当前的所有类。
packa\DemoA.class
packa\abc\DemoAbc.class
*/
//导包的原则:用到哪个类,就导入那个累。
//import 干嘛用的呀?为了简化类名书写。
如果导入的两个包中存在着相同名称的类。这时如果用到该类,必须在代码中指定包名。
**常见的软件包 : **
java.lang : language java的核心包,Object System String Throwable jdk1.2版本后,该包中的类自动被导入。
java.awt : 定义的都是用于java图形界面开发的对象。
javax.swing: 提供所有的windows桌面应用程序包括的控件,比如:Frame , Dialog, Table, List 等等,就是java的图形界面库。
java.net : 用于java网络编程方面的对象都在该包中。
java.io : input output 用于操作设备上数据的对象都在该包中。比如:读取硬盘数据,往硬盘写入数据。
java.util : java的工具包,时间对象,集合框架。
java.applet: application+let 客户端java小程序。server+let ——> servlet 服务端java小程序。
包-Jar包
jar :java
的压缩包,主要用于存储类文件,或者配置文件等。
命令格式:jar –cf 包名.jar 包目录
解压缩:jar –xvf 包名.jar
将jar包目录列表重定向到一个文件中:jar –tf 包名.jar >c:\1.txt
c:创建压缩文档。
f:制定存档名称。
v:显示详细信息。
m:加入自定义清单信息。
通常应用与Java制作的图形界面程序。在清单文件中其中定一个Main-Class:空格 带有主函数的类名回车
在设置一下jar文件的打开方式通过javaw –jar
就可以双击执行了
基础练习
package com.practice;
interface A{}
class B implements A{
public String func(){
return "func";
}
}
public class BasalPractice_01 {
public static void main(String[] args) {
A a = new B();
//编译看左边,运行右边,左边a中没有func方法。
System.out.println(a.func());
//编译失败,因为a所属的A接口中没有定义func方法。
}
}
class Fu{
boolean show(char a){
System.out.println(a);
return true;
}
}
public class BasalPractice_01 extends Fu{
public static void main(String[] args) {
int i = 0;
Fu f = new BasalPractice_01();
//多态,父类引用指向子类对象,调用子类的show()方法
BasalPractice_01 d = new BasalPractice_01();
for (f.show('A');f.show('B')&&(i<2);f.show('C'))
{
i++;
d.show('D');
}
}
boolean show(char a)
{
System.out.println(a);
return false;
}
}
//打印输出:
A
B
interface A{}
class B implements A{
public String test(){
return "yes";
}
}
public class Demo {
static A get(){
return new B();
}
public static void main(String[] args) {
A a = get();
//左边是接口型引用右边是方法(这方法肯定有返回值,返回类型肯定是A类型)
//A a = get();就相当于A a = new B();
System.out.println(a.test());
//编译失败,Cuz:A接口中没有定义test方法。
}
}
主函数里面左边是接口型引用右边是方法(这方法肯定有返回值,返回类型肯定是A类型)其实,A a = get();就相当于A a = new B();
下面输出语句会编译失败,Cuz
A接口中没有定义test方法。
class Super{
int i = 0;
public Super(String a){
System.out.println("A");
i = 1;
}
public Super(){
System.out.println("B");
i += 2;
}
}
public class Demo extends Super {
public Demo(String a){
//super(); 空参所以下一步去无参构造
System.out.println("C");
i += 5;
}
public static void main(String[] args) {
int i = 4;
Super d = new Demo("A");
System.out.println(d.i);
}
}
//打印输出:
B
C
7
interface Inter{
void show(int a, int b);
void func();
}
public class Demo {
public static void main(String[] args) {
//补足代码;调用两个函数,要求用匿名内部类。
Inter in = new Inter()
{
public void show(int a, int b){}
public void func(){}
};
in.show(3,4);
in.show()
}
}
/*new Inter()
{
public void show(int a, int b){}
public void func(){}
};
是匿名内部类,其实是匿名子类对象
Inter in = new Inter(){}
Inter是接口,接口引用指向子类对象,所以为多态
*/
//写出程序结果:
class TD{
int y = 6;
class Inner
{
static int y = 3;
//编译会失败,因为非静态的内部类中,不能定义静态的成员
void show(){
System.out.println(y);
}
}
}
public class Demo {
public static void main(String[] args) {
TD.Inner ti = new TD().new Inner();
ti.show();
}
}
选择题,选出错误答案错误的原因,用单行注释的方式。
class Demo {
int show(int a,int b){return 0;}
}
下面哪些函数可以存在于Demo的子类中://意味着搞一个类去继承Demo,下面的函数哪个会编译通过。
A.public int show(int a,int b){return 0;}//可以,覆盖了
B.private int show(int a,int b){return 0;}//不可以,权限不够
C.private int show(int a,long b){return 0;}//可以,子类特有方法
D.public short show(int a,int b){return 0;}//不可以,调用的不确定性。
E.static int show(int a,int b){return 0;}//不可以,静态只能覆盖静态。
输出:
4 //
5
showZi
showZi
//private int x,y;
private int sum;
public void add(int a,int b)
{
this.x = a;
this.y = b;
sum = a+b;
}
public void show()
{
//System.out.println(this.x+y);
System.out.println(sum);
}
打印输出:
B //到B之后去catch了,然后接着执行下面的语句
C
D
class Super{
int i = 0;
public Super(String s)
{
i = 1;
}
}
class Demo extends Super
{
public Demo(String s){
//super();//父类中没有空参构造函数,编译失败。
i = 2;
}
public static void main(String[] args){
Demo d = new Demo("yes");
System.out.println(d.i);
}
}
编译失败。方法的调用的不确定性。
编译失败:
throw new Exception(); //throw之后跳转了,程序挂掉
System.out.println("A");//该条无法执行,废话
上面的题为什么没事,
因为这个方法将这个异常进行了封装,一封装完就变成了内部的了,外界是不是不知道啊,外界知道你有可能有问题,但是一定有问题吧,不一定,所以那句话还是有机会执行到的
记住了:像throw下面别写任何语句,根本没有机会执行到,必须转,懂吧
A:在位置1写 new Inner(); //可以。外部类想访问内部类,必须建立内部类对象
B:在位置2写 new Inner(); //不可以,因为主函数是静态的,只能调用静态成员,所以内部类也必须是静态
C:在位置2写 new d.Inner(); //不可以,new new Demo().Inner();格式错误 new Demo().new Inner();
D:在位置2写 new Demo.Inner();//不可以,格式正确,但是Inner必须是静态的。
//多catch时,父类的catch放在最下面。
/*
解释一下为什么多个catch,父类要放在最下面
try 一旦发现了问题,会把这个问题给catch,给那个catch,当然是首先给第一个catch,并判断这个对象是不是所属于
这个类型,一看所属于,那就ok了,任何异常对象的类型都是Exception e,这个能处理所有的异常对象,意味着下面
的两个catch是废话
一般我们都是发现几个异常我们就处理几个异常,如果在发生第三个异常,让他停掉吧,那就是程序就挂了吧,
为什么呢,如果他不挂是不是我们发现不了问题,正因为它挂了我们才发现了问题,就可以解决了,是这个道理
*/
new Demo.show(new Test{
public void func(){}
})
//主函数是静态的,得new对象调用方法,interface必须写实现。
打印输出:
134
13423
interface Areable{
public double getArea();
}
class NoValueException extends RuntimeException{
NoValueException(){
super();
}
NoValueException(String mes){
super(mes);
}
}
//这就是异常的好处,我在里面抛不用在函数里面声明了,
//因为我声明是没有必须的,我声明了你不是要处理吗,
//你处理完了那起不是还要继续往下运行,没有意义吗,这就是runtime的好处
class Rec implements Areable{
private int length;
private int width;
Rec(int length,int width){
if (length<= 0 || width <= 0)
{
// System.out.println("数值非法");
// return;
throw new NoValueException("数值非法");
}
this.length = length;
this.width = width;
}
@Override
public double getArea() {
return length*width;
}
}
class Circle implements Areable{
private int radius;
public static final double PI = 3.14;
Circle(int radius){
if (radius <= 0)
throw new NoValueException("半径值非法");
this.radius = radius;
}
@Override
public double getArea() {
return radius*radius*PI;
}
}
public class BasalPractice_03 {
public static void main(String[] args) {
Rec r = new Rec(-3,5);
double a = r.getArea();
System.out.println("area:"+a);
}
}
public class Demo {
private static int j = 0;
private static boolean methodA(int k)
{
j += k;
return true;
}
public static void methodB(int i)
{
boolean b;
b = i <10 | methodA(4); //
b = i <10 || methodA(8);
}
public static void main(String args[])
{
methodB(0);
System.out.println(j);
}
}
//打印输出:
4