Java中包(Package)的深入理解

被一个java新人请教关于包的理解和使用,想起了自己当年刚学java时候的体会。

包-Pakage,概念理解和使用不难,但对于刚接触java的人而言,有点抽象和不解。

人人皆知,java中的包主要用于解决类的重名问题,类似于XML、C#,PHP等命名空间的概念,但与这些语言有所区别,它既有命名空间的逻辑分割又有物理上的实际目录划分。

一.使用

pakage mosang.tech //表明A.java 源文件中,所有类都位于mosang.tech包中
public class A{
public static void main(String [] args){
new B().showInfo();
}
}
class B{
public showInfo(){
System.out.print("this a method of how to using pakage");
}
}

假设我们已经将classpath设置为A.java所在的目录:

上述代码运行编译后会自动生成mosang/tech文件夹,同时得到一个B.class字节码文件位于tech文件夹中

javac -d . A.java

这时候,运行B.class文件需要带完整包名,哪怕我们在命令行窗口进入了mosang/tech目录。

java mosang.tech.B

这就是包的基本用法。

二.陷阱一:类名的使用

如果我们在命令行进入mosang/tech目录运行B.class,编译器会提示找不到文件,因为类的名字已是mosang.tech.B而不是B。

二.陷阱二:classpath路径与包名

JVM在加载带包名的路径时候,会先到classpath指定的目录,再从此按照包名结构去查找class文件。如果我们在命令行进入mosang/tech目录运行mosang.tech.B,同样会报错,因为此时的完整路径变成了classpath/mosang/tech/mosang/tech/B.class

三.陷阱三:包名与目录名

java中,包名必须经过程序中pakage语句来指定,而不是靠目录结构来指定的,是先有了包名,才需要相应的目录结构。我们来做个试验:

删除原生成的B.lass文件,在源代码A.java中,将pakage语句注释掉,重新编译得到B.class,将B.class拷贝到mosang/tech文件夹中,classpath目录下运行mosang.tech.B,会出错,因为此时的类名是B而不是mosang.tech.B。

所以,我们常常误以为把一个类文件放到了一个目录中,这个目录结构就自然成了包名,这就大错特错了。这点很多人包含有多年java经验的人都会犯错,绝大部分新手更是有这种自以为是理解。

 

 

 

AJAX跨域与URI大小写问题

以前解决过很多异步请求跨域的问题,自认为不会有什么难点,可今天调试一个项目API的时候遇到了之前没遇到的问题,弄了好久才解决,记录如下:

项目中涉及一个主域(以www.abc.com表示),子域(以s.abc.com表示),子域控制器s.abc.com/Action向主域www.abc.com/Reply发异步POST请求,www.abc.com/Reply发出Header参数“Access-Control-Allow-Origin:http://s.abc.com““Access-Control-Allow-Methods:POST”,按理说这应该没什么问题了,但问题来了:
1. 直接访问通过URL:www.abc.com/Reply访问,可以看到添加的“Access-Control-Allow-Origin“等响应头,也能正常传递JSON,如下:

Access-Control-Allow-Methods: POST
Access-Control-Allow-Origin: http://s.abc.com
Access-Control-Expose-Headers: Authorization
Content-Encoding: gzip
Content-Length: 138
Content-Type: text/json; charset=utf-8
Date: Wed, 27 Mar 2019 07:03:09 GMT
Server: Tengine
Vary: Accept-Encoding

2. 但通过子域的AJAX请求,死活就得不到添加的Headers参数,FF及Chrome均提示提示同源错误“原因:头缺少 ‘Access-Control-Allow-Origin’”,明明已经设置,就是没有传递,而之前做的一个接口几乎代码一样,但却能正常返回值,确实让人百思不得其解。
无奈之下,只能检查服务器设置,发现URLrewrite有如下规则:

<rule name="LowerCaseRule1" stopProcessing="true">
                    <match url="[A-Z]" ignoreCase="false" />
                    <action type="Redirect" url="{ToLower:{URL}}" />
</rule>

 

恍然大悟!

这个是SEO常设规则,用来规避因为URL大小写而带来搜索引擎识别为不同站点的问题,而项目很多CLASS命名都根据大小驼峰规则,Controls也是大小混写,这条重写规则强制将文件名小写返回,而AJAX请求则认为是两个不同请求,导致Headers参数无法正常接收。

取消此规则,问题解决。

jquery $().val的bug

