【黑马程序员】第四章:面向对象(下)

一、类的继承


1、继承的概念

   在现实生活中,继承一般指的是子女继承父辈的财产。在程序中,继承描述的是事物之间的所属关系,通过继承可以使多种书屋之间形成一种关系体系。例如猫和狗都属于动物,程序中便描述为猫和狗继承自动物,同理波斯猫和巴厘猫继承自猫,而沙皮狗和斑点狗都继承自狗,这些动物之间会形成一个体系。

   在 Java  中,类的继承是指在一个仙有泪的基础上去构建一个新的类,构建出来的新类被称作子类,现有类被称为父类,子类会自动拥有父类所有可继承的属性和方法。在程序中,如果想声明一个类继承另一个类,需要使用 extends 关键字。

//定义 Animal 类
class Animal{
	String name;
	void shout(){
		System.out.println("动物发出叫声");
	}
}
//定义 Dog 类继承 Ainmal 类
class Dog extends Animal{
	public void printName(){
		System.out.println("name=" + name);
	}
}
class ExtendsDemo1{
	public static void main(String[] args){
		Dog dog = new Dog();
		dog.name = "沙皮狗";
		dog.printName();
		dog.shout();
	}
}

   运行结果如下:

   继承的特点:

  • 类只支持但继承,不允许多重继承,也就是说一个类只能有一个直接父类;

        //这种情况是不合法的!
        class A{}
        class B{}
        class C extends A,B{}

  • 多个类可以继承一个父类;

	//这种情况是允许的!
	class A{}
	class B extends A{}
	class C extends A{}

  • 多层继承是可以的,即一个类的父类可以再去继承另外的父类;

	//这种情况是允许的!
	class A{}
	class B extends A{}
	class C extends B{}

  • 子类和父类是一种相对概念,也就是说一个类时某个类父类的同时,也可以是另一个类的子类。

   继承的作用:

  • 继承的出现提高了代码的复用性;
  • 让类与类之间产生了关系,提供了多态的前提。

   定义继承需要注意:

  • 不要仅为了获取其他类中的某个功能而去继承;
  • 类与类之间要有所属关系,xx1 是 xx2 的一种。

2、重写父类方法

   在继承关系中,子类会自动继承父类中定义的方法,但有时在子类中需要对继承的方法进行一些修改,即对父类的方法进行重写。需要注意的是,在子类中重写的方法需要和父类被重写的方法具有相同的方法名、参数列表以及返回值类型。

//定义 Animal 类
class Animal{
	void shout(){
		System.out.println("动物发出叫声");
	}
}
//定义 Dog 类继承 Ainmal 类
class Dog extends Animal{
	void shout(){
		System.out.println("汪汪······");
	}
}
//定义测试类
class ExtendsDemo1{
	public static void main(String[] args){
		Dog dog = new Dog();
		dog.shout();
	}
}

   运行结果如下:


  注意:子类重写父类方法时,不能使用比父类中被重写的方法更严格的访问权限,如父类中的方法是 public 的,子类的方法就不能是 private 的。

3、super 关键字

   当子类重写父类的方法后,子类对象将无法访问父类被重写的方法,为了解决这个问题,在 Java 中专门提供了一个 super 关键字用于访问父类的成员,例如访问父类的成员变量、成员方法和构造方法。

   super 关键字的具体用法:

  • 使用 super 关键字调用父类的成员变量和成员方法;

              >  super . 成员变量
              > super . 成员方法([参数1,参数2···])

class Animal{
    String name = "动物";
    void shout(){
        System.out.println("动物发出叫声...");
    }
}
class Dog extends Animal{
    String name = "犬类";
    void shout(){
        super.shout();
    }
    void printName(){
        System.out.println("name="+super.name);
    }
}
public class Example{
    public void main(String[] args){
        Dog dog = new Dog();
        dog.shout();
        dog.printNmae();
    }
}

  • 使用 super 关键字调用父类的构造方法;

              > super([参数1,参数2...])

class Animal{
	public Animal(String name){
		System.out.println("我是一只"+name);
	}
}
class Dog extends Animal{
	public Dog(){
		super("沙皮狗");
	}
}
public class Example{
	public void main(String[] args){
		Dog dog = new Dog();
	}
}

   需要注意的是

  • 通过 super 调用父类构造方法的代码必须位于子类构造方法的第一行,并且只能出现一次;
  • 子类中所有的构造函数默认都会访问父类中空参数的构造函数,因为每一个构造函数的第一行都有一句默认的 super();
  • 当父类中没有空参数的构造函数时,子类的构造函数必须通过 this 或 super 语句指定要访问的构造函数。

