Thread.currentThread().getContextClassLoader().getResourceAsStream("jdbc.properties");
这个方法来读取配置文件。
理由真的五花八门:有说在JavaEE服务器中里面classpath路径跟JavaSE里面的不一样,也有说类加载器不一样的。
我一脸懵逼,但还是试了一下,发现居然还真可以。
但是我代码里面并没有涉及到服务器的东西,又怎么会有区别呢?
于是我尝试把这两种做法的类加载器都输出出来。

得到的结果,基本上是否认了网上说的一些类加载器不一样的说法,因为两个加载器都是一模一样的。
(他们的说法可能在部署了服务器的时候有效)
而此时我把代码重新用回了第一种加载器来进行加载properties文件,发现代码顺利执行了。
这就更加肯定了,我的这个问题并不在于这两个方法的区别上。
观察文件目录结构

工程目录src/main/resources下的文件一般在编译后是会自动复制到target/classes目录下的。
但是也有例外的情况,IDEA有可能会自己抽风,并没有成功把resources中文件复制到classes对应的目录下。
这样,所以一开始无法读取成功的原因就是target的classes文件夹下缺少了这个properties文件。
我把这个target的classes文件夹下的这个properties文件,删除后,分别用两个不同的方法来进行读取。都报空指针异常。

因此,我的问题并不在于这两个读取方法的区别上,而是在于classpath中缺失配置文件。
- 尽管看了很多跟我这个问题并不扯上关系的资料,但是通过查阅资料了解到了在JavaEE部署上一般都用这种获取类加载器的方式来获得静态资源文件,而并非通过用户文件夹相对路径来查找。
- 这两个类加载器上确实有区别,网上资料中推荐使用Thread.currentThread().getContextClassLoader().getResourceAsStream(“jdbc.properties”)这种方法,提前知道,可以避免日后踩坑。
- classes文件夹其实就是技术资料所说的classpath。
2021.3.28问题描述今天在学习尚硅谷小项目的时候看到老师写的代码中用到了JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");这样一段代码,目的是为了加载jdbc.properties这个配置文件,但是我发现我自己使用的时候是空指针异常。非常莫名其妙。于是乎,在网上找了一堆资料,发现很多人都在文章中推荐使用Thread.currentThread().getContextClassLoader().
Exception in thread “main” javax.imageio.IIOException: Can’t read input file!:无法读取输入文件!
原可以从两个方面分析:
路径中文乱码问题
target/classes文件夹中并没有找到图片
加粗样式看下代码:
public class ImageUtils {
private static String basePath=Thread.currentThread().getContextClassLoader().getResource().getPath();
public static v
1. 写一个单元测试类,命名方式:XxxTest(测试类没有main方法)
2. 导入包,Junit4包
选中项目,右键 => Build Path => Add Library => 选中Junit
选中Junit4 => finish
3. 在测试类中,设计测试方法,命名方式:testXxx()
在方法上方会自动添加一个@Test注解
4. 运行,右键run as JUnit方式
如果是全部执行,直接选中 @Test注解,右键run as JUnit方式
如果是某一个方法,直接选中方法名,右键run as JUnit方式
@Before// 预执行注解,每次执行方法前都会执行该注解
@After// 每次执行方法后都会执行该注解
5. 运行后结果解释:
绿条:表示成功执行
红条:表示执行失败
1. 代码有异常
2. 执行失败,逻辑异常
6. 自己扩展(测试类中的断言assert)
2. 工具类:(掌握)
1. 概念:就是类中只有(静态、非静态)方法,就是为了调用方法。
2. 作用:就是一个轮子
3. 分类:
1. 静态的
方法全部都是static修饰的,调用方式:类名.方法名(...);
2. 非静态的
通过单例模式获取到对象后。就是只有一个对象的类
方法全部都是非static修饰的,调用方式:对象名.方法名(...);
4. 工具类命名:
XxxUtil、XxxUtils、XxxTool、XxxTools
3. jar 包 (掌握)
1. 什么是jar包:就是别人封装好的字节码文件
2. 作用:就是提高开发效率的。使用轮子
3. 使用:
3.1 使用别人写好的jar包
step1:在当前项目下,创建一文件夹folder,叫lib
step2:将jar包复制到当前的lib文件夹中
step3:将所有的jar包选中右键,build path
add library
step4:在我们代码中通过创建对象或者类名的方式使用即可
3.2 使用自己的jar包 (造轮子)
step1:选中自己想要封装成jar包的类,右键export导出
在输入框中输入 jar:
1. jar File : 普通的jar包,没有main方法(最常用)
在下面写一个jar包的名字,然后选择生成jar包的路径,finish即可
2. Runnable jar File:可以自己执行的jar包,有main方法 (几乎不用)
要封装为可以执行的jar包,必须先运行一下当前类。
然后在下面写一个jar包的名字,然后选择生成jar包的路径,finish即可
在cmd控制台中,切换路径到jar文件所在的目录。
输入 :java -jar jar包名.jar
step2:剩下的就是按照 3.1中的步骤使用即可
4. Properties资源(配置)文件的解析(重点)
---------------------------------(注意:重点)-------------------------------------
以后配置文件都要放到项目中的一个或者几个单独的文件夹中,为了好管理。
该文件夹类型是source folder类型,源文件夹,eclipse工具会自动编译
---------------------------------------------------------------------------------
传统方式获取流是new创建的,而在web开发中有可能获取不到配置文件,为了避免这种情况,用以下方式获取流:
1. 通过当前类字节码文件的方式,一般是用当前类的字节码文件
当前类.class.getResourceAsStream("/文件路径/文件名");
/:如果是resource文件夹,直接写文件名,如果是普通文件夹写文件夹名/文件名
2. 通过类加载器的方式
1.同当前类的加载器获取
1.当前类.class.getClassLoader();//获取当前类加载器
2.classLoader.getResourceAsStream("文件路径/文件名");//通过类加载器获取流对象,如果是源文件夹,直接文件名
2.通过当前线程的类加载器获取流
1、ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); //获取当前线程类加载器
2、InputStream is = classLoader.getResourceAsStream("文件路径/文件名");//通过当前线程的类加载器获取流对象,如果是源文件夹,直接文件名
建议用当前线程类的加载器方式获取流,线程安全问题
类加载器:( 扩展,5个月后会讲)
加载字节码的一个类而已:
1. 启动类加载器(Bootstrap classLoader) c++
本地代码实现的类加载器,它负责将 <JAVA_HOME>/lib下面的核心类库
或 -Xbootclasspath选项指定的jar包等 虚拟机识别的类库 加载到内存中。
由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,
所以 不允许直接通过引用进行操作。
2. 拓展类加载器(Extension classLoader):
扩展类加载器是由Sun的ExtClassLoader(sun.misc.Launcher$ExtClassLoader)
实现的,它负责将 <JAVA_HOME >/lib/ext或者由系统变量-Djava.ext.dir指定位置
中的类库 加载到内存中。开发者可以直接使用标准扩展类加载器。
3. 系统类 (应用类)加载器(Application classLoader):
系统类加载器是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的,
它负责将 用户类路径(java -classpath或-Djava.class.path变量所指的目录,
即当前类所在路径及其引用的第三方类库的路径,如第四节中的问题6所述)下的类库 加载到内存中。
开发者可以直接使用系统类加载器。
5. 设计模式:(框架中使用,是程序设计的高级思想)
1. 单例模式:(重点)
1. 概念:设计一个类,这个类只能创建一个对象。(限制创建对象对象数量)
2. 怎么设计?
控制创建对象的数量 => 创建对象通过new 调用构造方法 => 控制构造方法就能控制创建对象
控制调用构造方法 => 用private修饰 => 需要给外部提供一个对象 =>
先在类中创建一个对象 (联想到封装) => 提供一个公共的 getInstance给外部返回一个对象
3. 步骤:
1. 私有化构造方法
2. 在类中创建一个对象,并且用private、static、final修饰
private为了避免在外部直接访问当前对象
static是为了在静态方法中可以返回当前类中的对象
final:可加可不加,加了可以保证不可修改,且提供获取效率
3. 提供一个public修饰的方法getInstance给外部返回一个对象
4. 单例模式获取方式:
1. 饿汉模式:预加载模式 (优化方式:静态内部类)
优点:在类加载的时候,就创建好对象放到静态区了,获取对象效率高。线程安全
缺点:类加载效率低,并且static修饰的成员占用资源。
2. 懒汉模式:懒加载模式 (优化方式:双重校验锁)
优点:节省资源,在需要的时候创建对象。
缺点:线程不安全。获取对象的时候,效率低
最简单的线程安全的方式:同步方法,效率低
更好的的线程安全的方式:双重校验锁
3. 枚举:
public enum Singleton{
INSTANCE;
5. 作用:
1. 全局配置文件
2. 全局日志文件
3. 节省资源,例如,回收站、资源管理器、游戏单开窗口
6. 使用选择:
一般用饿汉模式,如果有序列化(自己百度扩展)要求,用枚举。
7. 不需要频繁创建对象的时候;不允许多个对象 用单例
2. 装饰者模式:(了解)
装饰者模式指的是在不必改变原类(Input)文件和使用继承的情况下,动态地扩展一个对象的功能。
它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
实现步骤 :通过对原类文件继承,对原有方法功能的基础上,增强新的功能
使用场景:
不必改变原类,并且对原有功能进行加强的时候,就需要用装饰者模式
3. 适配器模式:(了解)
适配器类:
命名方式: XxxXxxAdaptor
解决了两个功能的协同工作。(苹果充电器和华为手机充电的问题)
扩展性强,成本低廉
使用场景:
不同的类需要配合完成功能
4. 简单工厂模式:(了解)Spring框架中有用到
简单工厂模式:(后面可以用反射优化代码)
1.将生产和消费分离(解耦),即创建对象和使用对象的功能分离,便于管理
1.扩展性弱,如果添加了新的产品,则需要修改工厂方法
使用场景:
适用于产品数量较少,且不经常改变的情况
6. ThreadLocal类 (掌握简单使用):
解决了线程安全问题,通过线程隔离有安全问题的数据实现的,底层是通过map保存线程id和值的。
* Returns the context ClassLoader for this Thread. The context
* ClassLoader is provided by the creator of the thread for use
* by code running in this t...
0.问题描述
通过下面的代码获得配置文件的路径,并传给FileReader,发现找不到系统路径
String path=Thread.currentThread().getContextClassLoader().getResource("jdbc.properties");
try( FileReader fileReader = new FileReader(path)) {
//.......省略
通过打印path,发现
/D:/I
中文版本:
在jdk.nashorn.api.scripting.NashornScriptEngineFactory:431静态方法getAppClassLoader()
* 0: invokestatic java/lang/Thread.currentThread:()Ljava/lang/Thread;
* 3: invokevirtual java/lang/Thread.getContextClassLoader:()Ljava/lang/ClassLoader;
* 6: astore_0
1 简述反射机制
反射是一种间接操作目标对象的机制,在程序程序运行时获取或者设置对象自身的信息。 只要给定类的名字,就可以通过反
射获取类的所有信息,接着便能调用它的任何一个方法和属性。
反射的步骤有哪些?
第一:获取类加载器:ClassLoader loader=Thread.currentThread().getContextClassLoader();//获取当
前线程的上下文类加载器
第二:通过类加载器获取类 Class clazz=loader.loadClass("com.taobao.reflect.car")//通过对象的全称限
定来获取对象。
第三:通过clazz获得构造函数:Constructors cons=clazz.getDeclaraedConstructors(Class[]null);//调
用默认的构造函数
第四:然后通过构造函数构造对象:Car car=(Car)cons.newInstance();//获取类的默认构造函数对象并实例化对
第五:得到car对象,然后调用car的方法:Method methd
=car.getMethod("setName","String.class");//method声明,并指向car的setName这个方法,得到setName方
2 用反射机制能干什么事
刚开始在使用jdbc时侯,在编写访问数据库时写到想吐,有八个表,每个表都有增删改查中操作,那时候还不知道有反射
机制这个概念,所以就对不同的表创建不同的dao类,这样不仅开发速率慢,而且代码冗余的厉害,最要命的是看着差不多
的,然后直接复制修改,由于容易犯各种低级的错误(大小写啊,多一个或少一个字母啊……),一个错误就可以让你找半天。
有了java反射机制,什么都好办了,只需要写一个dao类,四个方法,增删改查,传入不同的对象,就OK啦,无需为每一个
表都创建dao类,反射机制会自动帮我们完成剩下的事情,这就是它的好处。说白了,反射机制就是专门帮我们做那些重复的
有规则的事情,所以现在很多的自动生成代码的软件就是运用反射机制来完成的,只要你按照规则输入相关的参数,所以低
级的程序员慢慢的就被抹杀了,为什么?因为代码都不用写了,随便一个人都会开发,还要程序员干什么啊?所以我们只有
一条出路,那就是努力努力再努力,成为高级程序员,专门开发傻瓜软件,让其他程序员 到一边凉快去~
3 使用过哪些xml解析
XML是一种通用的数据交换格式,它的平台无关性、语言无关性、系统无关性、给数据集成与交互带来了极大的方便。
XML在不同的语言环境中解析方式都是一样的,只不过实现的语法不同而已。
XML的解析方式分为四种:1、DOM解析;2、SAX解析;3、JDOM解析;4、DOM4J解析。其中前两种属于基础方法,是
官方提供的平台无关的解析方式;后两种属于扩展方法,它们是在基础的方法上扩展出来的,只适用于java平台。
Java路径中的空格问题
1、 URLTest.class.getResource(/).getPath();
URLTest.class.getResource(/).getFile();
URLTest.class.getClassLoader().getResource().getPath();
Thread.currentThread().getContextClassLoader().getResource().getPath();等多种相似方式获得的路径,不能被FileReader()和FileWriter()直接应用,原因是URL对空格,特
⼤数据⾯试题 Big Data ⾯试题总结 JAVA相关 1-1)List 与set 的区别? ⽼掉⽛的问题了,还在这⾥⽼⽣常谈:List特点:元素有放⼊顺序,元素可重复 ,Set特点:元素⽆放⼊顺序,元素不可重复。 1-2)数据库的三⼤范式? 原⼦性、⼀致性、唯⼀性 1-3)java 的io类的图解 1-4)对象与引⽤对象的区别 对象就是好没有初始化的对象,引⽤对象即使对这个对象进⾏了初始化,这个初始化可以使⾃⼰的直接new的也可以是直接其他的赋值的, 那么背new或者背其他赋值的我们叫做是引⽤对象,最⼤的区别于 1-5)谈谈你对反射机制的理解及其⽤途? 反射有三种获取的⽅式,分别是:forName / getClass / 直接使⽤class⽅式 使⽤反射可以获取类的实例 1-6)列出⾄少五种设计模式 设计⽅式有⼯⼚法,懒加载,观察者模式,静态⼯⼚,迭代器模式,外观模式、、、、 1-7)RPC 原理? Rpc分为同步调⽤和⼀部调⽤,异步与同步的区别在于是否等待服务器端的返回值。Rpc的组件 有RpcServer,RpcClick,RpcProxy,RpcConnection,RpcChannel,RpcProtocol,RpcInvoker等组件, 1-8)ArrayList、Vector、LinkedList 的区别及其优缺点?HashMap、HashTable 的区别及优缺点? ArrayList 和 Vector 是采⽤数组⽅式存储数据的,是根据索引来访问元素的,都可以 根据需要⾃动扩展内部数据长度,以便增加和插⼊元素,都允许直接序号索引元素,但 是插⼊数据要涉及到数组元素移动等内存操作,所以索引数据快插⼊数据慢,他们最⼤ 的区别就是 synchronized 同步的使⽤。 LinkedList 使⽤双向链表实现存储,按序号索引数据需要进⾏向前或向后遍历,但 是插⼊数据时只需要记录本项的前后项即可,所以插⼊数度较快! 如果只是查找特定位置的元素或只在集合的末端增加、移除元素,那么使⽤ Vector 或 ArrayList 都可以。如果是对其它指定位置的插⼊、删除操作,最好选择 LinkedList HashMap、HashTable 的区别及其优缺点: HashTable 中的⽅法是同步的 HashMap 的⽅法在缺省情况下是⾮同步的 因此在多线程环境下需要做额外的同步机制。 HashTable 不允许有 null 值 key 和 value 都不允许,⽽ HashMap 允许有 null 值 key和 value 都允许 因此 HashMap 使 ⽤ containKey()来判断是否存在某个键。 HashTable 使⽤ Enumeration ,⽽ HashMap 使⽤ iterator。 Hashtable 是 Dictionary 的⼦类,HashMap 是 Map 接⼝的⼀个实现类。 1-9)使⽤ StringBuffer ⽽不是 String 当需要对字符串进⾏操作时,使⽤ StringBuffer ⽽不是 String,String 是 read-only 的,如果对它进⾏修改,会产⽣临时对象, ⽽ StringBuffer 是可修改的,不会产⽣临时对象。 1-10)集合的扩充 ArrayList list = new ArrayList(90000); list扩充多少次?? public ArrayList() { this(10); } 默认的扩充是10由此计算 1-11)java的拆包与封包的问题 System.out.println("5" + 2); 52 1-12)Java中Class.forName和ClassLoader.loadClass的区别 Class.forName("xx.xx")等同于Class.forName("xx.xx",true,CALLClass.class.getClassLoader()),第⼆个参数(bool)表⽰装载类的时候是否 初始化该类,即调⽤类的静态块的语句及初始化静态成员变量。 ClassLoader loader = Thread.currentThread.getContextClassLoader(); //也可以⽤(ClassLoader.getSystemClassLoader()) Class cls = loader.loadClass("xx.xx"); //这句话没有执⾏初始化 forName可以控制是否初始化类,⽽loadClass加载时是没有初始化的。 1-13)hashMap与hashTable的区别 HashMap Hashtable ⽗类 AbstractMap Dictiionary 是否同步 否 是 k,v可否nu
//此处可以使用hbase的配置文件,也可以通过代码来实例化hbase连接
* Configuration HBASE_CONFIG = new Configuration();
* HBASE_CONFIG.set("hbase.zookeeper.quorum", "10.229.171.45");
* HBASE_CONFIG.set("hbase.zookeeper.property.clientPort", "2181");
* HBASE_CONFIG.set("hbase.zookeeper.quorum","10.233.92.85,10.233.92.86,10.233.92.88");
* HBASE_CONFIG.set("hbase.zookeeper.property.clientPort", "2181");
* HBASE_CONFIG.set("zookeeper.znode.parent", "/hbase-unsecure"); conf = HBaseConfiguration.create(HBASE_CONFIG);
conf = HBaseConfiguration.create();
if("kerberos".equals(conf.get("hbase.security.authentication"))){
try {
//设置hadoop.security.authentication为kerberos
conf.set("hadoop.security.authentication", "kerberos");
//获取kerberos配置文件路径(krb为kerberos配置文件)
String krbStr=Thread.currentThread().getContextClassLoader().getResource("krb").getFile();
//获取用户票据hezhong路径(hezhong为给合众分配的用户配置文件)
String keyStr=Thread.currentThread().getContextClassLoader().getResource("jdzy").getFile();
//初始化配置文件
System.setProperty("java.security.krb5.conf", krbStr);
//使用用户hezhong登录
UserGroupInformation.setConfiguration(conf);
UserGroupInformation.loginUserFromKeytab("jdzy/f04345e6-70c1-448a-9bbb-4ac6b4c0109b@POLICE.COM", keyStr);
} catch (IOException e) {
e.printStackTrace();
tablePool = new HTablePool(conf, poolsize);
logger.debug("create hbase connection success");
// System.out.println("create hbase connection success");
今天碰到一个问题,我做了一个动态编译的程序,但在javaSE环境直接运行(即javaApplication状态)可以运行,但在tomcat服务器上通过请求来处理就不行,提示找不到我动态生成的类:关键语句如下
(报错时我写的是:return
ClassLoader.getSystem
ClassLoader())
最后我发现写成如图这样,调用
Thread.
currentThread().get
ContextClassLoader().loadClass(name);,就不会报错了。
查询资料后解释如下:
一文理解class.getClassLoader().getResourceAsStream(file)和class.getResourceAsStream(file)区别
为什么是classpath而不是src,因为当web项目运行时,IDE编译器会把src下的一些资源文件移至WEB-INF/classes,classPath目录其实就是这个classes目录。这个目录下放的一般是web项目运行时的class文件、资源文件(xml,properties...);
另外,在使用spring......