杰 & C++ & Python & DM

2012年9月6日

命令行下Java的编译及运行(2)


  上篇博文中说明了如何通过命令编译及运行自己的Java文件。但是当前都是以项目为单位的,如何组织项目中的文件以及对项目的编译运行,是命令行编译主要解决的问题。

  1. 项目组织
  我们以下面的项目作为样例来说明:
Test/
|-- Test.jar
|-- classes
|   |-- Main.class
|   |-- OutterTest.class
|   `-- inner
|       `-- InnerTest.class
|-- run.py
|-- run.sh
`
-- src
    
|-- Main.java
    
|-- OutterTest.java
    `
-- inner
        `
-- InnerTest.java

  上面是一个项目:Test。其中,有两个文件夹,src用来放置所有的源代码,也就是.java文件;classes用来放置相应的.class文件。Test.jar是最终生成的jar文件,run.py和run.sh是项目的脚本文件。下面列出三个.java文件,只是简单的显示一句话:
// Main.java
import inner.*;
public class Main
{
    
public static void main(String[] args)
    {
        System.out.println(
"main: hello word!");

        OutterTest out1 
= new OutterTest();
        out1.hello();

        InnerTest in1 
= new InnerTest();
        in1.hello();
    }
}

// OutterTest.java
public class OutterTest
{
        
public void hello()
        {
                System.out.println(
"Hello OutterTest!");
        }
}

// InnerTest.java
package inner;
public class InnerTest
{
        
public void hello()
        {
                System.out.println(
"Hello InnerTest!");
        }
}

  注意上面的InnerTest类,它在package inner中,所以将InnerTest.java放在inner文件夹下,这样可以保证统一。在Eclipse中,新建一个类时,会让你填写package名,然后Eclipse会为你新新建一个相应的文件夹。

  2. 项目编译
  
  javac -d classes src/*.java src/inner/*.java
  
  由上一篇知道,javac中-d表示”指定存放生成的类文件的位置“,也就是将生成的.class文件放在-d指定的文件夹中。需要指出的是,classes文件夹是手动建立的。
  另外,javac还可以批量编译.java文件,上面的命令表示编译src目录下的所以.java文件、编译src/inner目录下的所有.java文件。这样就可以批量编译.java文件,并将生成的.class文件放在classes文件夹中。这里同样要指出一点,因为package inner的关系,会自动建立inner文件夹,并将所有package inner下的类文件.class放在classes/inner下。

  3.项目打包
  
  jar -cvf Test.jar -C classes/ .

  这个命令将会把classes下的所有文件(包括.class和文件夹等)打包为Test.jar文件。
  上篇博客中,介绍了参数-C的意义:-C  更改为指定的目录并包含其中的文件,如果有任何目录文件, 则对其进行递归处理。它相当于使用 cd 命令转指定目录下。
  注意后面的"."的用法,jar可以递归的打包文件夹,"."表示是当前文件夹。如果执行命令“jar -cvf Test.jar .”,表示将当前目录下的所有文件及文件夹打包。所以上面的命令的意思就是“将classes下的所有文件打包为Test.jar”。

  4.项目运行
  
  java -cp Test.jar Main

  通过上面的命令就可以执行Test.jar中的Main.class。其中,cp指定了jar文件的位置。

  5. 脚本文件
  通过上面的几步,我们就可以完成整个项目的编译和运行。而且,通过src和classes两个文件夹将源文件和目标文件分开,使项目比较整洁。但是如果每次编译、运行都要输入上面一系列命令,还是比较繁琐的,尤其当项目文件较多时,这时通过脚本文件管理整个项目是明智的选择。
  下面是项目的脚本文件run.py
import os
import sys

if __name__ == "__main__":

        ProjectJar 
= "Test.jar"

        
if sys.argv[1== "c":
                
print("Compile program.")

                src 
= "src/*.java src/inner/*.java"
                os.system(
"javac -d classes " + src)
                os.system(
"jar -cvf " + ProjectJar + " -C classes/ .")

        
if sys.argv[1== "r":
                
print("Run program.")
                os.system(
"java -cp " + ProjectJar + " Main")

        
print("Over!")

  通过这个脚本文件,可以使用"python run.py c"完成项目的编译、打包;使用"python run.py r"运行项目。

  通过这篇文章,我们已经了解了Java项目的管理方法,以及编译、打包、运行的命令行,最后介绍了使用脚本文件有效管理项目。
  附件中包含整个项目,同时还包括一个run.sh,方便不熟悉python的人查看。
  附件: Test.tar

posted @ 2012-09-06 18:56 jaysoon 阅读(3271) | 评论 (0)编辑 收藏

2012年8月22日

命令行下Java的编译及运行(1)


  当前大部分开发者在开发Java程序时使用Eclipse,它可以方便的进行程序的编译、打包和运行。但是不使用IDE,在完全的命令行下进行Java开发者从未用过的。在命令行下进行开发不是用来在展现自己有多牛,而是通过命令行开发,可以对Java的编译、jar包等各个部分有一个深入了解。
  下面的几篇博客将会对Java的编译、打包和运行方法由浅入深的进行介绍。
  在这里使用的操作系统是Linux,并提供相应的shell和python脚本。

  首先介绍一下三个常用的命令:javac、jar、java。每个命令都有不同的参数,这些参数的用法会详细介绍。

  1. javac
  javac的功能是对java源代码进行编译,将后缀为.java的文件编译为.class的文件。javac的一般格式是
  javac <选项> <源文件>
  例如:
  javac Main.java
  会产生Main.class文件。
  
  javac的常用选项有:
  -classpath <路径>      指定查找用户类文件的位置
  -cp <路径>                指定查找用户类文件的位置(与上面的选项一样,cp是classpath的简写)
  -d <目录>                指定存放生成的类文件的位置

  2.jar
  jar的功能是根据选项将指定的一些.class文件打包为一个jar包。jar的一般格式是
  jar {ctxui}[vfm0Me] [jar-文件] [manifest-文件] [-C 目录] 文件名 ...
  例如,
  jar cvf Test.jar Main.class Bar.class
  它将Main.class和Bar.class打包为一个文件Test.jar。

  jar命令的选项比较多,用到选项包括:
  -c  创建新的归档文件
  -t  列出归档目录
  -x  从档案中提取指定的 (或所有) 文件
  -u  更新现有的归档文件
  -v  在标准输出中生成详细输出
  -f  指定归档文件名
  -m  包含指定清单文件中的清单信息
  -e  为捆绑到可执行 jar 文件的独立应用程序,指定应用程序入口点
  -C  更改为指定的目录并包含其中的文件,如果有任何目录文件, 则对其进行递归处理。

  例如,
  上例中的cvf参数,分别表示创建新的jar文件、创建时显示jar包的信息(可以执行看一下)、指定jar包名为Test.jar。
  jar tf Test.jar      查看Test.jar的内容,其中t表示列出jar包内容,f指定jar包名
  jar xf Test.jar      解压Test.jar文件
  jar xf Test.jar Main.class      仅解压Test.jar中的Main.class文件
  这里要指出的是,f/m/e都指定一个名称(jar包名, 清单文件名和入口点名称),相应的名称顺序与参数的顺序要一致。

  3.java
  java的功能是执行应用程序。java的一般格式是
  执行一个类:    java [ options ] class [ argument ... ]
  执行一个jar包:java [ options ] -jar file.jar [ argument ... ]
  例如:
  java Main
  执行Main.class,注意上面没有.class后缀
  java Test.jar
  执行一个jar包,这个jar包中要指定了程序入口点(通过在MANIFEST.MF文件中指定)。

  常用的java的选项:
  -classpath<类搜索路径>    指定用户类文件的位置,可能为文件夹、zip、jar文件


  总结
  通过这篇内容,我们应用学会了如何使用javac编译自己的类,并使用java执行自己的类。但是关于打包的操作及jar的执行比较复杂,将在以后继续介绍。


  

posted @ 2012-08-22 15:45 jaysoon 阅读(593) | 评论 (0)编辑 收藏

2012年8月17日

Java中计算中文的MD5值

Java中计算中文的MD5值
  前几天的工作中,需要计算中文的MD5值,计算的函数接口及调用方式如下:
public static String getMD5(byte[] source);
String s 
= "中文编码";
String md5_value 
= getMD5(s.getBytes());
  其中getBytes函数使用平台默认的字符集将string编码为byte序列。由于平台的中文编码方式可能不同,所以同一中文经过getBytes得到的二进制是不一样的。为保证每次得到的结果一致,或者使用指定的编码方式得到byte序列,应该在getBytes中使用参数。
String md5_value = getMD5(s.getBytes("utf-8"));
  这样得到的值就是一样的。

posted @ 2012-08-17 15:32 jaysoon 阅读(1266) | 评论 (1)编辑 收藏

2012年5月14日

遇到java与crontab问题


  crontab命令是Unix/Linux中的一个常用命令,用于设置周期性被执行的指令。没有用过的可以查一下,在运行服务端程序时会经常用到。

  程序使用Java读取一个含有中文的文件,进行处理后,将结果写到一个结果文件中。在运行的程序时,出现了这样一个问题:在本地环境下,运行正确;但是当使用crontab定时执行时,得到的却是错误的结果。

  经过一定的调研发现原来是对crontab的机制没有弄明白导致的。crontab运行程序时,会使用它自己的环境变量,这个环境变量与你本地的环境变量可能会不同。比如,在你本地情况下,环境变量的语言为en_US.UTF-8,而在crontab中,环境变量可能是zh_CN.GBK,这样会导致读写文件时——尤其是读写中文文件时内容编码错误,进而导致结果出错。

  所以在使用Java读写文件时,一定要指定编码格式,而不是使用环境变量的格式。例如下面的语句用于读取utf-8格式的文件

String encodeType = "utf-8";
File in_file 
= new File("test.txt");
BufferedReader reader 
= new BufferedReader(new InputStreamReader(new FileInputStream(in_file), encodeType));
  
  网上也有关于修改crontab环境变量的方法,但是我不建议使用,因为一个系统可能是很多人共用的,修改crontab环境变量可能会引起他人程序的问题,最好修改自己的程序,保证它不依赖具体的环境变量。



posted @ 2012-05-14 15:20 jaysoon 阅读(741) | 评论 (0)编辑 收藏

2011年7月20日

算法的态度

很多同学和朋友经常向我抱怨说,算法已经成为他们学习和工作中的一个负担。在他们看来,算法只能应付一下考试,在学习中没有任何用处;也有人说,求职过程中考算法纯粹是浪费精力,在实际工作中没有用到任何算法。

在我的本科四年中,花了大量时间学习算法,参加算法比赛——成绩很滥,朋友们都说,和我的付出严重不成比例,但是我一点也不后悔花费时间学习算法。算法让我在平时的学习中应对各个科目都从容自如,学习起来事半功倍。

在平时的学习交流中,有时看其他人的代码相当别扭,不是看不懂(有时真看不懂),而是很纳闷,为什么要这么写呢,明明有很明显的顺畅的思路,干吗要写这么绕呢?我将我觉得正确的思路写出来的时候,对方也会觉得这样写顺畅了很多,代码也小了很多。

在一般应用中,不会说让你写一个KMP算法,或者给你设计一个动态规划的题目让你来解。但是如果你会这些算法,掌握了这种优化的思想,在平时的编写中会不知不觉的写出比较高效的代码。

有人说,现在中国的软件现状就是这样,不在乎你实现的有多快,只会在乎你有没有实现所有的功能点,随着硬件条件的改善,一般情况下都能满足要求。这样说也是没错,如果你甘愿只做一名码工,因为对于相当大的一部分软件,效率并不是最重要的,甚至第二、第三重要都排不上。但是对于企业级的应用和软件的核心部分,往往效率是非常重要的。

如果将编程和写作类比的话,可以进行这个的比较。编程必须会语言C/C++JavaPython等,写作也一定要会一个语言汉语、英语、法语,当然计算机语言有效率的优劣以及适合的应用的场景,而自然语言没有这种属性。算法修养之于编程就像文学修养之于创作,没有一定的文学修养就写不出好的作品,同样没有良好的算法修养也创造不出好的代码。数据结构就像写作中的排比句等,它们是骨架,结构化编程就像是写作中的段落,它们使得层次分明。

在静静的夜晚,明月透过窗子,照进小屋,顿时一种异样的情怀涌上心头。这时有下面几种人,第一种,嚎啕大哭,“妈妈……”;第二种,潸然泪下,“我嚓,想家了是不”;第三种,沉思状,“有首诗可以表达这个情怀”,拿起全唐诗,查找了整晚,夜尽天明时,惊呼“找到了,原来是‘举头望明月,底头思故乡’啊,我记得有的。”;第四种,徘徊低吟“举头望明月,底头思故乡”;第五种,心中油然而生,“独在异乡为异客,明月千里寄乡思”。

同样地,这也反映了一个程序员算法修养的几种境界。第一种,菜鸟,不会表达自己的思想,这种多见于初学语言的人,只会写hello world之类程序的人;第二种,入门,可以表达出自己的想法,但是不懂什么算法,只是凭自己对计算机语言的理解写程序,当然写出的程序功能点可能不少,但是不完善,潜在的bug很多;第三种,有一定的算法基础,但是没有掌握完全,写程序时有高效的意识,对一些核心程序,看着资料也能写出相应的代码,但是不能活学活用;第四种,有不错的算法修养,写出的代码清晰高效,可胜任关键代码的编写;第五种,有很高的算法修养,理解了算法的精髓,自己可以得出算法供世人学习,一般是大师级人物。国内有相当一部分程序员就是处于第二阶段,他们也有稳定的工作,甚至不错的收入,但是总感觉遇到了瓶颈,自己的能力踌躇不前。

有人说,现在各种库都有,那些代码都非常高效,我只要熟练使用这些库就行了,不用学习算法。可以达到熟练使用几个库的程序员也算是第三阶段,但是要想熟练驾驭更多的库,将库函数熟练的使用,没有一定的算法基础是不行的。比如,如果你想熟练使用STL库,那么对各种容器和算法都应该有一定的认识,什么时候使用list,为什么快排不能应用于list?要想回答这些问题,必须掌握初步的算法。

对于想要在技术上有一定作为的程序员,没有算法修养,就如同金庸小说中的学武之人不学习内功,它可以将外功学的炉火纯清,凭着这份功力可以走遍半个江湖,但是一旦遇到高手就挂了;反之如果有内功修为,学习外功可学一知十,触类旁通,内功修为越高将来的成就越大。

学习算法吧!

posted @ 2011-07-20 23:59 jaysoon 阅读(526) | 评论 (2)编辑 收藏

2011年1月21日

数据挖掘中的指数函数

            最近做数据时,使用神经网络建模。在神经网络中,会用到激发函数(activation function)。

        典型的激发函数有Sigmod函数:

                  image

        双曲正切函数:

                 image

          这两个都涉及到指数函数,在C中,为求指数函数,使用exp()函数。

      在数次出错后找到问题,原来是我的指数值过大,数据中有时会出现超过1000的数字,这导致在求值过程中,即使使用double型,也使得结果溢出。

      解决方法是定义一个指数函数,当指数值超过一定界限便指定一个相对无穷大的值,这样也符合数学定义。在我的处理中,将界限设定为15,当该值大于15时,返回3000000;当界限值小于-15时,返回0。

posted @ 2011-01-21 23:40 jaysoon 阅读(1094) | 评论 (0)编辑 收藏

2010年4月11日

配置本地文件共享

    在配置文件共享时,对没有经验的用户会出现很多问题,这里介绍三点注意事项:
    1. 首先打开菜单中“工具”->“文件夹选项”->“查看”,确认“使用简单文件共享”没有被选上;
    2. 右键“我的电脑”,“管理”->“系统工具”->“本地用户和组”->“用户”,右键“Guest”,“属性”,在“常规”选项卡中,取消“账户已停用”;
    3. 打开“控制面板”->“管理工具”->“本地安全策略”,打开“本地策略”->“用户权利指派”,双击“拒绝从网络访问这台计算机”,确认其中的“guest”已删除。

    
     本文针对XP用户,其它用户可参照。

posted @ 2010-04-11 17:08 jaysoon 阅读(253) | 评论 (0)编辑 收藏

2009年4月29日

赋值运算符和复制构造函数

      赋值运算符复制构造函数都是用已存在A的对象来创建另一个对象B。不同之处在于:赋值运算符处理两个已有对象,即赋值前B应该是存在的;复制构造函数是生成一个全新的对象,即调用复制构造函数之前B不存在。   
      CTemp a(b);      //复制构造函数,C++风格的初始化
      CTemp a=b;      //仍然是复制构造函数,不过这种风格只是为了与C兼容,与上面的效果一样
      在这之前a不存在,或者说还未构造好。

      CTemp a;
      a=b;               //赋值运算符
      在这之前a已经通过默认构造函数构造完成。

      我觉得将赋值运算符称为“赋值构造函数”是错误的,构造函数发生在对象创建时期,而赋值是运算符,发生在“运算”时期,赋值运算前,对象已经构造完成,所以不能称之为“构造函数”。
      一家之言!!

posted @ 2009-04-29 18:32 jaysoon 阅读(1888) | 评论 (2)编辑 收藏

C++是类层次保护的

 

class CStack
{
public:
       CStack();  
       CStack(
int  size); 
       CStack(
const CStack& st);   //复制构造函数,注意使用引用
private:    
       
int _size;
       
int _count;
       
int* _s;           
}
;
CStack::CStack(
const CStack& st)
{
      _size
=st._size;
      
if(_size > 0)
      
{
               _s 
= new int[_size];
               _count 
= st._count;                  //访问st的私有数据
               for(int i=0; i<_count; ++i)
                       _s[i] 
= st._s[i];             
      }
         
      
else
      
{
          _s 
=  0;
          _count 
= 0;           
      }
                                            
}

      在C++模型中,保护是在类层次应用的,而不是在对象层次。
      这句话的意思是,类的任意一个对象可以访问该类的另一个对象的内部的任何东西。在上面的例子中,复制构造函数中的赋值语句_size = st._size;访问了st的私有数据。
      这说明,C++中的访问保护是按照层次的原则定义的,那么什么事对象层次的保护呢?
      在Smalltalk中,成员函数可以当前对象的私有数据,但不能访问任何其它对象的私有数据,即使它们属于相同的类。

posted @ 2009-04-29 17:57 jaysoon 阅读(272) | 评论 (0)编辑 收藏

C++中关于构造函数的几个问题

首先我们给出一个类的例子

class CStack
{
public:
       CStack();  
       CStack(
int  size=100); 
       CStack(
const CStack& st);   //复制构造函数,注意使用引用
private:    
       
int _size;
       
int _count;
       
int* _s;           
}
;
CStack::CStack(
const CStack& st)
{
      _size
=st._size;
      
if(_size > 0)
      
{
               _s 
= new int[_size];
               _count 
= st._count;         
               
for(int i=0; i<_count; ++i)
                       _s[i] 
= st._s[i];             
      }
         
      
else
      
{
          _s 
=  0;
          _count 
= 0;           
      }
                                            
}


int main()
{
    CStack st;
    
return 0;    
}



一. 在main()中,对象st不知道该调用哪一个构造函数,因为CStack()和CStack(int  size=100)都满足条件,此时正确的编译器会立即标示这种冲突,强迫实现者解决这个问题。

二. 假设我们已经有了一个CStack对象s1
CSatck s2=s1;      //这个句子将会调用复制构造函数
CStack s3 = CStack(200);   //这个句子将会做什么呢?

首先,产生一个临时CStack对象,不妨命名为sss,并调用构造函数CStack::CStack(200),随后执行复制构造函数。也就是说,它相当于

CStack sss(200);
CStack s3(sss);

这很浪费时间,不过,大多数编译器还是能优化这种语句。但是我们为什么不养成一个好的习惯,直接写成
CStack s3(200)呢?

posted @ 2009-04-29 17:45 jaysoon 阅读(314) | 评论 (0)编辑 收藏

仅列出标题  
<2024年4月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

导航

统计

常用链接

留言簿

随笔分类

随笔档案

文章分类

文章档案

收藏夹

C++

搜索

最新评论

阅读排行榜

评论排行榜