二、final 关键字


   final 关键字可用于修饰类、变量和方法,它有 " 这是无法改变的 " 和 " 最终 " 的含义,因此被 final 修饰的类、变量和方法将具有以下特性:

  • final 修饰的类是不能继承的;
  • final 修饰的方法不能被子类重写;
  • final 修饰的变量(成员变量和局部变量)是常量,只能赋值一次。

1、final 关键字修饰类

   Java 中的类被 final 关键字修饰后,该类将不可以被继承,也就是不能派生子类。

//使用 final 修饰Animal类
final class Animal{}
//Dog继承Animal类
class Dog extends Animal{}
//测试类
public class Example{
	public void main(String[] args){
		Dog dog = new Dog();
	}
}

   编译程序报错:


2、final 关键字修饰方法

   当一个类的方法被 final 关键字修饰后,这个类的子类将不能重写该方法。

//定义Animal类
class Animal{
	//使用final修饰shout()方法
	public final void shout(){}
}
//Dog继承Animal类
class Dog extends Animal{
	//重写Animal类的shout()方法
	public void shout(){}
}
//测试类
public class FinalDemo1{
	public void main(String[] args){
		Dog dog = new Dog();
	}
}

   编译程序报错:


3、final 关键字修饰变量

   Java 中被 final 修饰的变量为常量,它只能被赋值一次,也就是说 final 修饰的变量一旦被赋值,其值不能改变。

class FinalDemo3{
	public static void main(String[] args){
		final int num = 4;
		num = 9;
	}
}

   编译程序报错:


三、抽象类和接口


1、抽象类

   当定义一个类时,常常需要定义一些方法来描述该类的行为特征,但有时这些方法的实现方式是无法确定的。例如在定义 Animal 类时,shout() 方法用于表示动物的叫声,但是针对不同的动物,叫声也是不同的。

   针对上面描述的情况,Java 允许在定义方法时不写方法体,不包含方法体的方法为抽象方法,抽象方法必须使用 abstract 关键字修饰,具体示例如下:

  • abstract   void   shout();

   当一个类中包含了抽象方法,该类必须使用 abstract 关键字来修饰,使用 abstract 关键字修饰的类为抽象类,具体示例如下:

  • abstract   class   Animal {
  •            abstract   int   shout();
  • }

   在定义抽象类时需要注意,包含抽象方法的类必须声明为抽象类,但抽象类可以不包含抽象方法,只需要使用 abstract 关键字修饰即可。另外抽象类不可以被实例化,因为抽象类包含抽象方法,抽象方法是没有方法体的,不能别调用。

//定义抽象类Animal
abstract class Animal{
	abstract void shout();
}
//定义Dog类继承抽象类Animal
class Dog extends Animal{
	void shout(){
		System.out.println("汪汪...");
	}
}
class AbstractDemo1{
	public static void main(String[] args){
		Dog dog = new Dog();
		dog.shout();
	}
}

   需要注意的是,被 abstract 修饰的函数不能同时被 private、final、static 修饰。

  • private:抽象方法需要被子类复写,但是抽象类中的私有抽象方法,不能被子类所知,无法复写;
  • final:被 final 修饰的类不能有子类;
  • static:如果 static 可以修饰抽象方法,那么直接类名调用就可以了,那么运行抽象方法没有意义。

2、接口

   如果一个抽象类中的所有方法都是抽象的,则可以将这个类用另外一种方法来定义,即接口。在定义接口时,需要使用 interface 关键字来声明,具体示例如下:

  • interface  Animal{
  •         int   ID = 1;
  •         void   breathe();
  •         void   run();
  • }

   代码中 Animal 即为一个接口,但是抽象方法 breathe() 并没有使用 abstract 来修饰,这是因为接口中定义的方法和变量都包含一些默认修饰符,接口中定义的方法默认使用 public abstract 来修饰,即抽象方法。接口中的变量默认使用 public static final 来修饰,即全局变量。

   由于接口中的方法都是抽象方法,因此不能通过实例化对象的方式来调用接口中的方法,此时需要定义一个类并使用 imlements 关键字实现接口中所有的方法。

