黑马程序员java知识
黑马程序员java知识(精选6篇)
黑马程序员java知识 第1篇
集合部分测试题
考试宣言: 同学们, 考试考多少分不是我们的目的!排在班级多少的名次也不是我们的初衷!我们考试的目的是要通过考试中的题目,检查大家在这段时间的学习中,是否已经把需要掌握的知识掌握住了,如果哪道题目你不会做,又或者做错了, 那么不用怕, 考完试后, 导师讲解的时候你要注意听!那时候学会了, 记住了, 也一样不影响你高薪就业!
本套题共40道不定项选择题,其中单选30道,多选10道。单选2分/题,多选4分/题。多选题不全对半分,全对满分。
1.所有异常的父类是()A a)Throwable类 b)Error类
c)Exception类
d)RuntimeException类
2.下列关键字能和try一起使用的有()CD a)final b)finalize c)finally d)catch
3.实现下列哪个接口,可以启用序列化功能()b a)Runnable接口
b)Serializable接口 c)Comparable接口 d)Comparator接口
4.当方法遇到异常(编译时异常)又不知如何处理时,下列哪种说法是正确的()C a)捕获异常(try{…}catch{…})b)抛出异常(throw 异常对象)c)声明异常(throws 异常类型)d)嵌套异常
5.如何自定义一个异常类()AB a)让这个类继承Exception b)让这个类继承RuntimeException c)让这个类继承Comparator d)让这个类继承Comparable
6.可以用来操作路径的类是()a a)File b)FileStream c)OutputStream d)FileReader
7.File类中,创建多级目录,可以通过以下那个方法()d a)create()b)createNewFile()c)mkdir()d)mkdirs()
8.下列哪一个import命令可以使我们在程序中创建输入/输出流对象()B a)import java.sql.*;b)import java.io.*;c)import java.net.*;d)import java.util.*;
9.下列关于Java中文件输入输出的说法正确的是()A a)在Java中,文件的输入输出功能是通过流来实现的
b)如果要把数据写入一个文件中,需要创建一个输入流对象 c)字符流在某些方面比如图片的处理,比字节流更方便x d)可以通过Reader r = new Reader(“c:java.txt”);创建一个输入流对象
10.获取指定目录下所有的文件,可以通过哪个方法实现:()D a)b)c)d)
list()getDir();listFile()listFiles()11.下面用字符流能成功拷贝的文件有()BC a)java基础自测题.doc b)学生考试答案.xml c)Student.java d)学生信息表.xlsx
12.看下面程序,加入xxx.txt里面有一个文字”a”,请问该程序输出结果是()D public static void demo1()throws FileNotFoundException, IOException {
InputStream is = new InputStream(“xxx.txt”);
int x = is.read();
System.out.println(x);} a)A b)a c)97 d)编译报错
13.如果我想保留a.txt中原有的内容,在后面追加写入数据,下面创建输出流对象写法正确的是()B a)FileOutputStream fos = new FileOutputStream(“a.txt”);
b)FileOutputStream fos = new FileOutputStream(“a.txt”,true);c)BufferedOutputStream bos = new BufferedOutputStream(“a.txt”,true);d)OutputStreamWriter osw = new OutputStreamWriter(new InputStream(“a.txt”,true),”gbk”);
14.下面关于字节流说法不正确的是()BC a)字节流拷贝文件时一次读取一个字节 b)字节流拷贝文件时一次读取两个字节
c)字节流使用完后需要关,内存输出流使用完后也必须要关闭 d)FileOutputStream没有缓冲区,而FileWriter有缓冲区
15.判断一个文件的大小我们可以使用哪个方法(AD)
a)File类的length()方法
b)File类的available()方法
c)FileInputStream类的length()方法 d)FileInputStream类的available()方法
16.已知 海贼王全集.avi 这个文件有20Gb 的大小,复制该文件最快的方式是()D a)
FileInputStream fis = new FileInputStream(“双元.jpg”);
FileOutputStream fos = new FileOutputStream(“copy.jpg”);
int b;
while((b = fis.read())!=-1){
fos.write(b);
}
fis.close();
fos.close();b)
c)
d)
FileInputStream fis = new FileInputStream(“致青春.mp3”);FileOutputStream fos = new FileOutputStream(“copy.mp3”);byte[] arr = new byte[fis.available()];
fis.read(arr);
fos.write(arr);
fis.close();fos.close();
FileInputStream fis = new FileInputStream(“xxx.txt”);FileOutputStream fos = new FileOutputStream(“yyy.txt”);
byte[] arr = new byte[1024];int len;while((len = fis.read(arr))!=-1){ fos.write(arr,0,len);}
fis.close();fos.close();
FileInputStream fis = new FileInputStream(“xxx.txt”);FileOutputStream fos = new FileOutputStream(“yyy.txt”);
byte[] arr = new byte[1024*8];int len;while((len = fis.read(arr))!=-1){
fos.write(arr,0,len);}
fis.close();fos.close();17.BufferedInputStream这个类是哪个设计模式的体现()B a)单例设计模式 b)装饰设计模式 c)工厂设计模式 d)适配器模式
18.下面说法正确的是()D a)OutputStream是个接口 b)Writer是个接口
c)BufferedOutputStream是FileOutputStream的父类x d)Reader是InputStreamReader的父类
19.下列关于Java I/O中字符输入流Reader类中的read方法描述错误的是?(A)
a)read()方法的返回值类型为char类型 b)read()方法的返回值类型为int类型
c)read()方法的返回值如果为-1,表示到流的末尾
d)read(char[] cbuf)方法表示将读到的多个字符存入字符数组cbuf中
20.下列哪些选项是Java I/O中字符输出流FileWriter类中的write方法?()ABCD a)write(char[] cbuf)写入字符数组
b)write(char[] cbuf, int off, int len)写入字符数组的某一部分 c)write(int c)写入单个字符 d)write(String str)写入字符串
21.假设存在xxx.txt文件,内容为”大家好”,以下代码运行的结果为?(A)public class Test { public static void main(String[] args)throws IOException {
FileReader fr = new FileReader(“xxx.txt”);
FileWriter fw = new FileWriter(“zzz.txt”);
int c;
while((c = fr.read())!=-1){
fw.write();
}
fr.close();
fw.close();} } a)b)c)d)
编译报错
程序正常运行,zzz.txt文件中的内容为”大家好” 程序正常运行,但是没有生成zzz.txt文件 程序正常运行,但是zzz.txt文件中是乱码
22.FileWriter类直接继承哪个类?(A)
a)OutputStreamWriter b)Writer c)BufferedWriter d)InputStreamReader
23.关于自定义字符数组拷贝文件的说法错误的是?()AC a)自定义的数组越大越好
b)自定义的数组既不能太大,也不能太小
c)可以使用FileReader类的available()方法的返回值作为数组的大小
d)建议定义为1024的整数倍,但不能太大,防止内存溢出
24.假设xxx.txt文件中存了2000个汉字,下列程序运行后输出的结果是?(B)
public class Test { public static void main(String[] args)throws IOException {
FileReader fr = new FileReader(“xxx.txt”);
FileWriter fw = new FileWriter(“yyy.txt”);
int count = 0;
char[] arr = new char[1024];
int len;
while((len = fr.read(arr))!=-1){
count++;
fw.write(arr,0,len);
}
fr.close();
fw.close();
System.out.println(count);} }
a)1 b)2 c)3 d)4
25.下面的哪个类代表字节输入流到字符输入流转换的桥梁?()A a)InputStreamReader b)OutputStreamWriter c)FileReader d)BufferedReader
26.BufferedReader读取一行时,判断该行是否结束的标志是什么?()AD a)
r b)
null c)
-1 d)
rn
27.缓冲流是否需要关闭()B a)否 b)是
c)以上答案都不对 d)关不关都可以
28.以下代码运行后输出的结果是?(A)
public class Test { public static void main(String[] args)throws IOException {
System.out.println(fun(5));} public static int fun(int num){
if(num == 1){
return 1;
}else {
return num * fun(num2)+ fun(num-1);
} } } a)34 b)13 c)21 d)5
30.为了提高读写性能,用下面的哪些流?()ABCD a)BufferedInputStream b)BufferedOutputStream c)BufferedReader d)BufferedWriter
31.下列关于Java I/O中哪个流可以将多个文件中的内容合并到一个文件中(序列流)?(cC a)SequenceOutputStream b)InputStreamReader c)SequenceInputStream d)OutputStreamWriter
32.下面的哪个流表示内存输出流?()A a)ByteArrayOutputStream b)ByteArrayInputStream c)ObjectInputStream d)ObjectOutputStream
33.下面说法正确的是?()b a)可以通过ByteArrayOutputStream和ByteArrayInputStream实现文件的复制)b)不可以通过ByteArrayOutputStream和ByteArrayInputStream实现文件的复制 c)用ByteArrayOutputStream和ByteArrayInputStream实现文件的复制,会出现乱码 d)用ByteArrayOutputStream和ByteArrayInputStream实现文件的复制,不会出现乱码
34.下面说法错误的是?()D a)ByteArrayOutputStream内部封装了字节数组
b)new ByteArrayOutputStream()利用空参构造创建对象时,内部的数组的大小为32 c)toByteArray()以字节数组的形式返回数据 d)以上说法都不对
35.关于对象操作流的描述正确的是?()A a)ObjectOutputStream流是将对象以Object类型写入到文件中 b)ObjectOutputStream流是将内容写入到Object对象中 c)ObjectInputStream流是读取Object对象里的内容
d)ObjectInputStream是从文件中读取一个对象出来,读出来是Object类型的,需要进行强制类型转换
36.ObjectOputStream类中的哪个方法可以将对象写入到文件中?()C a)write(Object obj)b)append(Object obj)c)writeObject(Object obj)d)objectWrite(Object obj)
37.下列选项中哪个不属于I/O流?()C a)FileWriter b)FileReader c)Properties d)PrintStream
38.下列说法不正确的是()D a)Properties 类表示了一个持久的属性集L b)Properties 可保存在流中或从流中加载L c)属性列表中每个键及其对应值都是一个字符串L d)以上说法都不对
39.以下代码运行后输出的结果是?()C public class Test { public static void main(String[] args)throws IOException {
Properties prop = new Properties();
prop.setProperty(“name”, “张三”);
prop.setProperty(“tel”, “***”);
Enumeration
while(en.hasMoreElements()){
String key = en.nextElement();
String value = prop.getProperty(key);
System.out.print(value + “:”);
} } } a)b)c)d)name:tel tel:name 张三:***: ***:张三
40.关于Properties类中的load和store方法说法正确的是?()AB a)load方法从流中读取属性列表,键值对,加载到Properties对象中 b)store方法将Properties对象中的键值对,写入到流中
c)load方法将Properties(Properties)对象中的内容加载到文件中
d)以上说法都不对
黑马程序员java知识 第2篇
在Java语言中,学好集合是非常重要的,下面简单的对集合进行总结,以便大家学习,有
问题再相互交流。
集合框架图
在集合框架图中可以看出,Collection接口中主要有两个子接口,分别是List和Set。List集合的特点是元素有序、包含重复元素,Set集合的特点是元素无序、不包含重复元素。Map集合中存储的是键值映射关系,元素都是成对出现的。Map接口的主要子接口有HashMap和TreeMap。
总结ist有顺序有重复没有排序,set无重复有排序,map的key也和set一样。
List接口
List : 特点是元素有序、可以包含重复元素。它有两个实现类分别是:ArrayList和LinkedList。
ArrayList : 内部维护一个数组结构,允许对元素进行快速随机访问,但是向List中间插入与移除元素的速度很慢。
LinkedList : 内部维护了一个双向链表结构,即通过节点之间彼此连接来实现的,每一个节点都包含前一个节点和后一个节点的引用。当一个新节点插入时,只需要修改其中保持先后关系的节点引用即可,这样的存储结构保证了LinkedList集合在增删元素时效率非常高。
Set接口
Set具有与Collection完全一样的接口,因此没有任何额外的功能,不像前面的List。实际上Set就是Collection只是行为不同,也就是说Set集合并没有对Collection接口进行扩充,只是比collection接口要求更加严了。
Set : 存入Set的每个元素都必须是唯一的,因为Set不保存重复元素。加入Set的元素必须定义equals()方法以确保对象的唯一性。
HashSet : 为快速查找设计的Set。存入HashSet的对象必须定义hashCode()。
TreeSet : 保存有序的Set, 底层为树结构。使用它可以从Set中提取有序的序列。
LinkedHashSet : 具有HashSet的查询速度,且内部使用链表维护元素的顺序。于是在使用迭代器遍历Set时,结果会按元素插入的次序显示。
Map接口
Map用于保存具有映射关系的数据,因此Map集合里存储两组值,一组用于保存Map里的key,另一组用于保存Map中的value,key和value都可以是任意引用类型数据,其中,作为key的值是不允许重复的,而value中可以出现重复。Map : 维护“键值对”的关联性,使你可以通过“键”查找“值”。
HashMap就是使用对象的hashCode()进行快速查询的。此方法能够显著提高性能。HashMap集合是基于哈希表的Map接口实现,并允许使用null键null值,但必须保证键的唯一性。
LinkedHashMap : 类似于HashMap,但是迭代遍历它时,取得“键值对”的顺序是其插入次序。而在迭代访问时发而更快,因为它使用链表维护内部次序。
TreeMap : 基于红黑树数据结构的实现。查看“键”或“键值对”时,它们会被排序(顺序由Comparabel或Comparator决定)。TreeMap的特点在于,你得到的结果是经过排序的。
Hashtable线程安全,但是存取速度很慢,且不允许存放null键null值,目前基本上被hashMap类所取代。Hashtable有一个重要的子类Properties。
Properties:java.util.Properties;key和value都是String类型,用来读配置文件。继承自Hashtable,比 Hashtable 更严格 属性列表中每个键及其对应值都是一个字符串。常用方法 String getProperty(String?key)和 setProperty(String key,String value);
用法:我在D盘下建了一个名为 AA.dat 的文件,文件的内容为:
name=ch
password=12345
Java程序内存泄漏研究 第3篇
在一些传统的编程语言(如C语言)中,回收内存的任务是由程序本身负责的。程序可以显式的分配/释放变量所占用的内存。在Java语言中,内存回收的任务由JVM(Java虚拟机)来担当,JVM提供了一个系统级的垃圾回收器(Gar bage Col l ect or,GC)线程,它负责自动回收那些无用对象所占用的内存,这种内存回收的过程被称为垃圾回收。Java程序员只需通过内存分配操作创建对象,而无须关心对象占用的空间是如何被收回的。因此很多程序员认为在Java中不必担心内存泄漏问题,然而实际并非如此,Java中仍然存在内存泄漏。
1 Java中的内存泄漏
1.1 GC的工作原理
内存泄漏,通常是指分配出去以后,却无法收回的内存空间。在Java中,内存的回收是通过GC自动实现的,对程序员来说是透明的,因此,要讨论Java的内存泄漏问题,必须从GC的工作原理谈起。
GC通过监控对象的运行状态(包括申请、引用、被引用、赋值等)及时准确的释放对象,而释放对象的根本原则就是在可达测试(Reachabi l i t y Test)[1]中从根对象不可达。GC从根对象(堆栈对象、静态对象、JNI句柄指向的对象等)开始,利用有向图按照引用关系遍历整个堆空间,所有被遍历到的对象即为“活”的对象,“活”的对象GC不能进行回收。GC把那些失去引用,从根不可达的对象删除,释放内存。
1.2 J ava内存泄漏的定义
J ava中的内存泄漏是存在一些被分配的对象,这些对象存在以下两个特点:首先,这些对象是可达的,即在有向图中,存在通路可以使其与根相连;其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象就可以被判定为Java中的内存泄漏,这些对象不会被GC回收,而它们却仍然占着内存。如图一所示。
现在越来越多的服务器采用Java技术,而服务器程序往往长期运行,因此Java的内存泄漏问题就不容忽视,否则即使是只有少量的泄漏,长期运行之后,也会导致耗尽全部的可用内存而导致系统崩溃的危险。
2 几种典型的内存泄漏及解决方法
在程序中要查明代码引起泄漏的原因是很困难的,但大多数Java程序中的内存泄漏情况却十分相似,下面介绍几种典型的内存泄漏及其解决方法。
2.1 未彻底清除失效对象。
常见的是废弃聚集的引用(Obsol et e Col l ect i on Refer ences):对象加入到一个聚集中,当其不再需要时并没有被移去,当该聚集用St at i c声明且在程序的整个生命周期中存在时问题尤为突出[2]。还有一种典型的错误是对临时对象的引用,临时对象使用后就会成为垃圾,但此时若还保持对其引用,就会使得GC无法回收这些失效的对象。
解决方式:及时清除失效对象的引用、尽量少用静态集合类、减少临时对象的使用、对象不用时最好显式的设置为Nul l。
2.2 类装载器
J ava类装载器的使用为内存泄漏提供了许多可乘之机。由于类装载器不仅仅只与对象引用有关,同时也和对象内部的引用有关,比如数据变量,方法和各种类。这意味着只要存在对数据变量、方法、各种类和对象的类装载器,那么类装载器将驻留在JVM中。因为类装载器在和很多类关联的同时又可以和静态数据变量关联,那么相当多的内存就可能发生泄漏[3]。
2.3 全局集合
大型应用程序中存在各种各样的全局数据仓库,Java程序中,不能在所有类之外定义全局变量,只能通过在一个类中定义公用、静态的变量来实现全局变量。由于静态的变量会永驻内存,难免造成内存泄漏。
常用的解决方式是周期运行的清除作业。这个作业会验证仓库中的数据然后清除一切不需要的数据。另一个解决办法是引用记数(Ref er ence Count i ng)[3]。集合负责统计集合中每个元素的引用者数量。引用者会通知集合什么时候已经对元素不再引用。当引用者的数目为零时,该元素就可以从集合中移除了。
2.4 高速缓存
高速缓存是一种用来快速查找已经执行过的操作结果的数据结构。缓存通常都是以动态方式实现的,如果缓存设置不正确而大量使用缓存的话则会出现内存溢出(Out Of Memor y Er r or)。
为避免这种错误,程序可以确定高速缓存在它所使用的内存中有一个上界,当高速缓存所占空间超过该上界时,移除缓存中旧的数据,释放缓存空间。另一个解决途径是使用J ava.l ang.r ef.Sof t Ref er ence类坚持将对象放入高速缓存。这个方法可以保证当JVM用完内存或者需要更多堆的时候,可以释放这些对象的引用。
3 Java内存泄漏的研究现状
对无用对象保持引用是造成内存泄漏的主要原因,因此针对Java内存泄漏的研究主要是通过分析对象是否仍有用以及对象之间的引用关系来进行的。
3.1 针对对象是否仍然有用
J VM利用有向图进行可达测试发现“活”的对象,“活”的对象GC将不进行回收,但是“活”的对象不一定就是有用的对象,因此无用对象的确定对发现内存泄漏有着至关重要的作用。文献[4]针对Java中的临时对象的创建而造成的内存泄漏提出了这样一种场景(Scenar i o):创建临时对象的时候,给出其期望的生命周期,当该对象的生命周期超出该期望期时,该对象就有可能是内存泄漏对象[2,3,4]。文献[5]针对J ava中的数组,提出了一个结合向前数据流分析和向后控制流分析算法标志变量的关系,并确定数组中元素的有用区域,算法给出个程序点相关的变量间的约束关系以及在各点依然有用的数组元素,GC利用数组间的约束关系回收不再有用的数组变量[2,3,4,5]。
3.2 针对对象间的引用关系
通过分析对象或者类之间的引用关系找出可疑的实例或者区域。专业检测内存泄漏工具Jpr obe Memor y Debugger正是基于对实例的引用树(Ref er ence Tr ee)和被引用树(Ref er r er Tr ee)的分析来判定该实例是否为可疑的泄漏对象。JDK TM1.2中定义了对象引用应用程序设计接口,该应用程序设计接口允许应用程序以对象引用的方式与JVM的内存管理器进行交互。应用对象引用队列可以创建这样一个环境:当通过对象引用获得某一对象时,应用程序得到通知。然后,应用程序就可以对相关对象进行跟踪和清除操作,这样就减少了内存泄漏。
3.3 针对对象行为的分析
文献[2]提出通过分析对象行为来确定该对象是否为内存泄漏对象的设想,同一个类的多个对象,使用逻辑的不同导致其行为不同,从而导致了某些对象被回收,而某些对象未被回收。用对象生存期行为模型来描述对象从创建开始到被销毁为止的整个时间段内的行为,通过分析泄漏对象和未泄漏对象的行为差异,获取导致泄漏的对象行为。然后在该基础上通过深入研究对象行为来确定更多的内存泄漏[2]。
3.4 针对根对象的研究
GC利用有向图从根对象开始遍历所用的引用,某对象由根可达,则保留之,不可达则释放之。由此,根对象的选择对GC显得尤为重要。如果根对象选择的不适宜,则可能使很多无用对象仍可以从根对象可达,无法被回收。文献[6]通过进行类型分析(Type Anal ysi s)以获得比较适合的根对象,同时分析变量是否有用使仍可用的对象成为根对象。
4 内存泄漏的检测、处理及改进
4.1 检测内存泄漏的存在
一般说来,一个正常的系统在其运行稳定后其内存的占用量是基本稳定的,不应该是无限制的增长。同样,对任何一个类的对象的使用个数也有一个相对稳定的上限,不应该是持续增长的。根据这样的基本假设,我们持续地观察系统运行时使用内存的大小和各实例的个数,如果内存的大小持续地增长,则说明系统存在内存泄漏,如果特定类的实例对象个数随时间而增长,则说明这个类的实例可能存在泄漏情况
[7]。
另一方面发生内存泄漏的一个迹象是:在应用程序中出现了Out Of Memor y Er r or。也有可能应用程序确实正在使用这么多的内存,但是在许多情况下Out Of Memor y Er r or都是内存泄漏的信号。一种查明方法是不间断地监控GC的活动,确定内存使用量是否随着时间增加。如果确实如此,就可能发生了内存泄漏。
4.2 处理内存泄漏的方法
一旦知道确实发生了内存泄漏,就需要更专业的工具来进行处理。这些专业工具从JVM获得内存系统信息的方法基本上有两种:JVMTI(Java Vi r t ual Machi ne Tool s I nt erf ace)和字节码技术。J VMTI及其前身J VMPI(J ava Vi r t ual Machi ne Pr of i l i ng I nt er f ace)都是标准接口,作为外部工具同JVM进行通信,搜集JVM的信息。字节码仪器则是引用通过探针获得工具所需的字节信息的预处理技术。
目前的大多数工具,要么是基于JVMTI创建Java堆转储,并离线分析它;要么是基于JVMTI获取Java程序时刻信息(堆信息及对象的分配、引用关系等)。而JVMTI会加大内存开销使程序的执行变得非常缓慢。下面简单介绍一下内存泄漏检测工具JRocki t Memor y Leak Det ect or。
J Rocki t Memor y Leak Det ect or是一种实时检测基于BEA J VM运行的J ava程序内存泄漏的工具,它内嵌于BEA J Rocki t J VM,支持在系统全速运行时使用,而不会引起通常与此类工具相关联的性能开销,因此不存在上述一般工具的缺点。该工具通过三步处理泄漏。
(1)通过趋势分析发现那些对象类存在泄漏。J Rocki t Memor y Leak Det ect or为用户提供了一个趋势分析,趋势分析可以说明应用程序中每个类使用堆空间的情况。它将告诉你某一类型的实例使用了多少空间、它们占用了堆的哪一部分、存在多少个实例以及每秒钟堆空间使用的增加速度,增长最快的对象就是值得怀疑的对象。
(2)找出同泄漏对象相关的其他类。可以在趋势分析表中选择一个怀疑类型,所有具有指向选中类型的实例的类型都可以显示在一个图中,用户可以回溯到导致引用的最终原因。类的实例可以被显示和内观,指向一个选中实例的所有实例都可以在一张实例图中显示出来,可以打开分配跟踪,跟踪某个类的所有分配情况。
(3)向下发掘,观察独立的对象之间是如何相互联系的。
同时,该工具也可以动态的找出所有内存分配的堆栈路径。利用这三个特性,将该工具紧紧地集成在JVM中,那么就可以安全有效的捕捉和修复内存泄漏了。
4.3 改进内存泄漏的方式
在实际应用中若能根据GC的运行机制有针对性的进行设计和编码,可以减少甚至避免内存泄漏的发生,具体措施包括以下几个方面。
(1)少用静态对象变量、静态集合类。像Has h Map、Vect or等静态集合类的生命与应用程序一样长,被其引用的对象GC无法回收,容易造成内存泄漏。
(2)慎用内部类。内部类中隐藏着外部类的对象实例的引用,一旦没有释放,可能导致一系列对象得不到释放。
(3)简化对象引用关系、及时清除静态变量的引用、对象不用时显式的设置为nul l。
(4)勿用长生命周期对象管理短生命周期对象。
(5)使用J ava.l ang.r ef包。该包中定义了三个引用类:Sof t Ref er ence、Weak Ref er enc、Phant om Ref er ence。可以解决未知或者多余对象引用造成的内存泄漏。
(6)及时关闭物理连接。比如数据库连接和网络连接,除非显式的关闭,否则不会被GC自动回收。
(7)使用Fi nal i ze方法清除本地系统资源。本地系统资源是通过Java以外的函数获得的资源,GC无法回收。可以在Fi nal i ze方法中加入清除内存代码,解决本地系统资源得不到释放造成的内存泄漏。
(8)调用对象的特定方法清除引用或释放资源。有些对象不再被使用时会提供用来清除引用或释放资源的方法,如对话框的Di spose方法、容器组件的Romove方法、Swi ng组件UI的Uni nst al l方法等。
5 结束语
内存泄漏是一个特别重要又难以解决的问题,不再有用的对象被其他依然有用的对象的引用是导致内存泄漏的主要原因。良好的编程风格和专业的检测工具可以有效地减少内存泄露,但如何更好地避免甚至消除内存泄漏、如何解决因内存泄漏而导致的Java程序系统性能下降问题将是内存泄漏相关领域的重要研究方向。
摘要:Java有比较安全的内存管理机制,垃圾回收器(GC)会自动地对无用的内存空间进行回收,但是GC并不能回收所有的垃圾空间,仍然存在着内存泄漏。本文从GC的工作原理入手,详细分析了产生内存泄漏的原因,讨论了几种典型内存泄漏及解决的方法,介绍了内存泄漏的相关研究及发展趋势,指出了检测和处理内存泄漏的方式,给出了避免内存泄漏的建议。
关键词:内存泄漏,垃圾回收,引用
参考文献
[1]QUEST公司.JProbe Memory Debugger Developer's Guide[R].2005.
[2]贾晓霞,吴际,金茂忠等.Java程序内存泄漏综述[J].计算机应用研究,2006,(09):1-3.
[3]Staffan Larsen.Memory Leaks,Be Gone[EB/OL].http://dev2dev.bea.com/pub/a/2005/06/memory_leaks.html,2005-06.
[4]Wim De Pauw,Gary Sevitsky.Visualizing Reference Patterns for Solving Memory Leaks in Java[C].Lisbon:Pro-ceedings of the13th Europ ean Conference on OO Program-ming,1999:116-134.
[5]RShaham,E K Kolodner,M Sagiv.Automatic Re-moval of Array Memory Leaks in Java[C].Berlin:Compiler Construction,the9th International Conference,volume1781of Lecture Notes in Computer Science,2000:50-66.
[6]Agesen O,Detlefs D,MOSS J E B.Garbage Collection and Local Variable Typeprecisionand Liveness in Java Virtual Machines[C].New York:Programming Language Design and Implementation(PLDI),1998:269-279.
Java程序混淆技术综述 第4篇
关键词:Java程序;混淆技术;软件水印;防篡改;软件版权保护
中图分类号:TP311.52 文献标识码:A 文章编号:1007-9599 (2012) 09-0000-02
人们对于计算机的依赖性随着软件技术的不断给人们带来便利和乐趣,使工作更富有主动性和操作性,但是任何事物在发展过程中都会有其负面效应与之相对,随着计算机软件应用量和种类的不断扩展,其使用安全问题便开始影响到人们的正确使用,并开始逐渐打击使用计算机的积极性,以及对于安全问题的无奈。主要攻击形式是通过对软件备份的破坏而直接将软件彻底摧毁,根据现有问题,当前的软件已经经过改良,重新调试,但是攻击则换角度也将攻击破解方式进行重组,使改良的部分无法发挥其作用,这样软件就不会得到应有的保护,依旧能够以非正规途径得到软件的任何数据,从而进行大面积的传播,这也就是为何当今软件盗版现象严重的途径之一。Java程序的出现,首先能够在网络上加快传播速度并且无客观條件可以轻松普及,但同时却能够通过无关性对产权进行有效的保护,不会轻易的被人盗用。
一、软件混淆技术基本原理
(一)基本释义
软件混淆技术是为了能够在软件受到侵害或是盗用时可以被保护的一种现代技术,软件技术首先咋1977年新西兰首次被提出,其实是将原有的程序的语言转换成一种密码语言,不能被一般的软件所识破的一种转换,并且研制出能够对这种转换进行评估的理论计算。转换的形式未变,只是在形式上保留原有的运行根组成,添加一些其他能够扰乱破坏和读取的程序代码。如今混淆技术开始不断的发展,诸多的被应用于产权的保护中,所谓的混淆就是通过语词的转变但保持其语义依然与之前相同,这就能保证软件在使用时功能是不变的,却能够防止逆向工程攻击和被静态分析。
(二)软件混淆技术的分类
当Java文件经过处理,在原有的基础上添加了其他程序代码成为类文件,这种类文件包括魔数( magic number) 、版本( version) 、常量池( constant pool ) 、访问标识( access flag s) 、(this)类、(super)类、接口( interfaces) 、域( fields) 、方法(methods) 和属性(attributes) 。
软件混淆主要通过类内混淆和类间混淆来根据不同的使用对象进行划分,类内混淆主要是克服(methods)中的code字段,而类间混淆却是针对多种类文件的综合体进行混淆处理,其主要的方式是合并和拆分相对的过程处理。
下面分别介绍两种混淆。
(1)、类内混淆
能够支持类内混淆的途径有数据混淆、控制混淆、切片混淆以及针对特定工具混淆四种。数据混淆是对数据的结构组成元素原有的书写方式进行修改,使其不再成为能够被认知的程序。主要有:变量存储和编码混淆、变量聚合混淆、顺序调整混淆、词法混淆以及移除注释和调试信息混淆。控制混淆是在信息传输过程中的保护,主要采取追踪定位等方式实时监测信息的状态,从而对其他袭击系统进行抵御。切片混淆,正常在一般的软件中问了能够让计算机更好的识读软件,所以增加了切片以帮助更好的理解,降低难度,而混淆了切片则能够使其首先无法有助于难度的下降,另外会造成对于程序的误导可能降低了或是更改了某些其他的程序,达到了增加难度混淆的目的。针对特定工具混淆软件为了能够起到保护作用,本身具有一定的反编译和反混淆的功能,会在一定的紧急时刻自动启动,而针对特定工具就是抑制了自动的传导,对于能够使软件本身内部之间的反编译都可以正常的进行,但是对于外部的反编译就不会在自动启动,这就是混淆的高级之处。相区别与前三者人的行为,针对特定工具混淆具有自动的功能针对的是自动化。如果能够与以上三种任意搭配就能够达到防住人机的共同目的。
(2)、类间混淆
类间混淆是合并拆分类文件而造成混淆的目的其中又有三种表现形式:类合并、类拆分、隐形类间混淆。类合并是要面对多个文件的自身组成的函数变量,通过将函数、变量的名称进行更改,注意在更改过程中一定不能同时出现两个或以上的名子,如果在合并中有参数同系统提示无法进行更改,则要为其中一个虚拟一个参数。另外如果合并文件中有继承关系的则需要在合并成功后加入一个布尔型的私有变量用于区分标识符相同的函数。类拆分则是观察现有的文件是否进行拆分,同时并作出相应的调整。对于结果是可拆分的类就是将A拆分为A1与A2,为了能够使A的要素都能够实现所以应该是A2处于继承A1关系。类型隐藏是通过接口来实现混淆,并且能够保证原文件并不需要改变接口,所以被称为隐形。相比类合并,类拆分和隐形类间混淆所用的时间相对较短。
二、软件混淆算法的攻击模式
(一)混淆算法的理论研究
混淆技术的提出者曾经试图将整个运行过程当做是一个虚拟黑箱来看,也就是说对于其他程序来说这就完全是另外一个软件和原软件无任何联系,从它的输入和输出的方式内容和代码都不同,这种在理论中是可以站的住的,但是在实际操作中会受到很多因素的限制,但在相对宽松的要求下还是可以实现混淆技术的使用。后来又根据该理论研究通过点函数来实现虚拟黑箱,总而言之其未来的研究空间还有很大,期待它的日益成熟。
(二)混淆算法的攻击模式
1.针对数据混淆算法的攻击
假定T 是对程序P 的一个单向的混淆变换,当且仅当从原始程序P 除去某些信息后,无法通过混淆后的程序Pc恢复出P。词法变换是最典型的不可逆混淆算法。虽然对于经过词法变换的程序进行攻击不可能恢复程序的原貌,但只要理解程序各个模块的含义就可能对程序产生威胁。例如根据无法被混淆的系统API 名称等关键字,攻击者可以推测出该模块的大致功能。
2.针对控制流混淆算法的攻击
目前,针对控制流混淆算法的主要攻击方法是动态分析。动态分析对大部分混淆算法的攻击作用,并阐述攻击模型. 对于控制流混淆而言, 变换后生成的程序中若存在始终不执行的分支,通过动态分析就能找到对破解程序有用的信息.针对控制流混淆,还有黑盒测试攻击, 该方法通过对程序进行黑盒测试,了解各个类及其函数的功能, 从而获取攻击者需要的信息. 这种方法对大多数的混淆变换均能加以攻击。同时,该方法也存在着一定的局限性: 黑盒测试缺乏自动分析工具,需要依靠大量的人力来完成分析工作。
三、对于混淆技术的未来预估
混淆技术是一种可用于抵制逆向工程和重组工程、对软件知识产权进行保护的程序变换技术。使用混淆技术虽然会使代码的效率有一定程度的降低,但是它的实现代价相对较小, 因而在近年引起了广泛的关注.由于混淆技术不改变程序功能的特性, 并且有些词法混淆算法甚至可以减小原程序的体积, 因此混淆技术得以在保护移动代码方面得到广泛的应用. 受移动平台资源的限制,今后混淆技术的研究方向一方面要加大混淆的力度,增加攻击者反编译的难度,另一方面也要考虑降低混淆算法对目标程序的运行负担。文献探讨了混淆技术在移动代理上的应用。目前保护软件知识产权的新技术主要有3种:混淆技术、软件水印技术和防篡改技术。这3种技术各有特长,将它们互相结合使用,可以给予目标软件更为可靠的保护,这也是目前研究的一个重要方向。对于混淆算法的评估、正确性验证以及如何研制高效可靠的混淆算法, 也是将来的发展方向之一。
四、总结
混淆技术是对软件进行的基础产权保护,为了能够增加破译的难度和代价,从而使产权盗版的情况发生率降低,同时还能提高软件水银的鲁棒性,有人提出将程序的编译混淆, 而不是混淆程序本身。但是该方法还有若干问题需要考虑。使软件的安全度进一步提升,给人们的使用带来了更多的福音,虽然该技术已经广泛运用也颇收到好评,在技术上依旧有大的发展前景,需要投入更多的研究力量。
参考文献:
[1]霍建雷,范训礼,房鼎益.Java标识符重命名混淆算法及其实现[J].计算机工程,2010,第1期
[2]咸星海.面向对象代码混淆技术研究及软件实现[J].中国科技博览,2010,第19期
[3]刁俊峰.软件安全中的若干关键技术研究[D],北京:北京邮电大学,2007
[4]李新良,罗戈夕.基于Java的代码混淆算法研究[J].湖南文理学院学报:自然科学版,2010,第3期
[5]霍建雷,范训礼.房鼎益,Java标识符重命名混淆算法及其实现[J].计算机工程,2010,第1期
[6]李婧.基于控制流混淆轉换的代码保护技术[J].计算机安全,2009,第8期
[7]袁征,冯雁,温巧燕,张华.构造一种新的混淆Java程序的不透明谓词[J].北京邮电大学学报,2007,第6期
黑马程序员java知识 第5篇
之前详细描述了4种debug方式,现在简绍第5种,不足之处,敬请指正:(源码在文章末尾处)五.maven项目debug
第一步:首先创建一个maven项目,如图所示:
第二步:debug运行前设置
黑马程序员济南中心 编著
执行完上面的步骤,会出现如下弹出框
黑马程序员济南中心 编著
Source配置如下
黑马程序员济南中心 编著
黑马程序员济南中心 编著
黑马程序员济南中心 编著
第三步:Debug运行,上面配置完成后,直接debug运行即可,如下图
第四步:打断点测试结果
黑马程序员济南中心 编著
希望能对不会使用maven进行debug的猿友们有所帮助,第六种方式我会尽快补上
maven-debug.zip
黑马程序员java知识 第6篇
编程语言Java,已经21岁了。从1995年诞生以来,就一直活跃于企业中,名企应用天猫,百度,知乎......都是Java语言编写,就连现在使用广泛的XMind也是Java编写的。Java应用的广泛已经到了“无处不用”的盛世,而且一直在语言排行榜榜首,从未被超越。废话不多说,今天主要是给大家讲讲怎样学习Java,给大家建议一条轻松精通Java的学习路线。有了相对标准的流程,那么你的学习效率一定会倍增。
下面以黑马程序员Java学院的学习路线为例,进行一次史无前例的剖析,细致的讲解零基础的人怎么学习Java。先看下Java在基础阶段的知识点路线图。
内容多吗?不要被吓到了,知识点剖析的比较细,所以看着比较多。不要害怕学不会,经过下面的详解,你就会明白这些知识点都是干嘛的了。
Java基础
入门阶段,主要是培养Java语言的编程思想。了解Java语言的语法,书写规范等,掌握Eclipse、MyEclipse等开发工具,编写Java代码的能力。学完这个阶段你应该可进行小型应用程序开发并且可以对数据库进行基本的增删改查管理。注意:此阶段知识点的学习,会有真实的项目进行驱动学习,让你轻松理解各知识点。
1.计算机基础知识
针对零基础学习的人,从对计算机操作等知识的了解,延伸到Java语言的发展与开发工具的使用上。主要是让你知道怎样执行计算机命令,认识Java这门语言,感受编程语言Java怎么开发程序。
1)计算机基础
让零基础学习的人先了解计算机相关知识,进而再去了解Java语言。
2)DOS常用命令
了解什么是DOS,并掌握DOS控制台的打开方式,同时熟悉常用的DOS命令,例如:盘符切换、进入指定目录、删除文件等,完成使用DOS命令对计算机进行操作和控制。
3)Java概述
了解Java语言的发展史、Java语言平台版本、Java语言的特点,以及JRE与JDK。JRE是Java的运行环境,JDK是Java开发工具包,它包含了Java的开发工具以及JRE。所以安装了JDK就不用再单独安装JRE了。
4)JDK环境安装配置
了解了什么是JDK,以及JDK的重要性,下一步我们就来学习如何安装和配置JDK环境。在安装JDK之前,我们首先需要下载JDK,针对不同的系统,我们需要下载不用版本的JDK。
5)环境变量配置
了解path、classpath环境变量,理解path变量和classpath变量的作用,并掌握path变量和classpath变量的配置方式。6)Java程序入门
当JDK、环境变量配置完毕,我们就可以开始 编写Java程序。编写Java程序可以使用如下几种工具:notepad(微软操作系统自带)、Editplus、Notepad++、Eclipse、MyEclipse。
2.编程基础
此模块学习是让你了解编程的具体流程,学习Java基础语法的格式等。具体要掌握不同数据类型的变量定义与使用,掌握不同运算符的运算规则,掌握流程控制语句的执行流程,编写方法的声明与调用,创建数组并访问数组元素等知识。
1)注释
在程序开发工程中,用于解释和说明程序的文字我们称之为注释,Java中的注释分为以下几种:单行注释、多行注释、文档注释。
2)关键字
了解Java关键字的含义及特点,掌握关键字使用的注意事项。
3)标识符
了解什么是标识符,标识符的组成规则,以及标识符使用时的注意事项。
4)常量与变量
理解常量与变量的含义,并掌握常量与变量的区别、变量的定义格式以及变量的赋值。5)数据类型
掌握Java语言的数据类型,如基本数据类型:byte、short、int、long、float、double、char、boolean,以及引用类型:类、接口、数组。
6)运算符
熟练掌握Java中的运算符:算术运算符、赋值运算符、比较运算符、逻辑运算符、位运算符、三目运算符。
7)流程控制语句
了解什么是流程控制语句,掌握以下流程控制语句:顺序结构、选择结构、循环结构,并能够通过流程控制语句实现特定的功能。
8)方法
掌握方法的定义及格式,并能正确的调用方法,理解方法的调用过程,同时清楚方法的注意事项;掌握方法重载及其特点。
9)数组
了解数组的概念,掌握数组的定义格式、静态初始化、动态初始化,并能够理解Java中数组的内存图解。熟练掌握数组的遍历、获取最值、数组元素逆序、数组元素查找、数组排序和二分查找,以及二维数组的定义格式及初始化。
3.面向对象 现实世界中,随处可见的一种事物就是对象,对象是事物存在的实体,如人类、书桌、计算机、高楼大厦等。人类解决问题的方式总是将复杂的事物简单化,于是就会思考这些对象都是由哪些部分组成的。通常都会将对象划分为两个部分,即动态部分与静态部分。静态部分,顾名思义就是不能动的部分,这个部分被称为“属性”,任何对象都会具备其自身属性,如一个人,它包括高矮、胖瘦、性别、年龄等属性。然而具有这些属性的人会执行哪些动作也是一个值得探讨的部分,这个人可以哭泣、微笑、说话、行走,这些是这个人具备的行为(动态部分),人类通过探讨对象的属性和观察对象的行为了解对象。
1)面向对象思想
了解面向过程编程思想,能够通过案例理解Java的面向对象编程思想,了解面向对象开发、设计、特征。
2)类与对象
了解什么是类,什么是对象,并理解类与对象之间的关系;熟练掌握类的定义、对象内存图等。
3)成员变量和局部变量
了解什么是成员变量,什么是局部变量,以及从他们在类中的位置、内存中的位置、生命周期、初始化值等方面掌握他们的区别。
4)匿名对象
了解什么是匿名对象,掌握匿名对象的两种使用情况。5)封装
清楚的了解什么是封装,并能够理解封装的优点与缺点,同时掌握封装的原则。
6)this关键字
掌握this关键字的含义与使用。
7)构造方法
了解什么是构造方法,构造方法的作用,以及与构造方法相关的注意事项。
8)继承
理解什么是继承,继承的好处以及java中继承的特点和注意事项,继承中成员变量的关系、构造方法的关系、成员方法的关系,方法重写与方法重载的区别。
9)多态
理解什么是多态,掌握多态案例及成员访问的特点,多态的优点和缺点,多态中的转型问题。
10)抽象类
了解什么是抽象类,抽象类的特点,抽象类成员的特点。
11)接口
了解什么是接口,接口的特点,接口成员的特点,类与类、类与接口的关系,以及抽象类与接口的区别。12)内部类
什么是内部类,内部类的访问特点,内部类的位置,什么是成员内部类、局部内部类、匿名内部类,以及匿名内部类在开发中的使用。
4.常用类
类库就是Java API(Application Programming Interface,应用程序接口),是系统提供的已实现的标准类的集合。在程序设计中,合理和充分利用类库提供的类和接口,不仅可以完成字符串处理、绘图、网络应用、数学计算等多方面的工作,而且可以大大提高编程效率,使程序简练、易懂。
学习内容:掌握Object类、Scanner类、String类、StringBuffer类、StringBuilder类、Arrays类、基本包装类、正则表达式、Math类、Random类、System类、Date类、DateFormate类、Calendar类,及其常用方法。
5.集合
集合类存放的都是对象的引用,而非对象本身,出于表达上的便利,我们称集合中的对象就是指集合中对象的引用。简单一点就是说,集合是存放数据的容器。
学习内容:什么是集合?数组与集合有什么区别,集合类的特点,掌握Collection接口、Iterator接口、List接口、ListIterator接口、ArrayList类、Vector类、LinkedList类、泛型、Set接口、HashSet类、Map接口、HashMap类、LinkedHashMap类等。
6.IO IO(Input/Output)是计算机输出/输出的接口。Java的核心库java.io提供了全面的IO接口,包括:文件读写,标准设备输出等等。Java中IO是以流为基础进行输入输出的,所有数据被串行化写入输出流,或者从输入流读入。
1)异常
了解什么是异常,异常的由来,常见的异常,异常的分类,掌握jvm对异常的默认处理方案,异常的处理方案:try…catch…finally、throws,什么是编译时异常,什么是运行时异常,掌握它们两的区别,throws关键字、throw关键字,以及这两个关键字的区别,熟练掌握自定义异常,异常注意事项。
2)File类
了解什么是File类,File类的用途,掌握File类的方法:createNewFile()、mkdir()、mkdirs()、delete()、renameTo(File dest)、isDirectory()、isFile()、exists()、等方法,以及File类的基本获取功能方法和高级获取功能方法。
3)IO流
了解什么是IO流,IO流的用途;熟练掌握输入流、输出流、字符流、字节流、IO流的常用基类;如何使用字节流读写数据、复制数据;什么是字节缓冲流,如何使用字节缓冲流读写数据;什么是转换流,如何使用转换流更加高效的读写数据,内存操作流、打印流、标准输入输出流、序列化流、Properties集合。
4)IO流练习
(1)复制文本文件;(2)复制图片;(3)把ArrayList集合中的字符串数据存储到文本文件;
(4)从文本文件中读取数据(每一行为一个字符串数据)到集合中,并遍历集合;(5)复制单极文件夹;
(6)复制单极文件夹中指定文件并修改文件名称;(7)复制多极文件夹;
(8)已知s.txt文件中有这样的一个字符串:“hcexfgijkamdnoqrzstuvwybpl”;(9)请编写程序读取数据内容,把数据排序后写入ss.txt中;(10)获取每次读取数据的行号;(11)登录注册IO版。
7.多线程
是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理(Chip-level multithreading)或同时多线程(Simultaneous multithreading)处理器。在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理(Multithreading)”。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程(台湾译作“执行绪”),进而提升整体处理性能。
1)多线程
了解什么是线程,什么是多线程,理解java程序的运行原理,掌握多线程的实现方案,如何获取和设置线程名称、线程的生命周期、如何解决线程安全问题、线程同步、Lock锁、死锁问题、线程间通信、线程的状态及状态转换、线程池。
2)多线程面试题
(1)多线程有几种实现方案,分别是哪几种?(2)同步有几种方式,分别是什么?(3)启动一个线程是run()还是start()?它们的区别?(4)sleep()和wait()方法的区别;
(5)为什么wait(),notify(),notifyAll()等方法都定义在Object类中;(6)线程的生命周期图。
3)设计模式
了解什么是设计模式,设计模式的分类,熟练掌握单例设计模式(懒汉式、饿汉式)。
8.网络编程
网络编程最主要的工作就是在发送端把信息通过规定好的协议进行组装包,在接收端按照规定好的协议把包进行解析,从而提取出对应的信息,达到通信的目的。
9.反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
10.Mysql及JDBC开发 MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品。MySQL 最流行的关系型数据库管理系统,在 WEB 应用方面MySQL是最好的 RDBMS(Relational Database Management System,关系数据库管理系统)应用软件之一。
JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC执行SQL语句操作数据库。
1)Mysql数据库
数据库使用、mysql的安装与卸载、mysql账号管理,SQL语句介绍、数据库增删改操作DDL语句使用、表增删改操作DML语句使用、表查询操作DQL语句使用、以及查询语句条件、排序、聚合、分组操作。
2)JDBC开发
JDBC介绍、驱动的原理及使用、JDBC核心使用、DriverManager、Connection、Statement、ResultSet详细使用,JDBC的CURD操作、防止SQL注入及PrepareStatement使用、大数据处理、批处理。
3)DBUtils开发
连接池介绍、DBCP连接池使用。DBUtils介绍、DbUtils类的使用、QueryRunner类的使用、ResultSetHandler详解。
成为一名Java开发工程师,上面的内容是首要必学的知识点。认真的讲,基础知识学好了,后面的知识点学习会非常容易,都是在基础上或添加或者变化的。想要更全面的了解Java,欢迎你咨询黑马程序员官网,黑马程序员十年Java教学,坚守“为莘莘学子改变命运而讲课”这份情怀,不忘初心。最后一张图看看黑马程序员Java学员各阶段市场价值!
黑马程序员java知识
声明:除非特别标注,否则均为本站原创文章,转载时请以链接形式注明文章出处。如若本站内容侵犯了原著者的合法权益,可联系本站删除。