近来研究一些开源的前端框架,重拾js,发现一个奇怪问题:
input如果设置为display:none,那么通过$(“#id”).val(“value”);方式赋值,无论是chrome、firefox还是IE都是传值方式失效。
改用$(“#id”).attr(“value”,”value”);却又可以
但是如果设置type=”hidden”,同样使用$(“#id”).val(“value”);方式赋值却又可以。

被深深感染的唢呐声

激情、技术、美感,让一个完全不懂音乐的外行都感受到了。

——刘英大师唢呐演奏

table doesn’t exist,mysql文件备份导入的问题

今天在兆维工业园IDC帮一位朋友迁移web服务器,他将之前拷贝的mysql数据库文件还原到新服务器中,结果有部分表在PHPmyadmin中没显示,而用Navicat 连接,能看到表,却打不开,提示”table doesn’t exist”。web应用显然不能正常运行。为何会这样,明明有表,却不能显示或是doesn’t exist?

我们知道,MySQL备份可直接拷贝datadir下的数据库文件,还原时将拷贝文件放到mysql下的data目录下就可以。但是今天却出现这种情况,是什么原因?

MySQL存储引擎分MyISAM与InnoDB,两种类型最主要的差别就是Innodb 支持事务处理与外键和行级锁,而MyISAM不支持。MyISAM类型的表强调的是性能,其执行速度比InnoDB类型快,但是不提供事务支持。

MyISAM类型的表,都对应data下的一个文件夹,文件夹名字就是表名,数据存储在该文件夹下三个”Table.frm”,”Table.MYD”,”Table.MYI”中;InnoDB的表,数据和结构是分别存储的,数据存储在ibdata1文件中(默认),而结构存储于.frm文件中。

我们常用的直接拷贝”Table.frm”,”Table.MYD”,”Table.MYI”方式来备份数据库,对MyISAM类型是有效的。但是对于InnoDB的表,还必须拷贝ibdata1文件。

之所以出现上面提到的问题,原因就是在于有部分是InnoDB表,却没有拷贝ibdata1文件。

出现错误后,直接拷贝ibdata1文件覆盖,还是问题依旧,这时只要删除log文件(ib_logfile*)就可以了。

这其实也是我多年前遇到的问题,当时备份数据库没注意,没有拷贝ibdata1,导致很多数据丢失,是得了教训的。

数据库备份一定要十分小心,不仅要测试,还要多种方式备份,最为稳妥的是导出sql文件,或用专业备份工具,多种形式备份。

 

 

 

陌桑教程:自己动手编写一个PHP开源框架(三)

前面的实例中,我们通过router.php对于不同请求分配给不同的control类来处理,而control又根据不同的请求内容,调用不同的model类来提取数据,基本流程如下图所示。现在我们需要来做的是通过view将数据展现出来。

view视图就是最终数据展现的模板,一般而言,视图层通常希望由前端人员来做,而前端人员常常对后端技术不了解或者我们不希望他们更多了解后端是怎么处理数据的,他们只需要通过简单的语法调用数据就可以。这样做的目的,达到数据页面的分离。我们在views文件夹下新建一个view_photo.php文件,代码如下:

<html>
<head></head>
<body>
<img src="<?=$data['photosrc'];?>" title="<?=$data['photodescri'];?>">
</body>
</html>

这是我们很熟悉的HTML,你可能会问这和以前php与html代码混合编程有何不一样?,这虽然有些类似但是有一点不同是,我们的业务逻辑已经和视图分离了。在后面的优化中,我们会将显示数据的语句做得更简洁,完全让前端人员不需要输入php语法[discuz!,dedecms等就是用简单标签来在view层显示数据的]。

陌桑教程:自己动手编写一个PHP开源框架(二)

五.创建Model模块文件

我们已经能通过类似index.php?channel=photo&content=latest来识别访问的不同频道,但是我们希望controllers只是负责action的分发,具体读取和显示数据分别由models和views来实现,这样能有更好的扩展性。

我们在models下面新建一个photo.php文件用于读取图片信息,这个信息通常存储在数据库或者是文件中,为了简单化理清思路,我们先不管从何处得到数据,而是直接显示一张图片。

<?php   
class photo_Model  
{  
    public function __construct()  // 构造函数,创建对象时自动执行
    {  
        echo("<img src='http://www.mosang.net/mvc/link.jpg'/>");  // 显示一张图片
    }  
}  
?>

下面更改controllers文件夹下的photo.php,我们不再希望photo.php直接输出信息,而是交给models下面的photo.php来实现。代码如下:

<?php
class photo_Controller {
    var $channelname ;
    function showcurrent($channelname) {
    $this->$channelname = $channelname;
    $photosModel = new photo_Model($channelname);
    }
}
/*
删掉了 echo("您正在访问的频道是:".$channelname)这条语句。新增了创建图片模型类的语句
*/ 
?>

此时,我们输入地址会报错,因为还没有引入models下面新建一个photo.php文件。更改controllers目录下router.php,代码如下:

<?php
$channel = $_GET["channel"]; //获取频道名
$action = $_GET["action"];
$channelFile = SERVER_ROOT . '/controllers/' . $channel . ".php"; //频道名对应的php文件
$ModelFile = SERVER_ROOT . '/models/' . $channel . ".php"; //频道名对应的php文件
if (file_exists($ModelFile)) {
    include_once ($ModelFile); //频道名对应的php文件引
    
} else {
    die("不存在该频道的Model文件!");
}
if (file_exists($channelFile)) {
    include_once ($channelFile); //频道名对应的php文件引入
    $channelclass = $channel . "_Controller";
    if (class_exists($channelclass)) {
        $controller = new $channelclass; //创建实例
        $controller->showcurrent($channel); //输出入当前频道名
        
    } else {
        die("不存在该频道的类!");
    }
} else {
    die("不存在您请求的频道!");
}
?>

现在在地址栏中输入域名+ index.php?channel=photo&content=latest,可以得到以下运行结果:

现在复制models下的photo.php文件,并放在同一目录下并更改文件名为news.php,更改news.php代码如下:

<?php   
class news_Model  
{   var $channelname;
    function __construct($channelname) 
    {   $this ->$channelname = $channelname;
        echo($channelname."频道为您展示的内容:慕尼黑市计划再次投票是否从Linux迁回到Windows");  
    }  
}  
?>

同时修改controllers文件夹下的news.php,代码如下:

<?php
class news_Controller {
    var $channelname;
    function showcurrent($channelname) {
    $this -> $channelname = $channelname;
    $newsModel = new news_Model($channelname);
    }
}
?>

 

运行域名+ index.php?channel=news&content=latest,可以得到以下运行结果:

news频道为您展示的内容:慕尼黑市计划再次投票是否从Linux迁回到Windows

到目前为止,我们大致了解了controllers,models请求处理的分工。