//定义Animalj接口
interface Animal{
	int ID = 1;
	void breathe();
	void run();
}
//定义Dog类实现Animal接口
class Dog implements Animal{
	public void breathe(){
		System.out.println("狗在呼吸");
	}
	public void run(){
		System.out.println("狗在跑");
	}
}
class InterfaceDemo1{
	public static void main(String[] args){
		Dog dog = new Dog();
		dog.breathe();
		dog.run();
	}
}

   接口的特点如下:

  • 接口中的方法都是抽象的,不能实例化对象;
  • 当一个类实现接口时,如果这个类是抽象类,则实现接口中的部分方法即可,否则需要实现接口中的所有方法;
  • 一个类通过 implements 实现接口时,可以实现多个接口,被实现的多个接口之间用逗号分隔;
  • 一个接口可以通过 extends 关键字继承多个接口,接口之间用逗号隔开;
  • 一个类在继承另一个类的同时,还可以实现接口,此时 extends 关键字必须位于 implements 关键字之前。

四、多态


1、多态概述

   在设计一个方法时,通常希望该方法具备一定的通用性。例如要实现一个动物叫的方法,由于每种动物的叫声不同,因此可以在方法中接收一个动物类型的参数,当传入猫类对象时 就发出猫类的叫声,当传入犬类对象时就发出犬类的叫声。在同一个方法中,这种由于参数类型不同而导致执行效果各异的现象就是多态。

   在 Java 中为了实现多态,允许使用一个父类类型的变量来引用一个子类类型的对象,根据被引用子类对象的特征的不同,得到不同的运行结构。

//定义Animalj接口
interface Animal{
	void shout();
}
//定义Cat类实现Animal接口
class Cat implements Animal{
	public void shout(){
		System.out.println("喵喵...");
	}
}
//定义Dog类实现Animal接口
class Dog implements Animal{
	public void shout(){
		System.out.println("汪汪...");
	}
}
class AbstractDemo1{
	public static void main(String[] args){
		Animal an1 = new Cat();//父类类型变量引用不同子类对象
		Animal an2 = new Dog();//父类类型变量引用不同子类对象
		animalShout(an1);
		animalShout(an2);
	}
	public static void animalShout(Animal an){
		an.shout();
	}
}

   由此可见,多态不仅解决了方法同名的问题,而且还使程序变得更加灵活,从而有效地提高程序的可扩展性和可维护性。

2、多态的体现

  • 父类的引用指向了自己的子类对象;
  • 父类的引用也可以接收自己的子类对象。

3、多态的前提

  • 类与类之间不虚有关系,要么继承,要么实现;
  • 存在覆盖,父类中有方法被复写。

4、多态的利弊

  • 利:提高了程序的扩展性和后期可维护性;
  • 弊:只能使用父类中的引用访问父类中的成员。也就是说使用了多态,父类型的引用在使用功能时,不能直接调用子类中的特有方法,如 Animal animal = new Cat(); 代码就是多态的体现,假设子类中有特有的抓老鼠功能,父类型的 animal 就不能直接调用。上面的代码可以理解为 Cat 向上转型。

   如果此时父类型中的引用想要调用子类型中特有的方法,就需要强制将父类型的引用转成子类型,向下转型:Cat  cat = (Cat) Animal; 。注意,如果父类可以创建对象 Animal animal = new Animal(); ,此时不能向下转型,Cat cat = (Cat) animal 这样的代码不允许存在,编译报错。

   我们能转换的是父类引用指向了子类对象,该引用可以被提升,也可以给强制转换。多态自始至终都是子类在作者变化!

//定义Animal类
abstract class Animal{
	public abstract void eat();
}
//定义Cat类,覆盖父类的eat方法,并编写cat类中的特有方法。
class Cat extends Animal{
	public void est(){
		System.out.println("吃鱼");
	}
	public void catchMouse(){
		System.out.println("抓老鼠");
	}
}
class {
	public static void main(String[] args){
		Animal animal = new Cat();
		animal.eat();
                //将父类引用强转为子类,并调用子类的特有方法
                Cat cat = (Cat)animal;
		cat.catchMouse();
	}
}

