1.包的定义与使用
包的本质实际上就是一个文件夹,在项目开发中很难避免类名称重复问题,如果所有的java文件都放在一个文件夹,就有可能出现覆盖问题。
1.1.包的定义
在java文件首行使用package 包名称
即可定义。包定义示例:
package www.xpu.java;
public class Code{
public static void main(String[] args) {
System.out.println("Hello");
}
}
在项目开发中一定要定义包,一旦程序出现包名,那么*.class必须存放在相应目录下。打包编译命令:javac -d.类.java
- -d:表示生成的目录,根据package的定义生成
- “.”:表示在当前所在目录生成目录
eg:javac www.xpu.java.Code
1.2.包的导入
开发中使用包的定义后,相当于把一个大的项目按照一定要求保存在了不同包中,但是这些程序一定会出现相互调用的情况,这时候就需要包的导入。
范例:编写简单的Java类,本类需要被其他的Java类调用
package xpu.com.java;
public class Message {
public void print(){
System.out.println("[Message] Hello Package");
}
}
范例:导入包
package xpu.com.test;
//导入包
import xpu.com.java.Message;
public class Demo {
public static void main(String[] args) {
Message message = new Message();
message.print();
}
}
从正常角度来讲,Demo类引用了Message类,那么首先编译的应该是Message类再是Demo类。最常用的打包编译
命令为:javac -d ./*.java
。
!!!注意
:class 和public class的区别
public class
:类名必须与文件名成保持一致,如果希望一个类被其他包访问,则必须定义为public class(default) class
:类名可以和文件名称不一致,在一个*.java中可以定义多个class,但是这个类除了相同包以外不允许被其他包访问
需要注意的是,以上导入的语句"import 包.类"这样只会导入一个类,如果现在要导入一个包中多各类,可以直接采用通配符"*"来完成
import xpu.com.java.*;
通配符"*"并不表示把所有类都进行导入,而是只导入你需要的部分
。
不通包的相同类情况
如果一个类现在需要同时导入不通包的相同类,那么调用该类时会出现编译的歧义,此时我们采用全名称定义
,如下:xpu.com.java.Message msg = new xpu.com.java.Message();
1.3.系统常用包
- java.lang:系统常用基础类(String、Object),此包从JDK1.1后自动导入
- java.lang.reflect:java反射编程包
- java.net:进行网络编程的开发包
- java.sql:进行数据库开发的支持包
- java.util:Java通过的工具程序包(集合类等)
- java.io:IO编程开发包
1.4.访问控制权限
java中有四种访问控制权限:private<default<protected<public,定义如下:
No. | 范围 | private | default | protected | public |
---|---|---|---|---|---|
1 | 同一包中同一类 | √ | √ | √ | √ |
2 | 同一包中不同类 | √ | √ | √ | |
3 | 不同包中的子类 | √ | √ | ||
4 | 不同包中非子类 | √ |
在不同的包中,只有子类能访问父类的protected权限
权限总结:
- 对于封装的描述90%使用private,只有10%会使用protected,这两个都叫封装
- 属性都使用private,方法都使用public
封装性就是private、default和protected三种权限的使用
1.5.jar命令
jar本质也是一种压缩文件,里面保存的都是.class文件,也就是说现在要实现某一个功能模块,可能有几百个类,最终交付给用户使用时,为了方便管理,就会将这些文件形成压缩包提供给客户。
jar命令有如下几个参数:
- “c”:创建新档案
- “f”:指定档案文件名
- “v”:在标准输出中生成详细输出
编译源文件并打包示例:
public class Message {
public void print(){
System.out.println("[Message] Hello Package");
}
}
对上述源文件编译而后变为jar文件:
- 打包进行程序编译:javac -d.Message.java
- 将生成的程序类打包为jar文件:jar -cvf Message.jar Message.class
打开后就发现有一个META-INF文件夹,里面包含版本号等信息,此时的Message.jar就是我们需要的程序类。要想使用jar文件并不是将其放到程序目录中就可以,还要配合CLASSPATH设置jar的加载路劲才会起效
。
2.单例设计模式&多例设计模式
2.1.单例设计模式
所谓单例设计指的是一个类只允许产生一个实例化对象
。
一段简单程序:
class Singleton{
public void print(){
System.out.println("Hello world");
}
}
public class SingletonTest {
public static void main(String[] args) {
Singleton singleton = null;//声明对象
singleton = new Singleton();//实例化对象
singleton.print();
}
}
以上程序在进行对象实例化的时候调用了Singleton的无参构造。
class Singleton{
private Singleton(){}//private声明构造
public void print(){
System.out.println("Hello world");
}
}
这时候类中已经明确的提供了一个私有的构造方法,那么默认生成的无参构造不在产生,此时进行对象实例化的时候一定会有错误。一旦构造方法被私有化了,就表示外部无法产生新的实例化对象,此时的类相对而言是一个封闭状态。如果此时还想调用Singleton类的print()方法,那么必须提供实例化对象。考虑到封装的特点,可以在类的内部产生一个实例化对象:
class Singleton{
//在类的内部可以访问私有结构,所以可以在类的内部产生实例化对象
Singleton instance = new Singleton();
private Singleton(){}//private声明构造方法
public void print(){
System.out.println("我是小可爱");
}
}
现在Singleton内部的instance对象(属性)是一个普通属性,所有的普通属性必须在有实例化对象的时候才能进行内存空间分配,而现在外部无法产生实例化对象,所以应该想办法在Singleton没有实例化对象的时候也可以将instance使用,所以我们想到了static关键字,使用static产生实例化对象:
class Singleton{
//在类的内部可以访问私有结构,所以可以在类的内部产生实例化对象
static Singleton instance = new Singleton();
private Singleton(){}//private声明构造方法
public void print(){
System.out.println("我是小可爱");
}
}
public class SingletonTest{
public static void main(String[] args) {
Singleton singleton = null;//声明对象
singleton = Singleton.instance;
singleton.print();
}
}
//我是小可爱
以上虽然可以取得Singleton类的实例化对象,但是对于类中属性应该使用private进行封装,要想取得private属性应该提供getter()方法,由于此时访问的是static属性,并且这个类无法在外部提供实例化对象,一次应该提供一个static的getter()方法,因此static方法不受对象实例化控制,static的getter()方法:
class Singleton{
//在类的内部可以访问私有结构,所以可以在类的内部产生实例化对象
private static Singleton instance = new Singleton();
//私有化构造函数
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
public void print(){
System.out.println("我是小可爱");
}
}
public class SingletonTest{
public static void main(String[] args) {
Singleton singleton = null;//声明对象
singleton = Singleton.getInstance();
singleton.print();
}
}
//我是小可爱
做了如上这么多其实就是为了使类只产生一个唯一的单例,单例设计模式分为两种:
懒汉式单例
(用时再new):存在线程安全问题
//懒汉式单例(用时再new)
class Singleton{
private static Singleton singleton;
private Singleton(){}
public static Singleton getInstance(){
if(singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
饿汉式单例
:上来就new
//饿汉式单例(上来就new)
class Singleton{
private final static Singleton SINGLETON = new Singleton();
private Singleton(){}
public static Singleton getSingleton(){
return SINGLETON;
}
}
2.2.多例设计模式(现在使用枚举取代)
比如描述一周数的类,描述性别的类,这些类产生对象个数固定,称为多例模式。所谓的多例模式只是比单例模式多了些内部实例化对象而已
。
定义一个表示性别的多例类,示例如下:
class Sex{
private String title;
public static final int MALE = 1;
private static final int FEMALE = 2;
private static final Sex MA = new Sex("男");
private static final Sex FEMA = new Sex("女");
private Sex(String s){
this.title = title;
}
public static Sex getInstance(int flag){
switch (flag){
case MALE:
return MA;
case FEMALE:
return FEMA;
default:
return null;
}
}
public String toString(){
return this.title;
}
}
总结:无论单例还是多例:
- 构造方法私有化
- 类内部提供一个static方法用于取得实例化对象
3.Java的异常捕获
几乎所有的代码里都会出现异常, 为了保证程序可以在异常出现后可以正常执行完毕就要进行异常处理,首先我们来看一下异常的继承结构:
受查异常
:必须进行强制处理
,除了RuntimeException及其子类和Error及其子类以为都是受查异常非受查异常
:无需强制处理
的异常,RuntimeException及其子类和Error及其子类,例如:空指针异常、数组越界异常、类型转换异常
3.1.异常的影响
异常是导致程序中断的一种指令流,程序之中如果出现异常并且没有合理处理的话就会导致程序在异常语句之前正常执行,但是在产生异常的语句处终止执行
。
3.2.异常处理格式
异常处理格式如下:
try{
有可能出现异常的语句
}[catch(异常类 对象){
}...]
[finally{
异常的出口
}]
对于以上关键字可能出现的组合:
try...catch
try...finally
try...catch...finally
要注意的是,无论try...catch中是否有返回语句,都会执行finally块
。
打印错误堆栈的方法:
e.printStackTrace()
3.3.throws——作用于方法上
throws作用在方法上
:在进行方法定义的时候,如果要告诉调用者本方法会产生哪些异常,就可以使用throw方法进行声明,表示将异常抛回给调用方,使用throws定义方法示例如下:
public class Test{
public static void main(String[] args) {
try{
System.out.println(calculate(10,0));
}catch (Exception e){
e.printStackTrace();
}
}
public static int calculate(int x, int y) throws Exception{
return x/y;
}
}
如果现在调用了throws声明的方法,那么在调用的时候必须使用try...catch来捕获
,因为该方法可能产生异常,所以必须按照异常的方式来进行处理。主方法本身也是一个方法,所以主方法也可以使用throws进行异常抛出,这个时候如果产生异常就由JVM处理
。主方法抛出异常示例:
public class Test{
//主方法抛出异常由JVM处理
public static void main(String[] args) throws Exception{
System.out.println(calculate(10,0));
}
public static int calculate(int x, int y) throws Exception{
return x/y;
}
}
3.4.throw——作用于方法中
throw是直接编写在语句之中
,表示人为进行异常抛出
,如果异常类对象不希望由JVM产生而通过用户产生
,就可以使用throw语句来实现。使用throw产生异常对象示例如下:
请解释throw和throws的区别:
- throw作用于方法内部,主要用于
手动抛出异常
- throws作用于方法上,
明确告诉用户使用本方法可能产生的异常,表示将异常抛回给调用方,同时该方法可能不处理此异常
3.5.异常处理标准格式
现在要求编写一个方法进行除法操作,但是对于此方法有如下要求:
- 在进行除法操作之前打印一行语句“**”
- 如果在除法运算过程中出现错误,则应该将异常返回给调用处
- 不管最终是否有异常产生,都要求打印一行计算结果信息
实现代码如下:
public class Test{
public static void main(String[] args) {
try{
System.out.println(calculate(10,0));
}catch (Exception e){
e.printStackTrace();
}
}
public static int calculate(int x,int y) throws Exception{
int result = 0;
System.out.println("计算开始前**");
try{
result = x / y;
}catch (Exception e){
throw e;//把异常抛出
}finally {
System.out.println("计算结束**");
}
return result;
}
}
执行结果如下:
对如上格式进行进一步简化,直接使用try…finally
public static int calculate(int x,int y) throws Exception{
int result = 0;
System.out.println("计算开始前**");
try{
result = x / y;
}finally {
System.out.println("计算结束**");
}
return result;
}
3.6.RuntimeException类
先来看一段代码:
public class Test{
public static void main(String[] args) {
String str = "100";
int num = Integer.parseInt(str);
System.out.println(num * 2);
}
}
//200
我们来看一下parseInt的源码定义:
public static int parseInt(String s) throws NumberFormatException
该方法明确抛出异常,但是在进行调用时即使没有异常处理也可以照常执行,这就属于RuntimeException范畴。很多代码都可能出现异常,如果所有出现异常的地方都强制性处理,这样代码就太复杂了。所以在异常设计时,考虑到一些异常是简单问题,所以将这类异常称为RuntimeException,也就是使用RuntimeException定义的异常类可以不需要强制性进行异常处理
。
请解释Exception和RuntimeException的区别,并列举几个RuntimeException?
- Exception是RuntimeException的
父类
- 使用
Exception定义的异常都要求必须使用异常处理
,而使用RuntimeException定义的异常可以由用户自由选择
异常处理与否 - 常见的RuntimeException有数组越界异常,类型转换异常,空指针异常等
3.7.断言assert
断言是从JDK1.4开始引入的概念,断言指的是当程序执行到某些语句之后其数据的内容一定是约定的内容。
想要断言起作用需要使用-ea参数传给虚拟机,手动开启,默认情况下不开启断言。
AssertError:断言错误—>费受查异常
Java引入assert是为了和C++兼容,开发中不提倡使用
3.8.自定义异常
在Java中,针对可能出现的公共的程序问题都会提供有相应的异常信息,但是很多时候这些异常信息往往不够我们使用,此时我们可以自定义异常。
自定义异常实现如下:
class AddException extends Exception {
public AddException(String str) {
super(str);
}
}
public class Test{
public static void main(String[] args) throws AddException {
int num1 = 20;
int num2 = 30;
if(num1 + num2 == 50){
throw new AddException("错误的操作");
}
}
}
在开发中涉及到与业务相关的异常问题此时都需要自定义异常。