Head First Java 十四 至 终章

第十四章 发布你的代码

分离.java 和 .class文件

没用IDE的话,编译好的文件和源码文件,是在同一个文件夹下的,会非常乱。手动分离会非常麻烦。

$cd MyProject/source    
首先进入source文件夹

$javac -d  ../classes  MyApp.java
然后编译源文件,到../classes文件夹下,-d用于指示编译好的.class文件放到哪里去。MyApp.java就是你要编译哪个文件,如果是该文件夹下的所有.java的话,就写:
javac -d ../classes *.java

$cd MyProject/classes
然后进入编译好的classes文件夹下,准备执行java程序。

$java Mini
执行名为Mini的程序。

注意:编译命令为javac,执行用java。

创建可执行的JAR

1、把所有.class文件放在classes文件夹下。

2、在这个classes文件夹下,建立一个manifest.txt文件,里面写:
Main-Class:MyApp
标识出,哪个是你main函数所在的类。

注意: MyApp后面可没有.class。

注意: MyApp后面一定要打回车!不然就失败了。

3、创建JAR文件,用jar工具
$cd MiniProject/classes
$jar -cvmf manifest.txt app1.jar *.class
OR
$jar -cvmf manifest.txt app1.jar MyApp.class

运行JAR

$cd MyProject/classes
进入JAR文件所在路径

$java -jar app1.jar
执行JAR文件,当然得找得到才行。不写-jar就默认运行.class了。

package

package 就是为了减少命名冲突。

建议的命名方式就是,包的前缀写为你域名的相反字符串。

比如百度 baidu.com,就写成 com.baidu.project.aProgram
aProgram是类名。

注意: 如果要把你写的.java文件放入一个包中, 包名必须为.java的第一个语句 ,必在import之前,且只有一个。
package com.baidu.project; 
这句话必须为第一个。

注意: 一般,最好最好,把文件的结构,设置的与包结构一样。比如上面的package com.baidu.project;
你就创建一堆文件夹,com下有个baidu文件夹,baidu下又有project文件夹,然后你的.java文件就在这个project文件夹下。这么做有好处。