5、多态的特点

   多态中非静态成员函数的特点:

  • 在编译时期:查看引用性变量中是否包含调用的方法,如果有编译通过,如果没有则编译失败;
  • 在运行时期:查看对象所属的类中是否有调用的方法,如果父类中有一个非抽象方法,而子类将其复写,调用这个同名函数时,运行的将是子类的方法;

         简单的总结就是:成员函数在多态调用时,编译看左边,运行看右边!

   多态中成员变量的特点:

  • 无论编译或者运行,都查考左边(应用型变量所属的类)。多态中的引用滴啊用成员变量时,如果父类和子类有同名的成员变量,那么将调用父类中的成员变量。

   多态中静态成员函数的特点:

  • 无论编译或者运行,都参考左边。父类引用调用静态成员函数时,调用的是父类的静态函数。这是因为类一加载,就被解析到了内存中,这时不需要创建对象,直接类名调用即可。同时,父类中的静态函数一般不会被复写。

6、多态的应用

  • 定义好工具类,即将共同行为封装在一个类中;
  • 对类进行抽取;
  • 操作同一个父类,对其子类型均可操作。

/* 
电脑的运行实例。
电脑的运行由主板控制,假设主板只是提供电脑运行,但是没有上网,听歌等功能。而上网、听歌需要硬件的支持。
而现在主板上没有网卡和声卡,这时可以定义一个规则,叫PCI,
只要符合这个规则的网卡和声卡都可以在主板上使用,这样就降低了主板和网卡、声卡之间的耦合性。用程序体现。 
*/  

//定义一个PCI接口
interface PCI{
    void open();
    void close();
}

//定义网卡,实现PCI接口
class NetCard implements PCI{
    public void open(){
        System.out.println("NetCard_open");
    }
    public void close(){
        System.out.println("NetCard_close");
    }
}
//定义声卡,实现PCI接口
class SoundCard implements PCI{
    public void open(){
        System.out.println("SoundCard_open");
    }
    public void close(){
        System.out.println("SoundCard_close");
    }
}
class MainBord{
    public void run(){
        System.out.println("MainBord_run");
    }
    public static void usePCI(PCI p){
        if(p != null){
            p.open();
            p.close();
        }
    }
}
class Example{
    public static void main(String[] args){
        MainBord mainBord = new MainBord();
        mainBord.run();
        mainBord.userPCI(new NetCard());
        mainBord.userPCI(new SoundCard());
    }
}

五、异常


1、什么是异常?

   尽管人人都希望自己身体健康, 处理的事情顺利进行,但在实际生活中总会遇到各种状况,比如感冒发烧。同样在程序运行的过程中,也会发生非正常状况,比如磁盘空间不足、网络中断等。针对这样的情况,Java 语言引入了异常,以异常类的形式对这些非正常情况进行封装,通过异常处理机制对程序运行时发生的各种问题进行处理。

   异常是 Java 中的重要机制,也是用了面向对象的思想,进行了封装。

   在 Java 中提供了大量的异常类,这些类都继承自 java.lang.Throwable 类。Throwable 类有连个直接子类 Error 和 Exception,其中 Error 代表程序中产生的错误,Exception 代表程序中产生的异常。接下来对两个子类进行详解:

  • Error 类:称为错误类,它便是 Java 运行时产生的系统内部错误或资源耗尽的错误,是比较严重的,紧靠修改程序本身是不能恢复执行的。
  • Exception类:称为异常类,它表示程序本省可以处理的错误,在开发 Java 程序中进行的异常处理,都是针对 Exception 类及其子类。在 Exception 类的众多子类中有一个特殊的 RuntimeException 类,该类及其子类用于表示运行时异常,除此类之外的其他Exception 子类都是表示编译时异常。

2、运行时异常和编译时异常

   在实际开发中,经常会在程序编译时产生一些异常,而这些异常必须要进行处理,这种异常被称为编译时异常(也称为 checked 异常)。另外还有一种异常在程序运行时产生,这种异常及时不便携异常处理嗲吗,依然可以听通过编译,因此可以称为运行时异常(也称为 unchecked 异常)。

   编译时异常:

   在Java 中,Exception 类除了 RuntimeException 类及其子类都是编译时异常。编译时异常的特点是 Java 编译器会对其进行检查,如果出现异常必须对异常进行处理,否则程序无法通过编译。

   处理编译时期的异常有两种方式,具体如下:

  • 使用 try ... catch 语句对异常进行捕获;
  • 使用 throws 关键字声明抛出异常,调用者对其处理。

   运行时异常:

   RuntimeException 类及其子类都是运行时异常。运行时异常的特点是 Java 编译器不会对其进行检查,也就是说,当程序出现这类异常时,即使没有使用 try ... catch 语句捕获或者使用 throws 关键字声明抛出,程序也能编译通过。

   运行时异常一般是由程序中的逻辑错误引起的,在程序运行时无法恢复。比如通过数组的角标访问数组的元素时,如果超出了数组的最大角标,就会发生运行时异常。

