java路径踩坑,详踩!
关于java文件的运行原理都是首先转化成二进制的.class文件,然后交给java虚拟机去运行。但是我们在开发工具中,都是在src下面做开发的,我们怎么知道当前java运行的路径在哪里呢?
所以我们需要去了解java在开发和实际运行中的区别:
在普通java工程下:
我们首先进行分析(我们可以直接到盘符下,将我们的项目ctrl+c,然后利用xmind工具:思维导图工具,进入之后ctrl+v,它就会帮我们列出目录结构和文档类型):
这里就可以看出,其实虽然我们开发是在src目录下开发的,但是其实是运行是在bin目录下运行的,因我们都知道java文件运行是依靠.class文件的,我们就可以去对比bin目录和src目录的区别:
我们发现,src和bin目录下的文件除了.java文件和.class的文件,其它的目录结构和文件一模一样,所以我们得出,bin目录才是我们运行java项目的实际目录,并且结构和开发目录src完全相同(注意,所有在src目录下的文件都会拷贝过去,图片,文本等等:包括后缀为.css、.xml、.properties的各类文件)!
所以我们才能在普通java项目中直接使用相对路径,按照src下的目录结构都是能直接访问的。但是这里我们需要注意:在普通java项目中我们最好使用相对路径,因为类似这种绝对路径:
FileInputStream fis =new FileInputStream(“/内容详情.txt”);// 但是我们不建议名称使用中文
是肯定会出错的,在普通java文件下,直接‘/’绝对路径就到了盘符下了例如:E:/
那么怎么使用相对路径?我们怎么知道当前的相对路径在什么地方?那我们首先就要了解普通java项目运行的根目录在哪里:
File测试法:
首先我们在我们的xml项目下的src中新建一个test包,在test包下新建一个TestPath的java文件。
以上可以看出,不管我们的.java文件运行在哪个包下,项目的运行路径(即:当前路径),总会被置回到项目路径下。
所以在这里使用相对路径更好:
例如我要访问上面的:内容详情.txt
1) InputStreamin = new FileInputStream("./src/jdbc.properties");
2) InputStreamin = new FileInputStream("./bin/jdbc.properties");
这两种方式都可以获取,理解了以上的讲解我们就明白了。(” ./”代表当前路径)
在web项目工程下:
Web的目录结构和普通java工程在开发工具中,几乎一样,但是在运行的时候,目录结构完全不同;我们知道普通java项目是将.java文件编译成为二进制的.class文件之后,按照和src目录下同样的目录层次生成在bin目录下,其它位于src下的文件原封不动的拷贝到bin目录下;(如果有位于src外的类似lib之类的jar包目录,在生成自己的可执行jar包的时候,需要拷贝到和bin同级的目录下,这一点需要注意!)
我们来分析web项目的运行:
我们可以看出在web目录中,所有的src中的文件(除了.java)文件都被原封不动的拷贝到了webRoot下的WEB-INF中的classes中,而之前我们说的lib也同样位于和classes同级的目录下,而.java文件也同样被编译成二进制的可执行文件放到了classes下的和src中同样层次的目录下了,但是普通java文件不同这里没有了bin目录,注意:这里的根目录是classes,不是webRoot;因为我们的java文件可以“触摸”到的最大的范围就是在classes下,可以看出classes外的文件都和.class文件没有直接关系了。
File测试法:
同样我们新建一个TestPath的web项目,在下面新建一个servlet包,在其中新建一个TestPathServlet的servlet:
在eclipse中测试:
在myeclipse中测试:
可以看出这里的当前路径就和我们的项目没有半毛钱关系了!为什么呢?
因为我们的IDE的开发环境下的运行原理是,开发web项目每次运行之后,都会把路径切换到工具运行的目录下!!!这个路径对于我们开发者来说也没有任何意义,因为当我们的项目具体跑在tomcat上时,依照的目录和这个目录完全不同:
使用绝对路径:
使用盘符绝对定位;
使用相对路径:
将你要读的文件放在上面file测试出来的位置;
以上两种方式理论上是可以的,但是只是开发的时候管用,但是当将项目转到Tomcat上之后必然出错!!!
这里就可以看出Servlet中提供的ServletContext的getRealPath()方法的重要性了!(获得以当前项目为根目录的绝对路径)
Servlet中通过servletConfig对象去获得servletContext对象,然后ServletContext中提供了两种方法:
1) 获得绝对路径:getRealPath(“参数”); // 其中的参数是具体的文件名,也可以用”.”,” .”都代表当前路径,这种用法很重要!
2)获得上下文路径的方法:getContextPath();获取当前的相对路径,即项目名
【java设计者,为了简便开发者的操作,提供的跳过ServletConfig直接获取Context的方法,但是其实底层代码实现还是通过调用ServletConfig获得的】
我们测试运行此方法:
【注意我们当前的项目名称叫做TestPath】:
开发环境中的视图:
实际运行的时候在tomcat中的路径的视图:
这里我将TestPath项目运行到【Tomcat下的webapps中】:
【通过getRealPath(“参数”):获得当前项目的绝对路径,然后得到其中的hello.txt】:
FileInputStreamfis = new
FileInputStream(this.getServletContext().getRealPath(".")+
"/WEB-INF/classes/hello.txt");
补充:
当然读者认为我这种先找路径再new流对象的方式过于繁琐,其实也有一体的,这里就不再累述:getResourceAsStream(),可以达到获得路径,并且new 流对象两步一体;
请自行查阅!
如何在web和普通java项目下都适用呢?
举一个最简单的例子,在操作jdbc中,我们new一个JDBCUtil工具,但是因为用户名、密码、URL和导入驱动包的全路径,这四者是在我们工具中可能回重复修改的四个属性,为了我们的代码更友好,我们应该将这个四个字段抽离出来,例如的放到一个properties的文件中,利用集合的Property进行从.properties文件中进行读取。
但是我们的数据库连接不是只在web或者普通java项目中使用吧?怎么能在目录结构大不相同的两个工程中同样适用呢?
这里就需要使用到 Class.getResourceAsStream:
此方法怎么使用呢?其实就是相对路径的高级运用,首先我们随意获得项目下一个类的class对象,通过这个类的class对象我们可以获得当前的绝对路径,然后在当前路径上做变换,直接获得我们需要的文件的流!(因为此方法高级剖析涉及类加载器,如有兴趣,请读者自行查阅):
同样是File测试法:
首先在eclipse中的普通java项目下:
通过 ”.” 获得当前路径
数据库驱动加载成功,且当时sysout的路径为:
通过 “/” 获得当前路径下的运行根目录:
注意我们的目录结构:
【以下web目录中同样,不再赘述】
在myeclipse中的web项目下:
通过 ”.” 获得当前路径:
数据库驱动加载成功,且当时sysout的路径为:
通过 “/” 获得当前路径下的运行根目录:
以上也验证了我们在普通java项目下运行的根目录为bin目录;
在web项目下的运行根目录为classes目录;
总结:
在用File法进行测试的时候:
相对路径:” .” 代表当前目录,当前目录的本质就是java命令的运行目录!
Java项目:” .” 代表当前目录,及项目的根目录!
Web项目下 ‘.’当前目录指向%tomcat%的bin目录,因为tomcat的命令是在bin目录下启动的,运行目录就在Tomcat的bin下。在web项目下就不能使用相对路径!
在普通java项目中我们可以随意的使用相对路径,因为bin目录和src的相同性;避免使用绝对路径,绝对路径一跳就到了盘符根目录下了。
但是在web项目下,我们就要尽量使用绝对路径;数量使用ServletContext提供的getContextPath和getRealPath方法;
但是最大适用性上class对象. getResource()和class. getResourceAsStream是最好的选择!
学习参考:
https://blog.csdn.net/my__sun_/article/details/74450241
感谢!