但是有了包以后,编译和执行都变得麻烦了一点。
编译:
$cd MyProject/source
$javac -d ../classes com/baidu/project/*.java
执行:
$cd MyProject/classes
$java com.baidu.project.aProgram
注意:执行要写出全名,不然JVM不认识。

如果有了包名后,加上-d进行编译时,会自动在classes(你上面写的classes)文件夹下,加入包的结构文件夹。就是上面那个命令编译完了后,classes下会出现com文件夹,com下会有baidu文件夹,然后再往下游project文件夹,然后里面才有.class文件。非常方便。

注意:执行的时候,java com.baidu.project.aProgram这个命令如果不是在classes文件夹下执行,比如你在com文件夹下执行,会报找不到这个程序。因为它现在在com文件夹下,它会继续找com文件夹,然后找baidu文件夹,这样一步一步下去。

制作带包的JAR

1、确保你的所有类都放在了正确的文件结构下。

2、写manifest.txt
Main-Class: com.baidu.project.aProgram
注意:把这个manifest.txt放在classes文件夹下,即和com文件夹并列。

3、$cd MyProject/classes
     $jar -cvmf manifest.txt packEx.jar com
注意:一定要把com文件夹写出来,你的类都在这呢。

浏览JAR包命令
jar -tf packEx.jar

tf 表示 table file

解压JAR命令
jar -xf packEx.jar

xf表示 extract file

597~600 在讲JWT,远程运行java程序,网上的。我没仔细看。

第十五章 分布式计算

RMI

平时我们调用一个调用一个函数,或读取一个对象的数据,使用的对象都在本地的堆上。如果我们想要调用一个远程的对象,RMI就用上了。

RMI就是能给你提供client helper和service helper的东西。什么是client helper和service helper呢?就是我是个client,我想要调用一个远程机器上的对象提供的服务,比如一个函数。那我这个client helper就给我的client提供了一种假象,让我的client以为服务是由client helper提供的,然后client在本地,调用client helper,说需要这个服务,然后client helper会将请求包装起来,通过Socket和Stream,发送给远程的server helper,然后server  helper又去调用它本地的server的服务。最后再这么一步一步的把服务的结果传回来。

client helper 和 server helper其实就是将所有要远程通信用到的,底层的Socket 和 Stream全封装起来的对象。

RMI将你要用的底层的基础东西全封装起来,你不用自己再写网络或IO代码。他来提供远程服务相关的代理工作。

注意:虽然说是调用的时候和调用本地对象一样,但是也有很大不同。主要体现在异常上,你客户必须认识到异常的出现。因为最终是要靠client helper去远程调用server helper,会牵涉到网络和IO,而这两个东西是最爱出异常的。

那么,信息是如何从一个JVM,传到另一个JVM的呢?
答:要看具体用的什么协议了。JRMP或IIOP。一般两个java程序之间,远程调用用的是JRMP协议,而IIOP是用来调用非java程序的。

在RMI中,client helper 称为STUB,server helper称为 SKELETON.

创建远程服务的步骤 , 先简介,后面详述

1、创建自己的Remote Interface
里面要定义你的client能远程调用哪些方法,Stub 和 Skeleton都要实现这个接口。
2、实现远程接口Remote Interface
真正完成工作的类。
3、用rmic工具产生stubs 和 skeletons
4、启动RMI registry,翻译为注册单吧
用户去这里找client stub/helper 对象,找代理
5、启动远程服务

注意: 这都在讲的是你远程服务类的创建,这个远程服务类最终通过rmic工具,编程两个.class文件,然后一个客户用,一个服务用。这块看的就是晕,大概看看,然后一直往下走,就明白了。

步骤一 创建自己的Remote Interface

Remote 接口只是一个标识,里面没有需要实现的方法。

Remote接口在java.rmi.Remote。

1、要创建自己的Remote Interface ,首先要继承Remote接口。

注意: 再次提醒,接口可以继承。

public interface MyRemote extends Remote

注意: 客户使用服务的时候,实际上是用的MyRemote,你写的这个接口类型的引用。用的多态,然后把你写的服务类的对象,放在这个接口类型的引用中。为什么这么干呢?接口就像契约一样,规定了客户能调动的方法有哪些,客户要遵守规则,只能调动这个接口里写出来的方法。

2、你在自己的这个Remote Interface里声明的所有方法,都要声明抛异常。

public interface MyRemote extends Remote {
     public String sayHello() throws RemoteException;
}
因为会牵涉到网络和IO,必须要让调用服务的人知道,有风险,有异常,并且逼着它处理。

3、确保你写的方法的参数,和返回值都是基本类型,或者能串行化
public String sayHello() throws RemoteException;

因为返回类型要被包装,然后从server发送回来,回来后就会有个解包装的问题,就是去串行化。所以,返回类型必须支持串行化。

注意: 一般API的类型,arrays collections 什么的都支持串行化的,只是你自己写的类,要注意这个问题。

步骤二 写一个类,实现你刚才写的那个Remote Interface

1、实现刚才写的Remote Interface
写的这个类,就是我的服务类,实现上面的Remote Interface,是因为里面有我客户会调用的方法,我实现了客户才能去调。
public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote{
     public String sayHello() {
          return "Server says , 'Hey'";
     }
     //more code in class                     
}

2、继承UnicastRemoteObject类
为了作为远程对象使用,我写的这个类的对象,需要一些与“远程”相关的方法。最简单的就是继承UnicastRemoteObject类,不然要自己写。
public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote{

3、写一个没有参数的构造函数,并给他声明抛异常
新继承的类UnicastRemoteObject,有个不好的地方,就是它的构造函数会抛异常,所以你继承于它,也得抛异常,但你不用写东西。
public MyRemoteImpl() throws RemoteException {};

4、向RMI registry注册你的服务
现在你有了远程的服务,你必须让它能够被远程的客户访问。所以你要实例化你的服务,然后把它交给RMI registry。RMI registry必须启动,不然下面语句就失败了。当你注册了对象后,RMI系统实际是把stub放入了RMI registry。
用java.rmi.Naming类的静态函数rebind()来注册。
try {
     MyRemote service = new MyRemoteImpl();
     Naming.rebind("Remote Hello" , service);
} catch (Exception ex) {...}

注意:绿色句子部分,那个Remote hello字符串,是给了你的服务一个名字,这样客户在使用的时候,好在注册的服务表中,找到你的服务。当你调用rebind()这个函数的时候,RMI为 stub,把你的服务包装起来,然后把stub放入服务注册表。

注意: 绿色句子很重要,蓝色句子也很重要的。看到用的是接口类型的引用,然后放入的是完成服务的类的对象,用的多态。注册也注册的是这个引用。这样用多态、接口来约束用户的行为,用户只能调动接口内有的方法,别的方法就不能乱用了。

步骤三 生成stub和skeleton

用rmic,对刚才写的服务类运行它,产生两个类,一个stub,一个skeleton。

注意: 是给自己写的那个服务类用这个工具,不是接口。

注意: 末尾没写.class。

这里为了简练,其实rmic工具还可以设置很多事,比如用什么协议(IIOP?),是否产生skeleton等等。

包名也省略了。

步骤四 运行remiregistry
注意: 要确保你执行这句话的时候,能访问到你的类,最简单办法就是在你classes文件夹内运行。

步骤五 启动服务

注意: 启动的就是你刚写的服务类。

服务类代码完整示例

The Remote Interface:
import java.rmi.*;

public  interface  MyRemote  extends  Remote {
         public  String sayHello()  throws  RemoteException;
}

The Remote Service(the implementation):
import java.rmi.*;
import  java.rmi.server.*;

public  class  MyRemoteImpl  extends  UnicastRemoteObject  implements  MyRemote{

        protected  MyRemoteImpl()  throws  RemoteException {
               super ();
               //  TODO  Auto-generated constructor stub
       }

        @Override
        public  String sayHello()  throws  RemoteException {
               //  TODO  Auto-generated method stub
               return  "Server says , 'Hey'" ;
       }

        public  static  void  main(String[] args) {
              
               try  {
                     MyRemote service =  new  MyRemoteImpl();
                     Naming.rebind( "Remote Hello" , service);
              }  catch (Exception ex) {
                     ex.printStackTrace();
              }
                     
       }
}

客户怎么使用stub

客户主要是通过Naming.lookup()函数,来找到有哪些服务可用,然后再去调用这些服务。
注意: 感觉讲的很细,就不解释了。就开头service类型为MyRemote,接口类型注意看看,这是实现方式,多态。

客户完成服务的全过程:

客户怎么得到stub的 .class文件呢?

客户必须得有stub的class文件,就是前面用rmic生成的那个。不然的话,就无法去找服务,得到服务结构,和去序列化。

如果是个简单的程序,可直接手工把这个stub 的class文件交给client。

但是复杂的话,比较有用的方法是用 dynamic class downloading。本书不讲。基本就是要用哪个类,会通过URI,用HTTP去得到这个class文件。

确保每个机器都有他需要的class文件

RMI上,三个最容易忘的事:
1、忘了启动rmiregistry,就直接启动了远程服务。远程服务的启动,就是运行那个你写的远程服务类。

2、忘了提供的服务方法,的返回值,还有参数必须要能串行化。
注意:这里如果出错,编译不提示错误的,到了运行时候才出错,很麻烦。

3、忘了把stub class文件,交给client。

     
625~648 没好好看了。。servlet EJB JSP等。

终章 一些问题

位操作 , |   &    ^    >>     <<     >>>      <<<

断言

assert (height > 0); 

assert (height > 0) : "height = " + height; 这个方法保证: 后的表达式值,不为null就行,断言失败可以多点信息。

有断言时,编译:
javac TestDrive.java
即,没变化。

执行:
java -ea TestDrive

不变性 String 和 wrapper

前面也提到了,String这个类型很神奇,你String a = "aaa";String b ="aaa"。这a b 都引用的是同一个对象,只要值一样就是同一个对象。

看书上怎么讲。说java中,有个String pool,每创建一个String对象,就放在里面,然后如果创建的新String,java发现其值已经在String pool里面了,那就直接返回这个String的引用,不再创建新的String。

一个引用,无法改变一个String的值,当还有另一个String引用,指向该对象的时候。

然后,书上又讲,说GC是不去String pool 的。也就是说,String建立以后,这个对象就一直存在,没了引用也存在。

所以要处理很多String的时候,用StringBuilder这个类。

对于Wrapper,有个性质,就是Wrapper的值不会改变。Integer an = new Integer(43); 然后这个对象的值就一直是43,你an可以引用别的Integer去,但是对其做不出改动。

static nested classes

public class FooOuter {
      static class BarInner {
          void sayIt() {
               System.out.println("method of a static inner class");
          }
     }
}

class Test {
     public static void main(String[] args) {
           FooOuter.BarInner  foo = new  FooOuter.BarInner() ;
          foo.sayIt();
     }
}
注意: 到了吗?一般的内嵌类(inner class),是我要想获得内部类的一个对象,我必须先创建外部类的对象。而这里,因为nested class是static的,所以可以直接通过nested class的名字,直接创建nested class的对象。

注意: 提醒下,Test是在外部类的外面。

注意: static的nested class,只能随意的访问外部类的static的方法,因为只有外部类的static的方法,才保证不会和外部类的对象相关。

注意: 所有的inner class都是 nested class,而nested class不一定是inner class。 只要在一个类中的类,都叫nested class,但其中non-static的,称为inner class。

匿名类 比较牛X的东西

我们前面监听一个Button的时候,这么干:
QuitListener quitListener = new QuitListener();
button.addActionListner(quitListener);

然后写一个内嵌类QuitListener,实现ActionListener接口,写出actionPerformed()函数。

但现在可以这么干,去掉QuitListener那个对象的建立,然后写:
button.addActionListener ( new ActionListener() {
     public void actionPerformed(ActionEvent ev) {
          System.exit(0);
     }
     });

即,把本来该放监听对象的地方,直接放进去了一个类,而且这个类没有名字。

然后这个类里面还实现了接口要求的函数。

注意: new ActionListener()看着以为是建立对象的呢,但是 ActionListener是接口!不可能创建出来对象 ,所以这里语义就不是这个。

注意: 仔细看上面的几个括号,类的结束为},而addActionListener()函数结束用的),且有分号。

注意: new ActionListener()一般意思是建立一个对象,但是这里的意思就是 创建一个类,没有名字,并且实现了接口ActionListener,并立即创建这个类的一个对象,放入该处。

访问级别

四个访问级别,三个访问标识符,因为default级别,就是什么都不写,没标识符。

public 就是在任何地方都能访问public thing ,这里thing指class 变量 方法 构造函数等。

private 就是只有本class的才能访问,的thing。

注意:本class指的可不是对象,就说,两个Dog,是可以看到对方的private thing的,但是一个Cat 看不到。

default 是在本包内的,可以访问的东西。别的包就看不到了。主要用来隔开命名空间,防止污染。

protected 和default差不多,只是本包被声明为protected的,能够被包外的类继承。这样的话,比如我一个class 的方法,被声明为protected,你包外访问不了,用不成,但是你可以先继承,继承出来个子类后,你就能用了。

StringBuffer/StringBuilder

刚前面说了,String的不变性,很麻烦。所以操作String的时候,用StringBuffer/StringBuilder要高效的多。

很常用的,只是你不知道。

java5.0后,一般用StringBuilder,而不用StringBuffer,除非是你要求线程安全。

他有一大堆操作字符串的函数,自己查吧。。。。

多维数组

int [][] a2d = new int [4][2];
这是声明方法,它里面就是一堆对象的引用:


枚举

public enum Members {JERRY,BOBBY,PHIL};
这就像个类声明一样,其实枚举在java中,就是类。这里就建立了一个enum类的对象。

public Members selectedBandMember; Members类型的变量,里面就只能有三个值。

if (selectedBandMember == Members.JERRY)

还讲了一些,木有仔细看。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值