3、异常的处理

   第一种方式:异常捕获

   由于发生了异常,程序立即终止,无法继续向下执行。为了解决这样的问题,Java 中提供了一种对异常进行处理的方式:异常捕获。异常捕获通常使用 try ... catch 语句,具体格式如下:

try{
	//程序代码块
}catch(ExceptionType e){
	//对ExceptionType的处理
}

   其中在 try 代码块中编写可能发生异常的 Java 语句,catch 代码块中编写针对异常进行处理的代码。当 try 代码中的程序发生了异常,系统会将这个异常的信息封装成一个异常对象,并将这个对象传递给 catch 代码块。catch 代码块需要一个参数指明它所能够接收的异常类型,这个参数的类型必须是 Exception 类或其子类。

class Example{
	public void main(String[] args){
		try{
			int result =divide(4,0);
			System.out.println(result);//发生异常之后不会被执行
		}catch(Exception e){
			System.out.println(e);
		}
		System.out.println("程序向下执行");
	}
	public static int divide(int x,int y){
		int result = x/y;
		return result;
	}
}

   需要注意的是,在 try 代码块中,发生异常语句后面的代码是不会被执行的。

   在程序中,有时候我们希望有些语句无论程序是否发生异常都要执行,这时可以在 try ... catch 语句后,加一个 finally 代码块。

class Example{
	public void main(String[] args){
		try{
			int result =divide(4,0);
			System.out.println(result);
		}catch(Exception e){
			System.out.println("捕获的异常信息为"+e.getMessage());
			return;//用于结束当前语句
		}finally{
			System.out.println("进入finally代码块");
		}
		System.out.println("程序继续向下执行");
	}
	public static int divide(int x,int y){
		int result = x/y;
		return result;
	}
}

   需要注意的是,finally 中的代码块有一种情况下是不会执行的,那就是在 try ... catch 语句中执行了 System.exit(0) 语句。System.exit(0) 表示推出当前的 Java 虚拟机,Java 虚拟机停止了,那么任何嗲吗都不能再执行了。

   第二种方式:抛出异常

   在上述的例子中,由于调用的是自己写的 divide() 方法,因此很清楚该方法可能会发生异常。试想一下,如果去掉用其他人写的方法时,时候能知道别人写的方法是否会发生异常呢?这是很难做出判断的。

   针对这种情况,Java 中允许在方法的后面使用 throws 关键字对外声明该方法有可能发生的异常,这样调用者在滴啊用方法时,就明确的知道该方法有异常,并必须在程序中对异常进行处理,否则编译无法通过。

   throws 关键字声明抛出异常的语法格式如下:

修饰符 返回值类型 方法名([参数1,参数2 ...])throws ExceptionType{}

   从上述的语法格式中可以看出,throws 关键字需要卸载方法声明的后面,throws 后面需要声明方法中发生异常的类型,通常将这种做法称为方法声明抛出一个异常。

