程序员,程序猿,程序媛,码农
———————————————————————————————————————————————————————
——Java的应用领域
J2SE 主要用来开发桌面应用软件。
J2ME 嵌入式开发,例如手机里的软件,掌上电脑软件等等。
J2EE 属于网络编程,例如JSP等等,做网站用。
——参考资料
张孝祥 《Java就业培训》
林信良 《Java学习笔记》
机械工业 《Java核心编程》
机械工业 《Java编程思想》
——配置环境变量
set classpath=.;自定义目录 //则先查找当前目录,然后再查找自定义目录,如果没有点,则只查找自定义目录
——标识符
在程序中自定义一些名称。
由26个英文字母大小写、数字0-9符号 _ $ 组成。
定义合法标识符规则:
1、数字不可以开头。
2、不可以使用关键字。
Java中严格区分大小写。
Java中名称的规范:
包名:
多单词组成时所有字母都小写。
类名接口名:
多单词组成时,所有单词的首字母大写。
变量名和函数名:
多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写。
常量名:
所有字母都大写,多单次时每个单词用下划线连接。
——常量
常量表示不能改变的数值。
Java中常量的分类:
1、整数常量:所有整数。
2、小数常量:所有小数。
3、布尔型常量:true false
4、字符型常量:将一个数字、字母或者符号用单引号(‘ ‘)标识。
5、字符串常量:将一个或多个字符用双引号标识。
6、null常量:只有一个数值,就是null。
对于整数:
二进制:0 1
十进制:0-9,逢十进一。
八进制:0-7,逢八进一,用0开头表示。
十六进制:0-9,A-F,逢16进一,用0x开头表示。
——变量
变量的概念
内存中的一个存储区域。
该区域有自己的名称(变量名)和类型(数据类型)。
该区域的数据可以在同一类型范围内不停变化。
为什么要定义变量
用来存放同一类型的常量,并且可以重复使用。
变量使用注意:
变量的作用范围。(一对{ }内有效)
变量初始化。
定义变量的格式
数据类型 变量名 = 初始化值;
数据类型
基本数据类型:
数值型:
整数类型:
byte 1个字节 -128~127 -2^7~2^7-1
short 2个字节 -32768~32767 -2^15~2^15-1
int 4个字节 (默认)
long 8个字节 赋值时后面一般加小写L
浮点型:
float 4个字节 赋值时后面一般加f
double 8个字节(默认)
字符型:char 2个字节 //Java中字符型变量占两个字节。
布尔型:boolean
引用数据类型
类:class
接口:interface
数组:[ ]
整数默认 int 小数默认 double
——强制类型转换
由于Java是强类型语言,所以赋值时需要注意类型转换问题。
当一个java算术表达式中包含多个基本类型的值时,整个算术表达式的数据类型将发生自动提升。Java定义如下的自动提升规则:
1. 所有byte型、short型和char型将被提升到int型。
2. 整个算术表达式的数据类型自动提升到与表达式中最高等级操作数同样的类型。
float 和 int 进行运算时,以 float 为准。
——运算符
取模运算时,假如其中一数为负数,则只看运算符左边的数字:
-2%5 余数为-2。
6%-8 余数为6。
-10%-5 余数为0。
+ 号
字符串连接符:
“haha”+“heihei” 输出结果为 hahaheihei
字符串数据和任何数据使用+相连接,最后都会变成字符串。
System.out.println("haha"+ (++a)); 输出结果为:haha2 // ln表示括号内所有语句输出完毕后在结尾处换行一次
System.out.println("a = "+5); 输出结果为:a = 5
——转义字符
\n:换行 Linux中只有一个换行符:\n
\b:退格Backspace Windows中有两个换行符:\n \r
\r:回车 DOS中可以直接识别 \n
\t:下一个制表位
/*
System.out.print("hello \r world");
为什么输出结果是:
——————
world"
——————
在早期打字机中,每打完一行,会先跳回行首,再执行换行。
*/
如何输出上引号
System.out.println("\"hello world\"");
输出结果:”hello world“
char ch = ‘\n‘;也可以,因为\n转义完之后是一个换行符。
char ch = ‘啊‘;也可以,因为Java中char占2个字节,一个汉字也占两个字节。
——赋值运算符
shotr x = 4;
x = x + 5; //编译失败
而
short s = 3;
s += 5; //编译成功,因为x = x + 5;是两次运算,在运算过程中会进行自动类型提升,运算后的结果是int类型,无法赋给short类型
的变量;而 s += 5;只进行一次运算,在运算过程中会自动进行强制类型转换。
——比较运算符
比较运算符的运行结果都是boolean型,要么是true要么是false。
——逻辑运算符
& | ^ !
^:异或(相同为假,不同为真)
true ^ true = false;
true ^ false = true;
false ^ true = true;
false ^ false = false;
^:两边相同结果是 false
两边不同结果是 true
& 和 &&的区别:(一假全假)
&:无论左边是 true 还是 false ,都不会影响右边的运算。
&&:当左边为 false 时,右边不运算。
| 和 ||的区别:(一真全真)
|:两边都参与运算。
||:当左边为 true 时,右边不运算。(短路)
——按位操作符
与运算:& 或运算:| 异或运算:^ 取反:~
一假全假——
如果两个输入位都是1,则按位与(&)运算,生成一个输出位1;否则生成一个输出位0。
一真全真——
两个输入位只要有一个是1,则按位或( | )运算,生成一个输出位1;只有在两个输入位都是0的情况下,才会输出一个输出位0。
相同为假,不同为真——
如果输入位的某一个是1,但不全都是1,那么按位异或(^)运算,生成一个输出位1;两个都为0或1,生成一个输出位0。
&:(一假全假)
6 & 3 = 2;
6 ---- 110 0 就是假,1 就是真,一假全假。
3 ---- & 011
2 ---- 010 = 2
| :(一真全真)
或同理。
^ :(相同为假,不同为真)
异或同理,一个数异或同一个数两次,结果还是初始值,一般用于加密。
——位移算符(运算速度最快)
左移:<< 右移:>> 无符号右移:>>>
先把数字转换成二进制形式,然后将其左移或右移,缺少位用 0 补足。
| 3 << 2 = 12; | 3 << 1 = 6; | 3 << 3 = 24; |
| 3 * 4 = 12; | 3 * 2 = 6; | 3 * 8 = 24; |
| 3 * 2^2 = 12; | 3 * 2^1 = 6; | 3 * 2^3 = 24; |
往左移,移几位,就是十进制数字乘以二的几次幂。
往右移,移几位,就是十进制数字除以二的几次幂。(舍弃小数位)
右移(>>)时,正数高位补 0 ;负数高位补 1 。
无符号右移(>>>)时,高位补 0 。
练习:
1、最有效率的方式算出2乘以8等于几? 2 << 3
2、对两个整数变量的值进行互换(不需要第三方变量)。
(1)int n , m;
n = n + m; // 如果n和m 的值非常大,容易超出int范围。
m = n - m;
n = n - m;
(2)n = n ^ m;
m = n ^ m; // (n ^ m) ^ m n 异或 m 两次,结果还是 n。
n = n ^ m; // n ^ ( n ^ m) m 异或 n 两次,结果还是 m。
——流程控制
switch语句:
只接收四种类型的值:byte short int char /* JDK 7.0版本可以判断字符串 */
运行过程中如果没有 break ,则按顺序向下执行,直到 switch 结束。
break和continue:
在循环过程中可以通过break *; 或者是continue *; 来中断 / 跳出某个循环,例如:
/* 这是一个9*9乘法表,如果在标号为 w的for循环中有一个break w;
则运行到该语句时会中断 w 循环,break w; 也可以写在内循环当中。
标号只能用于循环。
*/
class chengfabiao
{
public static void main(String[] agrs)
{
w:for (int x = 1;x <= 9; x++)
{
q:for(int y = 1; y <= x; y++)
{
System.out.print(y+"*"+x+"="+y*x+"\t");
}
System.out.print("\n");
break w;
}
}
}
——函数重载
重载的定义:
在同一个类中,允许存在一个以上的同名函数,只要它们的参数个数或者参数类型不同即可。
重载的特点:
与返回值无关,只看参数列表。
重载的优点:
方便阅读,优化了程序设计。
重载示例:
1、public static int add(int x,int y)
{
return x+y;
}
2、public static int add(int x,int y,int z)
{
return x+y+z;
}
3、public static int add(int x,int y,int z)
{
return add(x,y)+z;
}
——数组
定义:
同一数据类型的集合。其实数组就是一个容器。(能存数据的地方就称之为容器)
在堆内存中定义数组时,都有默认值。
Java中栈内存,当函数调用结束时由系统自动释放,堆内存由Java垃圾回收机制不定时回收。
格式1:
元素类型 [ ] 数组名 = new 元素类型 [ 元素个数或数组长度 ]
示例:
int [ ] arr = new int [ 5 ];
int arr [ ] = new int [ 5 ];
格式2:
元素类型 [ ] 数组名 = new 元素类型 [ ] {元素1, 元素2, ......};
示例:
int [ ] arr = new int [ ] {3,5,7};
int [ ] arr = {3,5,7};
注意:当两个引用同时指向一个数组时,其中一个引用null,该数组不会变成垃圾值。(Java中的引用就是C语言的指针)
数组中有一个属性可以直接获取到数组元素的个数:length
字符串中获取长度的方法是:length();
使用方式:数组名称.length
数组的二分查找:
二分查找可以提高效率,但是必须保证数组中的元素是有序的。
右移一位相当于本数除以2。
/**
*
*
*2016年3月20日00:33:49
* 使用二分查找 获取数组元素
*
*@author WYC
*
*/
public class BinarySearchDemo {
publicstatic void main(String[] args)
{
intarr[] = {2,4,7,9,15,17,26,37,58,66};
System.out.println(binarySearch_2(arr,15));
}
//方法一
publicstatic int binarySearch_1(int arr[] , int key)
{
intmax = arr.length-1,min = 0 , mid = (max + min)>>1;
while(min<= max)
{
if(arr[mid]> key)
{
max= mid - 1;
}
elseif(arr[mid] < key)
{
min= mid + 1;
}
else
{
return mid;
}
mid= (max + min)>>1;
}
return-1;
}
//方法二
publicstatic int binarySearch_2(int arr[], int key)
{
intmin = 0, max = arr.length - 1, mid = (max + min) >> 1;
while(arr[mid]!= key)
{
if(arr[mid]> key)
{
max= mid - 1;
}
elseif(arr[mid] < key)
{
min= min + 1;
}
if(max< min)
{
return-1;
}
mid= (max + min) >> 1;
}
returnmid;
}
}
练习:
又一个有序的数组,想要将一个元素插入到该数组中,还要保证该数组是有序的。
如何获取该元素在数组中的位置。
将返回值改为 -min。
基于折半查找的返回要插入的脚标元素。
——面向对象(面向过程强调的是“动作”,面向对象强调的是“对象”)
对象就是多个功能的封装。
对象是通过new来产生的一个实体,存在于堆内存。
1、面向对象概念
理解面向对象:
面向对象是相对面向过程而言。
面向对象和面向过程都是一种思想。
面向过程:强调的是功能行为。
面向对象:将功能封装进对象,强调具备了功能的对象。
面向对象是基于面向过程的。
面向对象的特点:
面向对象是一种思想,它能让复杂的问题简单化,它能让我们的角色发生转变,从执行者转变为指挥者(怎么做的我不
知道,我只要结果)。
万物皆对象。
面向对象的三个特征:封装,继承,多态。
开发,其实就是找对象,使用对象,建对象,并维护对象之间的关系。
2、类与对象的关系
类就是对现实生活中事物的描述。
对象就是这类事物,实实在在存在的个体。
类的属性对应的是类中的变量,类的行为对应的是类中的函数(方法),其实定义类,就是在描述事物,属性和行为共同称为类
中的成员(成员变量和成员方法)。
通过类来创建一个对象:
类 abc = new 类();
abc是一个类类型变量(引用类型变量),类类型变量指向对象。
成员变量和局部变量作用范围:
成员变量作用于整个类中。
局部变量作用于函数中,或语句中。
成员变量有默认值,可以参与运算,局部变量无默认值。
* 其中数值类变量默认值为0
* 浮点型默认值为0.0
* 字符变量默认值为空格
* 布尔型变量默认值为false
成员变量和局部变量在内存中的存储:
成员变量:
成员变量随着类的加载而加载并且生成默认值。
在堆内存中,因为对象的存在,才在内存中存在。
局部变量:
存在于栈内存中。
匿名对象:
匿名对象是对象的简化形式。
new Person().age = 20;
匿名对象的两种使用情况:
当对对象方法仅进行一次调用的时候
匿名对象可以作为实际参数进行传递
匿名对象使用方式一:
当对对象的方法只调用一次时,可以用匿名对象来完成,这样写比较简单。
但是对一个对象进行多个成员调用,必须给这个对象起名字。
因为第二次匿名调用会使第一次匿名调用的对象成为垃圾值。
匿名对象使用方式二:
可以将匿名对象作为实际参数进行传递。
当类存在于不同文件之间,JVM会在当前目录下寻找class文件和java文件,没有则会报错。
3、封装(Encapsulation)
封装:是指隐藏对象的属性和细节实现,仅对外提供公共访问方式。
好处:将变化隔离
便于使用
提高重用性
提高安全性
封装原则:
将不需要对外提供的内容都隐藏起来。
把属性都隐藏,提供公共方法对其进行访问。
private:
私有,权限修饰符:用于修饰类中的成员(成员变量,成员函数)。
私有只在本类中有效。
将成员变量私有化以后,类以外即使建立了对象也不能直接访问,就需要在类中提供对应访问成员变量的方式。
一个属性通常对应两个方法:set 和 get。
注意:私有仅仅是封装的一种表现形式。
之所以对外提供访问方式,是因为可以在访问方式中加入逻辑判断等语句,对访问的数据进行操作,提高代码的健壮性。
4、构造函数
当创建对象时,因为调用了名字相同的类,所以通过函数重载对对象进行初始化。
特点:
1、函数名与类名相同
2、不用定义返回值类型
3、不可以写return语句
作用:
类对象一建立就会调用与之对应的构造函数。
构造函数的作用是给对象进行初始化。
注意:
1、默认构造方法的特点
给成员变量赋予初始值。
2、多个构造方法是以重载的形式存在的。
构造方法的小细节:
当一个类中没有定义构造方法时,那么系统会默认给该类加入一个空参数的构造函数(默认构造方法)。
当在类中自定义了构造函数以后,默认的构造函数就没有了。
构造函数和一般函数在写法上有不同,在运行方面也有不同:
构造函数是在对象一建立就运行,作用是给对象初始化;
而一般函数是对象调用才执行,是给对象添加对象具备的功能。
一个对象建立,构造函数只运行一次;
而一般方法可以被该对象调用多次。
什么时候定义构造函数?
当分析事物时,该事物存在即具备一些特性或者行为(例如人出生具备姓名年龄),那么将这些内容定义在构造函数中。
构造代码块:构造代码块中定义的是不同对象共性的初始化内容。
作用:
给对象进行初始化。
对象一建立就运行,而且优先于构造函数执行。
构造代码块和构造函数的区别:
构造代码块是给所有对象进行统一初始化,而构造函数是给对应的对象进行初始化。
格式:
{......}
区别于静态代码块( static{......} ),静态代码块在一个程序中只在类的第一次加载时执行一次。
构造代码块在每创建一个对象时调用一次。
5、this 关键字
this关键字是用于区分局部变量和成员变量同名的情况。
可以在构造函数中使用this关键字,在构造函数中,this代表所创建对象。
格式:
this.name
this代表它所在函数的所属对象的引用,简单来说,就是哪个对象在调用this所在的函数,this就代表哪个对象。
this的应用:
当定义类中功能时,该函数内部要用到调用该函数的对象,这时用this来表示该对象。
但凡本类功能内部使用到了本类对象,都用this表示。
构造函数间的函数调用:
this关键字可以用于构造函数中,并且构造函数间不能用 函数名(实参) 来调用,需要用this关键字来调用。
格式:
this(实参)。
注意:
this构造方法只能定义在构造函数的第一个语句,初始化动作要先执行。
要防止构造函数间无限调用,陷入死循环。
6、static 关键字
用于修饰成员(成员变量和成员函数)
被修饰后的成员具备以下特点:
随着类的加载而加载,并且随着类的消失而消失,说明它的生命周期最长,简称为类变量,未创建对象时可以用类名调用。
优先于对象存在(非静态成员变量只有创建对象时才会创建成员变量)
被所有对象所共享
可以直接被类名调用
类中静态成员互相调用时省略的是(类名 . ),而非静态成员变量互相调用时省略的是(this .)。
方法区,共享区,数据区,static静态变量存储在该区域内,所有对象可以共享该区域。
实例变量和类变量的区别:
1、存放位置
类变量随着类的加载而存在于方法区中
实例变量则随着对象的建立而存在于堆内存中
2、生命周期
类变量生命周期最长,随着类的消失而消失。
实例变量生命周期随着对象的消失而消失。
静态变量使用注意事项:
1、静态方法只能访问静态成员,成员包括方法和变量。
可以将对象作为参数传入静态方法,从而在静态方法中调用非静态方法。
非静态方法可以访问静态成员。
2、静态方法中不可以写 this、super关键字,无对象不能访问this。
3、主函数是静态的。
4、static静态变量的值可以修改,整个类享有同一个静态变量,一改全改。
静态有利有弊:
利:
对对象的共享数据进行单独空间的存储,节省空间,没有必要每一个对象中都存储一份。
可以直接被类名调用。
弊:
生命周期过长,浪费内存空间。
访问出现局限性。(静态只能访问静态)
6、main函数
public static void main(String[ ] agrs)
主函数:
是一个特殊的函数,作为程序的入口,可以被JVM调用
主函数的定义:
public:代表着该函数访问权限是最大的
static: 代表主函数随着类的加载就已经存在了
void: 主函数没有具体的返回值
main: 不是关键字,但是是一个特殊的单词,可以被JVM识别
(String[ ] args):函数的参数,参数类型是一个数组,该数组中的元素是字符串,是字符串类型的数组,args是变量名。
主函数是固定格式的:JVM识别。
注意:
当存在两个主函数时,参数列表不同,编译时不会报错,因为参数列表不同,相当于函数重载,不过入口还是(String[ ] arr)
JVM在调用主函数时,传入的是new String [0]; 推理如下:
class MainDemo
{
public static void main(String[] args)
{
System.out.println(agrs);
System.out.println(args.length);
}
}
在DOS命令行中运行 java MainDemo时,在MainDemo之后输入字符串,会将字符串作为函数参数传给主函数的String[ ]。
什么时候使用静态?
要从两方面下手:
因为静态修饰的内容有成员变量和函数。
什么时候定义静态变量(类变量)呢?
当对象中出现共享数据时,该数据被静态所修饰(一改全改)。
而对象中的特有数据要定义成非静态存在于堆内存中。
什么时候定义静态函数呢?
当功能内部没有访问到非静态数据(对象的特有数据),那么该功能可以定义成静态。
因为静态变量随着类的加载而加载,可以通过类名来调用静态变量,节省内存,提高效率。
静态的应用:
每一个应用程序中都有共性的功能,可以将这些功能进行抽取,独立封装成类,以便复用。
静态工具示例:
public class ArrayTool
{
public static void main(String[] args)
{
int [] arr = {1,4,2,6,7,3};
Tool tool = new Tool();
tool.show(arr);
tool.sort(arr);
tool.show(arr);
int max = tool.getMax(arr);
System.out.println("max = " + max);
int min = tool.getMin(arr);
System.out.println("min = " + min);
}
}
class Tool
{
public static int getMax(int [] arr)
{
int max = 0;
for(int i = 0; i < arr.length; i++)
{
if(arr[max] < arr[i])
{
arr[max] = arr[i];
}
}
return arr[max];
}
public static int getMin(int [] arr)
{
int min = 0;
for(int i = 0; i < arr.length; i++)
{
if(arr[min] > arr[i])
{
arr[min] = arr[i];
}
}
return arr[min];
}
public static void sort(int [] arr)
{
for(int i = 0;i < arr.length-1; i++)
{
for(int j = i+1; j< arr.length; j++)
{
if(arr[i] < arr[j])
{
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
}
public void show(int [] arr)
{
System.out.println("输出数组:");
for(int i = 0; i < arr.length; i++)
{
System.out.print(arr[i]);
}
System.out.println();
System.out.println("输出结束!");
}
}
虽然可以通过建立ArrayTool的对象使用这些工具方法,对数组进行操作,但是发现了问题:
1、对象是用于封装数据的,但是ArrayTool对象中并未封装特有数据。
2、操作数组的每一个方法都没有用到ArrayTool对象中的特有数据。
这时就考虑,让程序更严谨,是不需要对象的。
可以将ArrayTool中的方法都定义成static的,直接通过类名调用即可。
注意:
将方法都静态后,可以方便使用,但是该类还是可以被其他程序建立对象,为了使程序更加严谨:
强制让该类不能建立对象,可以通过将构造函数私有化来完成。
private ArrayTool ( ) { }
能私有的都私有。
7、帮助文档的制作 (也称为API文档,Application Program Interface 应用程序接口)
在DOS命令行下输入:javadoc -d myhelp -author -version ArrayTool.java
javadoc是Java自带工具,用于生成帮助文档。
-d 表示directory目录的意思
myhelp 表示创建文件夹的名字(如果需要,可以在-d之后加上自定义目录,如:c:\program\)
-author 表示生成文档的同时生成作者
-version 表示生成文档的同时生成文档版本
ArrayTool.java 表示源文件
注意:需要在源文件的class类前加上public,保证权限足够,才能够创建帮助文档。private权限下的无法生成帮助文档。
一个类中默认会有一个空参数的构造函数,该构造函数权限和所属类一致,如果类被public修饰,那么默认的构造函数也
带public修饰符,如果类没有被public修饰,那么默认的构造函数也没有public修饰。简单来说,默认构造函数的权限是
随着类的变化而变化的。如果将类名private,则无法生成构造函数帮助文档。
示例如下:
/**
这是一个可以对数组进行操作的工具类,该类中提供了获取最值、排序等功能。
@author 张三 //author表示作者,可以从源文件中提取,写入帮助文档
@version V1.0 version表示版本,可以从源文件中提取,写入帮助文档
*/
函数前写
/**
@param 函数参数 参数描述
@return 返回值描述
*/
8、静态代码块
随着类的加载而加载,只在类加载的同时执行一次,并且优先于主函数执行。
static
{
执行语句。
}
注意:
只有用到类中的内容时,静态代码块才会执行,而StaticCode s = null,静态代码块不会执行,只建立引用,类不会加载。
静态代码块中不能使用this,构造代码块中可以使用this。
9、静态代码块、构造代码块和构造函数的区别
静态代码块:用于给类初始化,类加载时就会被加载执行,只加载一次,优先于构造代码块。
构造代码块:用于给对象初始化,只要建立对象该部分就会被执行,且优先于构造函数。
构造函数: 给对应对象初始化的,建立对象时,选择相应的构造函数初始化对象。
创建对象时,三者被加载执行顺序:静态代码块--->构造代码块--->构造函数
10、对象的初始化过程
Person p = new Person();
1、在栈内存中,开辟main函数的空间,建立main函数的变量p。
2、加载类文件:因为new要用到Person.class文件,所以要先从硬盘中找到Person.class类文件,并加载到内存当中。
加载类文件时,除了非静态成员变量(对象的特有属性)不会被加载,其他的都会被加载。
3、执行类中的静态代码块:如果有静态代码块的话,对Person.class类进行初始化。
4、开辟空间:在堆内存中开辟空间,分配内存地址。
5、默认初始化:在堆内存中建立对象的特有属性,并进行默认初始化。
6、显示初始化:对属性进行显式初始化(编写人员通过语句进行的初始化叫做显式初始化,比如自定义构造函数;而系统
默认的构造函数就是隐式初始化)
7、构造代码块:执行类中的构造代码块,对对象进行构造代码块初始化。
8、构造函数初始化:对对象进行对应的构造函数初始化。
9、将内存地址赋给栈内存中的变量p。
p.setName("lisi");
1、在栈内存中开辟setName方法的空间,里面有:对象的引用 this,临时变量name。
2、将p的地址赋给this,this就指向了堆中调用该方法的对象。
3、在传参时将"lisi"赋值给临时变量name。
4、将临时变量的值赋给this.name。
注意:要想使用类中的成员,必须通过类名或者this或者super调用。
11、单例设计模式
设计模式:解决某一类问题最行之有效的方法,Java中有23种设计模式:
单例设计模式:解决一个类在内存中只存在一个对象。
想要保证对象唯一:
1、为了避免其他程序过多建立该类对象,先禁止其他程序建立该类对象。
2、为了让其他程序可以访问到该类对象,可以在本类中自定义一个对象。
3、为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式。
以上三步代码体现方式:
1、将构造函数私有化(对应1)
2、在类中创建一个本类对象(对应2)
3、提供一个方法可以获取到该对象(对应3)
//饿汉式,这个是先初始化对象
class Single1
{
private Single(){};
private static Single s = new Single();
public static Single getInstance(); //如果想要调用成员变量或者成员函数,只能通过类名或者对象来实现
{ 因为没有对象,所以只能通过类名来调用,并且只能是static
return s;
}
}
//懒汉式,对象是方法被调用时才初始化,也叫做对象的延时加载。
class Single2
{
private Single(){ };
private static Single s = null;
public static getInstance()
{
if (null == s)
s = new Single();
return s;
}
}
class SingleDemo
{
public static void main(String[] args)
{
Single s1 = Single.getInstance(); //因为类变量是静态的,所以只能创建一个,无法重复创建,但是可以修改值
Single s2 = Single.getInstance();
}
}
懒汉式和饿汉式的区别:
饿汉式
Single类一进内存,就已经创建好了对象。定义单例,建议使用饿汉式。
懒汉式
Single类进内存,对象还没有存在,只有调用了getInstance方法时,才建立对象。
懒汉式在运行中容易出现差错,因为CPU来回切换运行程序时容易创建多个对象,为了避免此情况,可以加
入synchronized关键字,保证对象唯一。
/*
饿汉式-多线程
*/
class Single3
{
private Single(){};
private static Single s = null;
public static getInstance()
{
if(null == s)
{
synchronized(Single.class) //可以避免每次都判断是否上锁,提高程序运行效率
{
if(null == s)
{
s = new Single();
}
}
}
}
}
对于事物该怎么描述,还怎么描述,当需要将该事物的对象保证在内存中唯一时,就将以上的三步加上即可。
12、类的生命周期
在类使用完之后,如果满足下面的情况,类就会被卸载:
1、该类所有的实例(对象)都已经被回收,也就是Java堆中不存在该类的任何实例。
2、加载该类的ClassLoader(类加载器)已经被回收。
3、该类对应的java.langClass对象没有任何地方被引用,无法再任何地方通过反射访问该类的方法。
如果以上三个条件全部满足,JVM就会在方法区垃圾回收的时候对类进行卸载,类的卸载过程其实就是在方法区中清空类信息
,Java类的整个生命周期就结束了。
——继承
一个新类从已有的类那里获得其已有的属性和方法,这种现象叫类的继承。
这个新类被称为子类,也叫派生类,已有的那个类叫做父类,也叫做基类。
继承的好处:
代码得到极大的重用。
形成一种类的层次体系结构 。
为多态创造条件。
继承的实现方式:
class SubClass extends SuperClass // SuperClass是父类的意思
{
··········
}
利用继承可以较好地模拟出现实世界事物之间的联系
注意:Java只支持单继承,不支持多继承,多继承容易带来安全隐患(当父类中定义了相同功能时,创建子类对象时,无法确定执行
哪个代码),C++中支持多继承,但是Java中保留了这种机制,并用另一种体现形式来完成表示,叫做多实现。
Java支持多层继承:C继承B,B再继承A。
Java中也可以继承构造代码块和静态代码块,但是不能继承构造方法。
子类可以继承父类所有的成员变量和成员方法,但子类永远无法继承父类的构造方法。在子类的构造方法中可以使用语
句super(参数列表)来调用父类的构造方法。
当子类父类类成员名相同时,可以使用super来调用父类成员,否则默认使用this调用子类成员。private情况下无法访问父类成员。
1、子父类中变量的特点
如果子父类中出现非私有的同名成员变量时,子类要访问本类中的变量需要this关键字,访问父类中的同名变量需要super关键字。
2、子父类中的函数(重写,覆盖)
当子类出现和父类一模一样的函数时,子类对象调用该函数,会运行子类函数的内容,如同父类函数被覆盖一样,这种情况是函数
的另一个特性:重写(覆盖)。
当子类继承父类,并将父类的功能集成到子类中,但是子类虽具备该功能,而功能的内容却和父类不一致,这时,没有必要
定义新功能,而是使用覆盖特性,保留父类的功能定义,并重写该功能。
覆盖:
1、子类覆盖父类,必须保证子类权限大于等于父类权限,才可以覆盖。
2、静态只能覆盖(复写)静态,不能覆盖非静态(静态先加载)。
重写和重载的区别:
重载:只看同名函数的参数列表
重写:子父类方法要一模一样,包括返回值类型。
3、子父类中的构造函数
在对子类对象进行初始化时,父类的构造函数也会运行,那是因为子类的构造函数默认第一行有一条隐式的语句:super();
super();语句会访问父类中空参数的构造函数,而且子类中所有的构造函数默认的第一行都是super();
为什么子类一定要访问父类中的构造函数?
因为父类中的数据子类可以直接获取,所以子类对象在建立时需要先查看父类是如何对这些数据进行初始化的,所以子类在
对象初始化时,要先访问父类中的构造函数。如果要访问父类中指定的构造函数,可以通过手动定义super来指定。
如果父类中有private的成员,虽然子类不能访问,但是子类一定会继承过来并开辟物理内存来存储父类的private成员,所以会
默认调用一下父类的无参构造方法super();来访问父类中的私有成员,以便开辟内存。
注意:super语句一定定义在子类构造函数的第一行。
子类当中至少有一个构造方法去访问父类构造方法。
结论:子类的所有的构造函数,默认都会访问父类中空参数的构造函数,因为子类每一个构造函数内的第一行都有一句super();
当父类中没有空参数的构造函数时,子类必须手动通过super语句的形式来指定要访问的构造函数。
当然,子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数,子类中至少有一个构造函数会访问父类
中的构造函数。
私有不能被继承
私有物理存储上已经被继承,不过逻辑上程序员不能去访问它
因此继承必须慎重,否则会浪费内存
——继承权限问题(重点)
子类的所有方法内部都可以访问父类除私有成员以外的所有成员,所谓子类的所有方法也包括子类的私有方法。
通过子类对象名可以访问:
1、父类除私有成员外的所有成员。
2、子类本身的除私有成员外的所有成员。
例如:SupClass.Sub();
子类可以继承父类除私有成员外的所有成员。
父类的私有成员不可以被子类继承,其他的成员都可以被子类继承。
——类的继承举例:
/*
2015年6月2日21:52:21
本程序证明了:
1、子类内部可以访问父类非私有的成员
私有成员无法被子类方法访问
2、通过子类对象名只能访问从父类继承过来的非私有成员
总结:
*/
class A
{
public int i;
protected int j;
private int k;
public void g(){}
private void s(){}
protected void b(){}
}
class B extends A
{
private void f()
{
i = 10;
j = 20;
//k = 30;//error 私有属性不能被继承
g();
b();
//s();//error 私有的方法不能被继承
}
public void f()
{
i = 10;
j = 20;
//k = 30;//error 私有属性不能被继承
g();
b();
//s();//error 私有的方法不能被继承
}
}
class M
{
public static void main(String[] args)
{
B bb = new B();
bb.i = 20;
bb.j = 30;
bb.b();
bb.g();
//bb.s();通过子类对象名只能访问从父类继承过来的非私有成员
}
}
——不同访问修饰符
——final 关键字
1、final可以修饰类,方法,变量。
2、final修饰的类不可以被继承。用final可以避免被继承,避免被子类复写函数。
3、final修饰的方法不可以被覆盖(复写)。
4、final修饰的变量只能被赋值一次。既可以修饰成员变量,也可以修饰局部变量。
当描述事物时,一些数据只是固定的,比如π,那么这时为了增强阅读性,都给这些值起个名字,方便阅读,而这个值不需要改变
,所以加上final修饰,作为常量:常量的书写规范所有字母都大写,如果有多个单词组成,单词间通过下划线连接。
5、内部类只能访问被final修饰的局部变量。
——抽象类:(只能修饰类方法)
当多个类中出现相同功能,但是功能主体不同,这时可以进行向上抽取,但是只抽取功能定义,而不抽取功能主体。
抽象方法必须存放在抽象类中。
抽象类的特点:
1、抽象方法一定定义在抽象类中。
2、抽象方法和抽象类都必须被abstract关键字修饰。
3、抽象类不可以用new创建对象,因为调用抽象方法没意义。
4、抽象类中的方法要被调用,必须由子类复写其所有的抽象方法后,再建立子类对象调用
如果子类对象只覆盖了部分抽象方法,那么该子类还是一个抽象类。
5、抽象类中可以有抽象方法,也可以有非抽象方法。
6、构造方法和静态方法不能抽象。
抽象类和一般类没有太大的不同,该如何描述事物就如何描述事物,只不过该事物内部有一些看不懂的东西,这些不确定的部分,也
是该事物的功能,要明确定义函数,但是无法定义函数主体,则需要子类对象去按需定义。
抽象类比一般函数多了个抽象函数,就是在类中可以定义抽象方法。
抽象类不可以实例化,必须通过子类来实例化。
特殊:
抽象类中可以不定义抽象方法,这样做仅仅是不让该类创建对象。
abstract class Employee
{
public String name;
public String id;
public double pay;
public abstract void work();
Employee(String name,String id,double pay)
{
this.name = name;
this.id = id;
this.pay = pay;
}
}
class Manager extends Employee
{
double bonus;
Manager(String name,String id, double pay)
{
super(name,id,pay);
this.bonus = bonus;
}
public void work()
{
System.out.println("Manager.name = "+this.name + "\nManager.id = " + this.id + "\nManager.pay = "+ this.pay + "\nManager.bonus = " +this.bonus);
}
}
class Profession extends Employee
{
Profession(String name,String id,double pay)
{
super(name,id,pay);
}
public void work()
{
System.out.println("Profession");
}
}
class AbstractTest
{
public static void main(String[] args)
{
Manager m = new Manager("zhangsan","研发部01",9800);
m.bonus = 5000f;
m.work();
}
}
——接口
接口和抽象类的父类引用可以接收子类对象。
接口的目的:
避免单继承的局限性:
可以解决类的单一继承问题。
接口之间可以实现多继承。
接口之间可以多实现。
解耦合
电脑上设置USB接口。
可以做到随时更换。
接口回调
在团队开发时,先写好接口。
其他类可以调用接口的方法,哪怕还没有完全实现。
指定标准和规范
具有指定的属性值
必须实现指定的方法。
格式:
interface aa{ }
接口中成员修饰符是固定的(接口中的成员都是public)
成员常量:
public static final int x = 3;
成员函数:
public abstract void show();
接口的出现将“多继承”通过另一种形式体现出来,即多实现。
注意:子类要将接口中的所有成员全都实例化才能实现。
接口方法不能有主体。
接口是不可以创建对象的,因为接口中包含抽象方法,需要被子类实现,子类对接口中的抽象方法全都覆盖后,才可以实例化,否则
子类还是一个抽象类。
接口可以被类多实现:
一个类可以同时实现多个接口,一个接口可以被类多实现,也是对多继承不支持的转换形式,Java支持多实现。
一个类在继承一个类的同时还能实现多个接口。
Java支持多继承,只有接口和接口之间支持多继承。
——接口的特点
接口中的所有方法都是抽象的、public的,但是可以不加修饰符,因为只要是interface,就默认强制abstract、public。
接口中的所有属性都是public static final,可以省略不写。
1、接口是对外暴露的规则。
可以通过接口访问类。
2、接口是程序的功能扩展。
将功能单独封装,在类中implements接口。
3、接口可以用来多实现。
降低类之间的耦合性。
4、类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口。
5、接口与接口之间可以有继承关系。
6、接口中可以定义静态属性,但是不能定义静态方法。
——多态
多态自始至终都是子类对象在做变化。
定义:
可以理解为事物存在的多种体现形态。
某一类事物的多种存在形式,例如猫是猫科动物,同时猫也可以成为动物。
1、多态的体现
父类的引用指向了自己的子类对象。(父类的引用也可以接收自己的子类对象)
2、多态的前提
必须是类与类之间有关系,要么是继承要么是实现。
通常还有一个前提,就是覆盖
3、多态的好处
多态的出现大大的提高了程序的扩展性。
后期再出现对象,方法不需要修改,可以直接执行。
4、多态的弊端
提高了扩展性,但是只能使用父类的引用访问父类中的成员。
5、多态的应用
6、多态的出现代码中的特点(多态使用的注意事项)
在多态中(当父类指向子类对象时)成员函数(非静态,可重写)的特点:
在编译时期:参阅引用所属的类中是否有调用的方法,如果有则编译通过,如果没有编译失败。
在运行时期:参阅对象所属类中是否有调用的方法。
Fu f = new Zi();
编译时查看Fu中有无调用的方法,运行时则查看new Zi()中有无调用的方法。
如果子类对象有该方法,就运行子类对象的,如果没有就运行父类的。 (其实就是子类的,因为子类已经继承了父类)。
简单总结就是:
成员函数在多态调用时,编译看左边,运行看右边。
在多态中成员变量的特点:
无论编译和运行,都参考左边(引用型变量所属的类)。
在多态中,静态成员函数的特点:
无论编译和运行,都参考左边。
多态调用情况:
当调用成员变量时,默认调用父类变量。
当调用成员方法时,默认调用子类方法。
7、类型转换
Animal a = new Cat(); //类型提升,向上转型。
此时a引用中只有Animal中的成员方法,并无Cat中的成员方法,如果想要使用父类的引用调用Cat中的成员方法,需要类型转换,向下转型:
Cat c = (Cat)a; //类型转换。
此时将Animal的引用a转换为Cat类型的引用c,c中既包含Animal的方法也包含子类Cat中的特有方法。
千万不要将父类对象转换成子类类型。Cat c = (Cat)a;转换的其实是父类的引用。
我们能转换的是父类的引用,当父类引用指向了自己的子类对象时,该引用可以被提升,也可以被强制转换。
多态自始至终都是子类对象在变化。
多态示例程序:
(1)
abstract class Animal
{
public abstract void eat();
}
class Dog extends Animal
{
public void eat()
{
System.out.println("dog eatting~~~~~~~");
}
}
class Cat extends Animal
{
public void eat()
{
System.out.println("cat eatting~~~~~~~");
}
public void act()
{
System.out.println("爬树");
}
}
class Pig extends Animal
{
public void eat()
{
System.out.println("pig eatting~~~~~~");
}
}
public class DuotaiDemo {
public static void main(String[] args) {
Dog d = new Dog();
d.eat();
Cat c = new Cat();
c.eat();
function(new Dog());
function(new Cat());
function(new Pig());
Animal a = new Cat();//类型提升,向上转型,转成父类型。
a.eat();
//如果想要调用猫的特有方法时,如何操作?
//强制将父类的引用,转成子类类型,向下转型。千万不要将父类对象转成子类类型。
//当父类引用指向了自己的子类对象时,该引用可以被提升,也可以被转换。
//多态自始至终都是子类对象在变化。
Cat cat = (Cat)a;
cat.act();
}
public static void function(Dog d){
d.eat();
}
public static void function(Cat c){
c.eat();
}
public static void function(Animal a){
a.eat();
}
}
(2)
abstract class Student
{
public abstract void study();
public void sleep()
{
System.out.println("Sleeping~~~~~~~");
}
}
class Daxue extends Student
{
public void study()
{
System.out.println("大学课程");
}
}
class Gaozhong extends Student
{
public void study()
{
System.out.println("高中课程");
}
public void sleep()
{
System.out.println("晚睡早起");
}
}
public class DuotaiDemo2 {
public static void main(String[] args) {
Operate o = new Operate();
o.student(new Daxue());
o.student(new Gaozhong());
}
}
class Operate
{
public void student(Student st)
{
st.study();
st.sleep();
}
}
(3)多态的主板示例
interface PCI
{
public void open();
public void close();
}
class mainBoard
{
public void run()
{
System.out.println("mainboard running!");
}
public void usePCI(PCI p)
{
p.open();
p.close();
}
}
class netCard implements PCI
{
public void open()
{
System.out.println("netcard running!");
}
public void close()
{
System.out.println("netcard closed!");
}
}
class soundCard implements PCI
{
public void open()
{
System.out.println("soundcard running!");
}
public void close()
{
System.out.println("soundcard closed!");
}
}
public class DuotaiDemo4 {
public static void main(String[] args)
{
mainBoard m = new mainBoard();
m.run();
m.usePCI(new netCard());
m.usePCI(new soundCard());
}
}
——Object类__equals()
1、Object类是所有对象的直接或者间接父类,该类中定义的肯定是所有对象都具备的功能。
2、equals()比较的是堆中两个对象的内容,而“==”比较的是栈中两个对象的引用(内存空间)。
class Demo
{
int num;
Demo(int num)
{
this.num = num;
}
public boolean equals(Object obj)
{
if(!(obj instanceof Demo)) //如果用到对象中特有数据,就需要判断和转换的动作。
return false;
Demo d = (Demo)obj;
return this.num == d.num;
}
}
public class equals {
public static void main(String[] args)
{
Demo d1 = new Demo(2);
Demo d2 = new Demo(2);
System.out.println(d1.equals(d2));
}
}
3、toString方法返回的是任意对象的所属类和哈希值。
Student st = new Student();
System.out.println("st.toString() = " + st.toString());
输出
st.toString() = [email protected]
4、类自带的equals()和toString()方法大多无意义,一般会通过复写方法自定义方法内容。
——内部类
1、将一个类定义在另一个类的里面,一个类里面那个类就称为内部类(内置类,嵌套类)。
2、访问特点
(1)内部类可以直接访问外部类中的成员,包括私有成员。
(2)而外部类要访问内部类中的成员必须要建立内部类的对象。
(3)想要在主函数中创建某内部类对象,格式为Outer.Inner in = new Outer().new Inner();
(4)之所以可以直接访问外部类中的成员,是因为内部类中有默认的一个外部类引用(外部类名.this),格式为外部类名.this.内
部类成员。
当外部类、内部类和内部类成员方法的局部变量同名时,默认调用局部变量,如果想要调用内部类变量可以使用this,调用外部
类变量使用 外部类名.this.变量(使用外部类的引用来调用外部类的变量)
package it.wang;
class Outer
{
int x = 3;
public void method()
{
System.out.println("Outer x = " + x);
}
class Inner
{
int x = 5;
void method2()
{
System.out.println("Inner x = " + Outer.this.x);
System.out.println("Inner x = " + x);
}
}
}
public class Neibulei {
public static void main(String[] args)
{
Outer out = new Outer();
out.method();
Outer.Inner in2 = new Outer().new Inner();
in2.method2();
}
}
访问格式:
1、当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类中直接建立内部对象。非静态,没对象,不运行。
格式
外部类名.内部类名 变量名 = 外部类对象.内部类对象;
Outer.Inner in = new Outer().new Inner();
2、当内部类被定义在外部类成员位置上,就可以被成员修饰符所修饰。
比如private:将内部类在外部类中进行封装。
static:内部类就具备静态的特性。
当内部类被static修饰后,只能直接访问外部类中的static成员,出现了访问局限性。
3、在外部其他类中,如何直接访问static内部类的非静态成员呢?
new Outer.Inner().function();
4、在外部其他类中,如何直接访问static内部类的静态成员呢?
Outer.Inner.function();
注意:
当内部类中定义了静态成员,该内部类必须是static的。
当外部类中的静态方法访问内部类时,内部类也必须是static的。
内部类定义在局部时:
(1)不可以被成员修饰符修饰。
(2)可以直接访问外部类中的成员,因为内部类中还持有外部类中的引用。
但是不可以访问它所在的局部方法中的变量,只能访问被final修饰的局部变量。
package it.wang;
class Outer
{
int x = 3;
public void method()
{
System.out.println("Outer x = " + x);
}
class Inner
{
int x = 5;
void method2()
{
System.out.println("Inner x = " + Outer.this.x);
System.out.println("Inner x = " + x);
}
}
static class Inner2
{
static void function()
{
System.out.println("Inner2");
}
}
}
public class Neibulei {
public static void main(String[] args)
{
Outer out = new Outer();
out.method();
Outer.Inner in2 = new Outer().new Inner();
in2.method2();
Outer.Inner2.function();
}
}
3、匿名内部类
(1)匿名内部类其实就是内部类的简写格式。
(2)定义匿名内部类的前提:内部类必须继承一个类或者实现接口。(因为需要用父类来创建内部类子类对象)
(3)匿名内部类的格式:new 父类或者接口( ) { 定义子类的内容 }
内部类创建对象:
new Inner().show();
转换为匿名内部类就是:
new AbsDemo() //因为没有了内部类,所以直接使用父类来创建对象
{
void show(){} //复写父类方法,
}.show(); //调用子类复写过的方法
红色:使用父类创建了一个匿名内部类对象并复写了父类方法。
蓝色:调用子类复写父类的方法。
(4)其实匿名内部类就是一个匿名子类对象。(把定义类和封装对象定义为一体的表现形式)
注意:
(1)匿名内部类中可以创建自己的成员。
(2)匿名对象对方法只能调用一次(只能同时调用一个方法)。
(3)在局部创建匿名内部类的对象时,格式为:父类名 对象名 = new 父类或者接口名( ) { 定义子类的内容 };
当父类中没有定义方法,则父类引用不能调用该方法。
需求:给内部类建立一个引用,可以多次调用内部类方法(父类方法和特有方法)。
AbsDemo d = new AbsDemo()
{
void show() //继承自父类的方法并复写
{
System.out.println("hello");
}
void run() //子类特有方法
{
System.out.println("world");
}
}
这样使用父类引用创建内部类的子类对象,就是多态。
这样定义的方式不能再最后加.show()来调用show方法。
当出现多态情况时,使用父类引用只能调用父类方法,不能使用父类引用调用子类方法。
(4)匿名内部类中的方法不要超过三个,否则影响阅读性。
——异常机制
1、异常:就是程序在运行时出现不正常情况。
2、异常的体系
Throwable
Error
通常出现重大问题,如:运行的类不存在或者内存溢出等。
Error类对象由Java虚拟机生成并抛出,程序无法捕获。
Exception
在运行时出现的一些情况,可以通过try catch finally
Exception类对象由应用程序处理或抛出。
Exception和Error的子类名都是以父类名作为后缀。
3、异常由来
问题也是现实生活中一个具体的事物,也可以通过Java的类的形式进行描述,其实就是Java对不正常情况进行描述后的对象的
体现。
4、对于问题的划分:
严重:
Java通过Error类进行描述。
对于Error一般不编写针对性的代码对其进行处理。
一般:
Java通过Exception类进行描述。
对于Exception可以使用针对性的处理方式进行处理。
5、异常的处理
Java提供了特有的语句进行处理。
try
{ 需要被检测的代码; }
catch(异常类 对象)
{ 处理异常的代码:(处理方式) }
finally
{ 一定会执行的语句; }
6、对捕获到的异常对象进行常见方法操作。
String getMessage(); // 返回异常信息
void printStackTrace(); // 打印异常在堆栈中的跟踪信息,JVM默认调用该方法。
String toString(); 输出异常名称:异常信息。
异常抛出:try_catch
class Jisuan
{
public int div(int a,int b)
{
return a/b;
}
}
public class Try_catch {
public static void main(String[] args)
{
Jisuan j = new Jisuan();
try
{
int a = j.div(8, 0);
System.out.println("j.div(8.4) = " + a);
}
catch(Exception ex) //Exception ex = new ArithmeticException();实际上就是多态,父类引用调用子类对象,所以ex可以使用父
类中的方法
{
System.out.println("不能除零");
System.out.println(ex.getMessage()); // 输出异常信息
System.out.println(ex.toString()); // 打印异常对象的字符串表现形式,在输出语句中打印对象,toSting可写可
不写。 输出 异常名称:异常信息
ex.printStackTrace(); // 无返回值,不能放在输出语句中 输出 异常名称,异常信息,异常出现的位置。
其实JVM默认的异常处理机制,就是在调用printStackTrace方法,打印异常在堆栈中个跟踪信息。
}
System.out.println("exit");
}
}
程序执行时,运行到int a = j.div(8.0);,跳转到Jisuan类中的div方法中,执行a/b时发现b为0,此时调用ArithmeticException()方法,通
过new ArithmeticException()将div方法封装成对象,传送到catch(Exception ex)中,
相当于Exception ex = new ArichmeticException(),然后执行catch内部命令,则异常处理完毕,程序继续向下知行。
7、异常声明
在方法上声明异常,便于提高安全性(因为方法可能有问题),让调用处进行try、catch或者抛出异常等处理,不处理则编译失败(不处理不让用)
8、对多异常的处理
1、声明异常时,建议声明更为具体的异常,这样处理的可以更具体。
2、函数处声明几个异常,调用处就要对应有几个catch代码块,并且catch不会同时发生,一旦出现一个异常,就会返回处理相对
应异常的catch。
可以使用Exception来处理所有异常,但是Exception处理异常没有针对性,最好不要写Exception,因为如果出现了预料之
外的异常,被Exception处理之后,程序员并不知道发生了什么异常。如果多个catch代码块中的异常出现继承关系,父类异
常catch代码块放在最后。
3、在进行catch处理时,catch中一定要定义具体处理方式,不要简单定义一句ex.printStackTrace();,也不要简单的就写一条输出
语句。用一个异常日志文件保存。
package Yichang;
class Jisuan
{
public int div(int a,int b) throws ArithmeticException,ArrayIndexOutOfBoundsException
{ //在方法上通过throws声明该方法可能会出现的问题。
int arr [] = new int[a];
System.out.println(arr[4]);
return a/b;
}
}
public class ExceptionDemo1 {
public static void main(String[] args)
{
Jisuan j = new Jisuan();
try
{
int a = j.div(4, 1);
System.out.println(a);
}
catch(ArithmeticException ex)
{
System.out.println(ex.toString());
System.out.println("不能除零!");
}
catch(ArrayIndexOutOfBoundsException ex)
{
System.out.println(ex.toString());
System.out.println("角标越界!");
}
catch(Exception ex)
{
System.out.println(ex.toString());
}
System.out.println("exit");
}
}
9、自定义异常
自定义异常需要继承Exception父类异常,否则该类和异常体系无关。
在方法中使用throw new一个对象抛出异常时需要在方法上使用throws声明异常。
定义自定义异常类时,可以在类中定义一个message字符串,throw异常时使用构造方法来初始化message,再在catch中使
用getMessage方法来获取信息,因为父类Throwable中已经定义了String message,所以不必在子类中定义Message了。
(1)因为项目中会出现某些特有的问题,而这些问题并未被Java所描述并封装对象,所以对于这些特有的问题可以按照Java对于
异常问题封装的思想,将特有的问题进行自定义的异常封装。
(2)当在函数内部出现了throw抛出异常对象,要么就必须要给出对应的处理动作,要么在函数内部try_catch处理,要么在函数上
声明让调用者处理。
一般情况下,函数内出现异常,函数上需要声明。
(3)如何定义异常信息
class Throwable{
private String message;
Throwable(String message){
this.message = message;
}
public String getMessage(){
return message;
}
}
class Exception extends Throwable{
Exception(String message){
super(message);
}
}
因为父类(Throwable)中已经把异常信息的操作都完成了,所以子类(Exception)只要在调用构造方法时,将异常信息
用super传递给父类,就可以直接通过getMessage方法获取自定义的异常信息了。
(4)自定义异常必须是自定义类继承Exception,如果不继承Exception,那么该自定义类就和异常无关。
(5)为什么继承Exception而不继承Throwable原因
异常体系有一个特点:异常类和异常对象都必须被抛出,因为它们都具备可抛性,这个可抛性是Throwable体系中的独有特点
,只有这个体系中的类和对象才可以被throws和throw操作。
(6)throws和throw的区别
throws使用在函数上,throw使用在函数内。
throws后面跟的是异常类,可以跟多个,用逗号隔开,throw后面跟的是异常对象。
class ZidingyiException extends Exception //getMessage();
{
/*
ZidingyiException(String msg)
{
super(msg);
}
*/
private String msg;
private int value;
ZidingyiException(String msg,int value)
{
this.msg = msg;
this.value = value;
}
public String getMessage()
{
return msg;
}
public int getValue()
{
return value;
}
}
class Jisuan
{
public int div(int a,int b) throws ZidingyiException
{
if(b < 0)
throw new ZidingyiException("被除数不能为负数",b);//手动通过throw关键字抛出一个自定义异常。
return a/b;
}
}
public class ThrowsDemo {
public static void main(String[] args) throws ZidingyiException
{
Jisuan j = new Jisuan();
try
{
int x = j.div(4,-1);
System.out.println(x);
}
catch (ZidingyiException ex)
{
System.out.println(ex.toString());
System.out.println(ex.getMessage());
System.out.println("错误的数字是:"+ ex.getValue());
}
}
}
10、RuntimeException
(1)Exception中有一个特殊的子类异常RuntimeException,运行时异常。
(2)如果在函数内抛出该异常,函数上可以不用声明,编译一样通过。
如果在函数上声明了该异常,调用者可以不用进行处理,编译一样通过。
(3)之所以不用再函数内声明,是因为不需要让调用者处理,当该异常发生,程序无法继续执行,希望程序停止后对代码进行修正
自定义异常时,如果该异常的发生导致程序无法继续执行,就让自定义异常继承RuntimeException。
RuntimeException以及RuntimeException的子类如果在函数内抛出,函数上不用声明。
(4)对于异常分两种:
1、编译时被检测的异常。
2、编译时不被检测的异常(运行时异常,RuntimeException及其子类)。
(5)编译时编译器默认判断(a instanceof RuntimeException)
11、finally关键字
try
{
连接数据库;
数据操作;
}
catch(SQLException ex)
{
对数据库进行异常处理;
}
finally
{
关闭数据库连接;
}
注意:catch用于处理异常,如果没有catch就代表异常没有被处理过,如果该异常是检测时异常,那么必须声明。
12、子父类覆盖时的异常特点
(1)子类在覆盖父类时,如果父类的方法抛出异常,那么子类覆盖的方法,只能抛出父类的异常或者该异常的子类。
(2)如果父类方法抛出多个异常,那么子类在覆盖该方法时,只能抛出父类异常的子集。
(3)如果父类或者接口的方法中没有抛出异常,那么子类在覆盖方法时,也不能抛出异常;如果子类方法发生了异常,则必须要
进行try处理,绝对不能抛出异常。
简单来说:
子类只能抛父类所抛过的异常,父类如果没有抛异常,子类也不能抛。
package Yichang;
class AException
{
}
class BException extends AException
{
}
class CException
{
}
class Fu
{
public void show()throws AException
{
}
}
class Zi extends Fu
{
public void show()throws BException
}
public class Zifuleiyichang {
public static void main(String[] args)
{
System.out.println("hello world");
}
}
13、异常总结
(1)异常是什么
异常是对问题的描述,将问题进行对象的封装。
(2)异常体系
Throwable
|----Error
|----Exception
|----RuntimeException
异常体系的特点:
异常体系中的所有类以及建立的对象都具备可抛性,也就是说可以被throw和throws关键字所操作,只有异常体系具备这个特点
(3)throw和throws的用法:
throw定义在函数内,用于抛出异常。
throws定义在函数上,用于抛出异常类,可以抛出多个异常类,用逗号隔开。
当函数内有throw抛出异常对象,并未进行try处理,必须要在函数上声明,否则编译失败。
注意:RuntimeException除外,也就是说,函数内如果抛出的RuntimeException异常,函数上可以不声明。
(4)异常有两种
编译时被检测异常
该异常在编译时,如果没有处理(没有throw也没有try),编译失败。
该异常被标识,代表这可以被处理。
运行时异常(编译时不检测)
在编译时,不需要处理,编译器不检查。
该异常的发生,建议不处理,让程序停止,需要对代码进行修正。
(5)异常处理语句
try
{
需要被检测的代码;
}
catch()
{
处理异常的代码;
}
finally
{
一定会执行的代码;
}
注意:
1、finally中定义的通常是关闭资源代码,因为资源必须释放。
2、finally只有一种情况不会执行,当执行到System.exit(0);程序退出,finally不会执行。
(6)自定义异常
定义类继承Exception或者RuntimeException
1、为了让该自定义类具备可抛性。
2、让该类具备操作异常的共性方法
当要定义自定义异常的信息时,可以使用父类已经定义好的功能,异常信息传递给父类的构造函数。
class MyException extends Exception
{
MyException(String message)
{
super(message);
}
}
自定义异常:按照Java的面向对象思想,将程序中出现的特有问题进行封装。
(7)异常的好处
1、将问题进行封装。
2、将正常流程代码和问题处理代码相分离,方便于阅读。
(8)异常的处理原则
1、处理方式有两种:try或者throw
2、调用到抛出异常的功能时,抛出几个就处理几个,一个try对应多个catch。
3、有多个catch时,父类的catch放到最下面。
4、catch内,需要定义针对性的处理方式,不要简单地定义printStackTrace输出语句。也不要不写。
5、当捕获到的异常本功能处理不了时,可以继续在catch中抛出。
6、Java允许throws处理异常,最终抛给虚拟机处理,虚拟机发现异常后会终止程序运行。
7、特殊情况下会做throws处理,将底层异常抛到上一层集中处理。
try
{
throw new AException();
}
catch (AException ex)
{
throw ex;
}
如果该异常处理不了,并且不属于该功能出现的异常,可以将异常转换后,再抛出和该功能相关的异常。
try
{
throw new AException();
}
catch (AException ex)
{
//或者先对AException进行处理,然后再抛出BException();
throw new BException();
}
异常的注意事项:
在子父类覆盖时
1、子类抛出的异常必须是父类异常的子类或者子集。
2、如果父类或者接口没有异常抛出时,子类覆盖出现异常,只能try不能抛。
——包package
1、对文件进行分类管理。
2、给类提供多层命名空间。
3、写在程序文件的第一行。
4、类名的全称是 包名.类名
5、包也是一种封装形式。
6、包名所有字母小写
7、包与包之间进行访问,被访问的包中的类以及类中的成员,需要public修饰。
8、不同包中的子类还可以直接访问父类中被protected权限修饰的成员。包与包之间的权限只有两种,public和protected
javac -d . PackageDemo.java 指定包所存放的目录;. 表示当前目录。
javac -d g:\Java program PackageDemo.java
将包路径指定为g:\Java program之后需要设置环境变量:set classpath=g:\Java program
java pack.PackageDemo 运行pack包中的PackageDemo类文件。
包的概念
包是Java提供的一中区别类名的名字及空间的机制,是包的组织方式,
是一组相关类和接口的集合,它提供了访问权限和命名的管理机制。
包的命名规范
一个唯一包名的前缀总是全部小写的ASCII码字母并且是一个顶级域名,通常是con、edu gov mil net org
或是1981年ISO 3166标准所指定的标识国家的英文双字符代码。包的后续部分根据不同机构各自内部的命名规范而不尽相同。
这类命名规范可能以特定目录名的组成来区分。
com.sse.edu.po
po:简单对象
Course
Manager
Student
Teacher
com.sse.edu.dao
dao:操作包
StudentDao
包在磁盘上的表现形式是文件目录
F:\工作室\Java Project\HomeWork\src\ArrayEmpSystem
实际的类要带有完整包名
包的应用
声明包
在源文件的开始加上:package 包名(要求全部小写,反域名制)
理解:
包在文件中的表现就是文件夹目录结构,功能相似的类放在同一目录下。
对乐力进行了包装,在不同的包中允许有相同类名存在。
在IDE工具下声明包:
在eclipse中通常先创建package,然后在包中创建类。
对大型项目开发时,通常是先建package,组织项目文件结构,然后再创建类。
使用包:
如果当前类要用到其他包中的类,需要使用import关键字来导入
语法:import java.util.*;
调用包
常用包
java.lang
包含了JAVA语言程序设计的基础类
lang是language的简写。
java.awt(抽象窗口工具类)
包含用于创建图形用户界面和绘制图形图像的相关类
awt是抽象窗口工具集abstract window toolkit
java.util
包含集合 日期 国际化 和各种实用工具类
utility
java.io
包含可提供数据输入/输出相关功能的类
java.net
提供用于实现Java网络编程相关类。
java.sql
提供数据库操作相关功能类
Struct query language
访问控制符
使用访问修饰符的原因
信息隐藏式OOP最重要的的功能之一,也是使用访问修饰符的原因
隐藏内部实现细节(private)提供对外访问接口(public)
同一个类同一个包内的类不同包内的子类不同包并且不是子类
private 是 否 否 否
只允许本类访问。
default 是 是 否 否
同类可以,同包可以,跨包无法访问。
protected 是 是 是 否
不同包中并且没有继承的类无法访问。
public 是 是 是 是
权限最大。
——jar包