class Example{
    public void main(String[] args){
        try{
            int result =divide(4,2);
            System.out.println(result);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    public static int divide(int x,int y)throws Exception{
        int result = x/y;
        return result;
    }
}

4、throw 和 throws 的用法

  • throw 定义在函数内,用于抛出异常;
  • throws 定义在函数上,用于抛出异常类,可以抛出多个,用逗号隔开;

   当函数内容有 throw 抛出异常对象,并未进行 try 处理,必须在函数上声明,否则编译失败。但是 RuntimeException 异常除外,也就是说在函数内抛出 RuntimeException 异常时,在函数上可以不用声明。

5、自定义异常

   JDK 中定义了大量的异常类,虽然这些异常类可以描述编程时出现的大部分异常情况,但是在程序开发中有时可能需要描述程序中特有的异常情况,为了解决这一情况,在 Java 中允许用户自定义异常,但是自定义的异常必须继承自 Exception 或其子类。

  • 为了让该自定义类具备可抛性;
  • 让该类具备操作异常的共性方法。

class Example{
	public void main(String[] args){
		try{
			int result =divide(4,-2);
			System.out.println(result);
		}catch(DivideByMinusException e){
			System.out.println(e.getMessage());
		}
	}
	public static int divide(int x,int y)throws DivideByMinusException{
		if(y<0){
			throw new DivideByMinusException("被除数为负数");
		}
		int result = x/y;
		return result;
	}
}

6、异常的好处和原则

   好处:

  • 将问题进行封装;
  • 将正常流程代码和问题处理代码相隔离,便于阅读。

   原则:

  • 处理方式有两种:try 或 throws;
  • 调用到抛出异常的功能时,抛出几个就处理几个,一个 try 可以对应多个 catch;
  • 多个 catch 时,父类的 catch 放在最后面,否则编译失败,因为后面的 catch 执行不到;
  • catch 内,需要定义针对性的处理方式,不要简单的定义 printStackTrace 或输出语句,也不要不写任何语句。当捕获到的异常,本功能处理不了是时,可以继续在 catch 中抛出。

7、异常的注意事项

  • 问题在内部解决就不需要声明;
  • catch 用于处理异常,如果没有 catch 就代表异常没有处理,如果该异常是检测时异常就必须声明;
  • 在子父类覆盖时,子类抛出的异常必须是父类异常的子类或者子集,如果父类或者接口没有抛出异常,子类覆盖出现异常,只能 try 不能 throws。

/* 练习:老师使用电脑讲课。 
     
描述电脑: 
	1、电脑运行 
	2、电脑重启 

描述电脑问题: 
	1、电脑蓝屏了 
	2、电脑起火了 
     
描述老师: 
	1、老师使用电脑 
	2、老师讲课。 

描述老师可能出现的问题: 
	1、老师不能继续讲课了,他让同学们自己做练习。 
     
*/  
      
//电脑蓝屏异常
class BlueScreenException extends Exception{
	BlueScreenException(String msg){
		super(msg);
	}
}

//电脑起火异常
class FireBreakingException extends Exception{
	FireBreakingException(String msg){
		super(msg);
	}
}
 
//老师无法继续上课异常
class StopTeachException extends Exception{
	StopTeachException(String msg){
		super(msg);
	}
}

//定义电脑
class Computer{
	int start = 1;
	void run()throws BlueScreenException,FireBreakingException{
		if(start == 2){
			throw new BlueScreenException("电脑蓝屏了");
		}
		if(start == 3){
			throw new FireBreakingException("电脑着火了");
		}
	}
	void reset(){
		start = 1;
		System.out.println("电脑重启了");
	}
}

//定义老师类
class Teacher{
	private String name;
	private Computer cpt;
	Teacher(String name){
		this.name = name;
		cpt = new Computer();
	}

	//老师讲课
	void teach()throws StopTeachException{
		try{
			cpt.run();
		}catch(BlueScreenException b){
			cpt.reset();
		}catch(FireBreakingException f){
			test();
			throw new StopTeachException("无法上课:"+f.getMessage());
		}
	}
	void test(){
		System.out.println("学生做练习");
	}
}

//测试类
class Test{
	public void main(String[] args){
		Teacher t = new Teacher("zhangSan");
		try{
			t.tech();
		}catch(StopTeachException e){
			System.out.println("e.toString()");
			System.out.println("放假");
		}
	}
}

六、包

1、包的定义与使用

   为了翻遍对硬盘上的文件进行管理,通常都会将文件分目录进行存放。同理在程序开发中,也需要将编写的类分目录存放便于管理,为此 Java 引入了包(package)机制,程序可以通过声明包的方式对 Java 类定义目录。

   包也是一种封装的形式,在包中可以有很多类文件,但只提供一个类文件供外界使用。

   在声明包时,使用 package 语句,具体示例如下:

package cn.itcast.demo;
public class Demo{...}

   当编译一个声明了包的java源文件时,需要使用命令生成与包名对应的木,具体示例如下:

javac -d . Demo.java

   其中,-d 用来指定生成的类文件位置,. 表示在当前目录,整行命令表示生成带包目录的 .class 文件并存放在当前目录下。

2、包的作用

  • 为了避免多个类重名的情况,如果出现两个相同名字的类,可通过包将两者区分,从而避免冲突。
  • 对类文件进行分类管理,可以将相关的类放在同一个包中。
  • 给类提供多层命名空间,如 a 包中的 Demo.class 文件,如果要创建 Demo 对象,那么需要 a.Demo  demo  =  new  a.Demo(); 。
  • 包的出现可以将 Java 的类文件和源文件分开。

3、import 语句

   在实际开发汇总,定义的类都是含有包名的,而且还有可能定义很长的包名。为了简化代码,Java 提供了 import 关键字,使用 import 关键字可以在程序中一次导入某个指定包下的类,这样不必在每次用到该类时都书写完整的包名了。格式如下:

   import 包名.类名

   需要注意的是,import 通常出现在 package 语句之后,类定义之前。

   有时候,需要用到一个包中的许多类,可以使用  import  包名 . *  来导入该包下的所有类。

   在 JDK 中不同功能的类都放在不同的包中,其中 Java 的很心累主要放在 java 这个包及其子包下,Java 扩展的大部分类都放在 javax 包及其子包下。为了方便以后的学习,接下来简单介绍 Java 语言中常用的包:

  • java.lang:包含 Java 语言的核心类,如 String、Math、System 和 Thread 类等,使用这个包中的类无需使用 import 语句导入,系统会自动导入这个柏爱霞的所有类。
  • java.util:包含 Java 中大量工具类、集合类等,例如 Array、List、Set 等。
  • java.net:包含 Java 网络编程相关的类和接口。
  • java.io:包含了Java 输入、输出相关的类和接口。
  • java.awt:包含用于创建图形化界面(GUI)相关的类和接口。

4、访问控制

   在 Java 中,针对类、成员方法和属性提供了四种访问级别,分别是 private、default、protected 和 public 。访问级别从小到大依次为:

  具体介绍如下:

  • public(公共访问级别):这是一个最宽松的访问控制级别,如果一个类或者类的成员被 public 修饰,那么这个类或者类的成员能被所有的类访问,不管访问类与被访问类是否在同一个包中;
  • protected(子类访问级别):如果一个类的成员被 protected 修饰,那么这个成员既能被同一包下的其他类访问,也能被不同包下该类的子类访问;
  • default(包访问级别):如果一个类或者类的成员不实用任何权限修饰符,那么它被称为默认访问控制级别,这个类或者类的成员只能被本博鳌下的其他类访问;
  • private(类访问级别):如果类的成员被 private 修饰,那么这个成员只能别该类的其他成员访问,其他类无法直接访问,类的良好封装性就是通过 private 关键字来实现的。

5、jar 包

   在实际开发中经常需要开发一些类提供别人使用,为了能够更好的管理这些类,在 JDK  中提供了一个 jar 命令,使用这个命令能够将这些类打包成一个文件,这个文件的扩展名为 .jar 文件,被称为 jar 文件。jar 文件的全称是 Java Archive File ,意思是 Java 档案文件,它是一种压缩文件,独立于任何操作系统平台,习惯上也将 jar 文件称为 jar 包。

   在命令行中输入 jar 命令,查看 jar 命令的用法帮助:

        

   生成 jar 包: jar  -cvf   helloworld.jar   cn

  • -c:代表穿件归档文件;
  • -v:代表在标准输出中生成详细输出;
  • -f:嗲表指定归档文件名。

         在生成的 jar 文件中,可以看到两个目录 cn 和 META-INF,cn 目录就是有完整包名的 HelloWorld.class,META-INF 目录下有一个 MANIFEST.MF 文件。

   运行 jar 包:java   -jar  helloworld.jar

         在命令行中输入以上命令,出现了 helloworld.jar 中没有主清单属性 的错误信息。

        

         这是因为在执行 jar 文件时,没有指定 jar 包中的朱磊,也就是没有指定作为程序入口的 main() 函数在那个 Java 类中。MANIFEST.MF 文件就是用来指定 jar 包主类的文件,在文件中加入一行内容:Main-Class:cn.itcast.helloworld ,这样就指定了 jar文件的主类是 cn.itcast.HelloWorld。

   解压 jar 包:jar   -xvf   helloworld.jar

  • -x:代表从档案中提取指定的文件。

   jar 包的好处:

  • 可以将多个包压缩为一个文件,方便项目携带;
  • 便于使用,只要在 classpath 中设置了 jar 路径,就可以执行 jar 包中的 Java 程序;
  • 数据库驱动,SSH 框架等都是以 jar 包体现的。
阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