首页
Web开发
Windows程序
编程语言
数据库
移动开发
系统相关
微信
其他好文
会员
首页
>
编程语言
> 详细
[Java解惑]应用
时间:
2015-01-29 01:25:08
阅读:
475
评论:
0
收藏:
0
[点我收藏+]
标签:
应用
...
33
47.
不可变的引用类型
...
33
48.
请同时重写
equals()
与
hashCode()
33
49.
日期设置
...
34
50.
IdentityHashMap
.
34
51.
静态导入的优先权
...
35
52.
PrintStream
对输出结果的缓冲
...
36
53.
调用操作系统命令时被阻塞问题
...
36
54.
实现
Serializable
的单例问题
...
37
55.
thread. isInterrupted()
与
Thread.interrupted()
39
56.
惰性初始化
...
39
57.
继承内部类
...
41
58.
Hash
集合序列化问题
...
42
59.
迷惑的内部类
...
43
60.
编译期常量表达式
...
44
61.
打乱数组
...
45
应用
47.
不可变的引用类型
BigInteger total = BigInteger.
ZERO
;
total.add(
new
BigInteger("1"));
total.add(
new
BigInteger("10"));
System.
out
.println(total);//0
上面程序的结果为
11
吗?答案是
0
。
BigInteger
实例是不可变的。
String
、
BigDecimal
以及包装类型:
Integer
、
Long
、
Short
、
Byte
、
Character
、
Boolean
、
Float
和
Double
也是如此。对这些类型的操作将返回新的实例。
不可变类型更容易设计、实现与作用;它们出错的可能性更小,并且更加安全。
本程序修改如下:
BigInteger total = BigInteger.
ZERO
;
total=total.add(
new
BigInteger("1"));
total=total.add(
new
BigInteger("10"));
System.
out
.println(total);//11
48.
请同时重写
equals()
与
hashCode()
class
T {
private
String str;
T(String str) {
this
.str = str;
}
public
boolean
equals(Object obj) {
if
(!(obj
instanceof
T)){
return
false
;
}
T t = (T)obj;
return
t.equals(
this
.str);
}
public
static
void
main(String[] args) {
Set set =
new
HashSet();
set.add(
new
T("str"));
System.
out
.println(set.contains(
new
T("str")));//false
}
}
上面的程序不会打印
true
,而是
false
,为什么?
hashCode
约定要求相等的对象要具有相同的散列码。
无论何时,只要你重写了
equals
方法,你就必须同时重写
hashCode
方法。
如果将自定的类型对象放入
HashSet
、
HashMap
、
Hashtable
、
LinkedHashSet
、
LinkedHashMap
这此散列集合时,一定需要重写
equals
与
hashCode
方法,这样在放入进去之后还能查找出来。如果放入其他非散列类型的集合时,其实只需要重写
equals
就可以了。
本程序解决办法重写
hashCode()
方法:
public
int
hashCode() {
return
37 *
this
.str.hashCode();
}
49.
日期设置
Calendar c = Calendar.
getInstance
();
c.set(2010, 12, 31);//
月是从
0
开始的,
11
其实表示
12
月
System.
out
.println(c.get(Calendar.
YEAR
) + " " + c.get(Calendar.
MONTH
));
c = Calendar.
getInstance
();
c.set(2010, 11, 31);
System.
out
.println(c.get(Calendar.
YEAR
) + " " + c.get(Calendar.
MONTH
));
本程序较简单,只需注意月是从
0
开始的就可以了,如果你设置月为
12
,则会自动转换为下一年。
50.
IdentityHashMap
class
T {
private
String str;
T(String str) {
this
.str = str;
}
public
int
hashCode() {
return
37 *
this
.str.hashCode();
}
public
boolean
equals(Object obj) {
return
this
.str.equals(((T) obj).str);
}
public
static
void
put(Map m) {
m.put("str", "1");
/*
*
由于上面程序将
"str"
放入了字符串常量池,
*
所以
str
是同一个对象,不管是什么样类型的
* Map
,即使使用
IdentityHashMap
都只放入一次
*/
m.put("str", "2");
m.put(
new
T("str"), "3");
m.put(
new
T("str"), "4");
}
public
static
void
main(String[] args) {
Map m =
new
HashMap();
put
(m);
System.
out
.println(m.size());// 2
//IdentityHashMap
比较时使用
==
替换
equals()
方法
m =
new
IdentityHashMap();
put
(m);
System.
out
.println(m.size());// 3
}
}
51.
静态导入的优先权
import
static
java.util.Arrays.toString;
import
java.util.Arrays;
public
class
T {
public
static
void
main(String[] args) {
prt
(1, 2, 3);
}
static
void
prt(Object... args) {
//
自身继承至
Object
类的
toString
的优先级高于静态导入的方法
//!! System.out.println(toString(args));//
不能编译
System.
out
.println(Arrays.
toString
(args));
}
}
本身就属于某个范围的成员在该范围内与静态导入相比具有优先权。
52.
PrintStream
对输出结果的缓冲
public
static
void
main(String[] args) {
String str = "Hello World";
for
(
int
i = 0; i < str.length(); i++) {
System.
out
.write(str.charAt(i));
}
}
上面的程序没有输出结果。
这里的问题在于
System.out
是带有缓冲的。输出的结果被写入了
System.out
的缓冲区,但是缓冲区从来都没有被刷新。大多数人认为,当有输出产生的时候
System.out
和
System.err
会自动地进制刷新,但这并不完全正确,这两个流都属于
PrintStream
类型,请看
API DOC
描述:一个
PrintStream
被创建为自动刷新,这意味着当一个字节数组(
byte[]
)被写入、或者某个
println
方法被调用、或者一个换行字符或字节(
‘\n‘
)被写入之后,
PrintStream
类型的
flush
方法就会被自动调用。
令人奇怪的是,如果这个程序用
print(char)
去替代
write(int)
,它就会刷新
System.out
并输出结果,这种行为与
print(char)
的文档是矛盾的,因为其文档叙述道:“打印一个字符,这个字符将根据平台缺省的字符编码方式翻译成一个或多个字节,并且这些字节将完全按照
write(int)
方法的方式输出。”,但这里没有换行符却也自动的刷新了。类似的,如果程序改用
print(String)
,它也会对流进行刷新。所以调用
print
方法也是会自动刷新的。
请加入到原博客中:
PrintStream
也可以对
OutputStream
进行包装并指定编码方式:
PrintStream(OutputStream out, boolean autoFlush, String encoding)
,但实质上也是调用
OutputStreamWriter
来实现的。
System.err
在
eclipse
中输出时是红色的字体。
53.
调用操作系统命令时被阻塞问题
public
static
void
main(String[] args)
throws
IOException,
InterruptedException {
String command = "java ProcessTest exc";
if
(args.length != 0) {
for
(
int
i = 0; i < 200; i++) {
System.
out
.println(command);
System.
err
.println(command);
}
}
else
{
Process process = Runtime.
getRuntime
().exec(command);
int
exitValue = process.waitFor();
System.
out
.println("exit value = " + exitValue);
}
}
执行
java ProcessTest
发现程序阻塞。
Process
文档描述:由于某些本地平台只提供有限大小的缓冲,所以如果不能迅速地读取子进程的输出流,就有可能会导致子进程的阻塞,甚至是死锁。这恰好就是这里所发生的事情:没有足够的缓冲空间来保存这些输出结果。为了结子进程(
Process
线程),父进程(
Main
线程)必须排空它的输出流(标准流与错误流都需要排空),即要去缓存中读取结果:
static
void
readResult(
final
InputStream is) {
new
Thread(
new
Runnable() {
public
void
run() {
try
{
//
排空缓存内容
while
(is.read() >= 0);
}
catch
(IOException e) {
e.printStackTrace();
}
}
}).start();
}
然后在
process.waitFor()
之前加上
readResult
(process.getErrorStream());
readResult
(process.getInputStream());
即可输出
exit value = 0
。
另外,只能根据
process.waitFor
返回的结果来判断操作系统命令执行是否成功(成功:
0
,失败:
1
),我们不能根据错误流中是否有内容来判断是否执行成功。
54.
实现
Serializable
的单例问题
class
Dog
implements
Serializable{
public
static
final
Dog
INSTANCE
=
new
Dog();
private
Dog(){}
}
上面能控制只生成一个单实例吗?
如果对实现了
Serializable
的对象进行序列化后,再反序列化,内中会不只一个实例了,因为反序列化时会重新生成一个对象。
既然
INSTANCE
为静态域,那序列化时返回的对象如果也是
INSTANCE
就可以解决问题了,而打开
API
我们发现
Serializable
接口确实有这样两个特殊的方法描述:
l
将对象写入流时需要指定要使用的替代对象的可序列化类,应使用准确的签名来实现此特殊方法:
ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
此
writeReplace
方法将由序列化调用,前提是如果此方法存在,而且它可以通过被序列化对象的类中定义的一个方法访问。因此,该方法可以拥有私有
(private)
、受保护的
(protected)
和包私有
(package-private)
访问。子类对此方法的访问遵循
java
访问规则。
l
在从流中读取类的一个实例时需要指定替代的类应使用的准确签名来实现此特殊方法:
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
此
readResolve
方法遵循与
writeReplace
相同的调用规则和访问规则。
上述两个方法的只要出现,就会履盖以下两个方法(这两个方法本质的意义就是用来替换序列与反序列的对象),虽然会执行它们,但最后得到的结果却是
writeReplace
、
readResolve
两个方法写入或读出的对象:
l
private void writeObject(java.io.ObjectOutputStream out) throws IOException
l
private void readObject(java.io.ObjectInputStream in)throws IOException, ClassNotFoundException;
另外,
writeObject
与
readObject
需成对实现,而
writeReplace
与
readResolve
则不需要成对出现,一般单独使用。如果同时出现这四个方法,最后写入与读出的结果以
writeReplace
和
readResolve
方法的结果为准。
所以下要解决真真单实例问题,我们如下修正:
class
Dog
implements
Serializable {
public
static
final
Dog
INSTANCE
=
new
Dog();
private
Dog() {}
private
Object readResolve() {
return
INSTANCE
;
}
}
public
class
SerialDog {
public
static
void
main(String[] args)
throws
IOException,
ClassNotFoundException {
ByteArrayOutputStream bos =
new
ByteArrayOutputStream();
new
ObjectOutputStream(bos).writeObject(Dog.
INSTANCE
);
ByteArrayInputStream bin =
new
ByteArrayInputStream(bos.toByteArray());
Dog dog = (Dog)
new
ObjectInputStream(bin).readObject();
System.
out
.println(dog == Dog.
INSTANCE
);//true
}
}
一个实现了
Serializable
的单例类,必须有一个
readResolve
方法,用以返回它的唯一实例。
55.
thread. isInterrupted()
与
Thread.interrupted()
public
class
SelfInerruption {
public
static
void
main(String[] args) {
Thread.
currentThread
().interrupt();
if
(Thread.
interrupted
()) {
// Interruped:false
System.
out
.println("Interruped:" + Thread.
interrupted
());
}
else
{
System.
out
.println("Not interruped:" + Thread.
interrupted
());
}
}
}
上面结果走的是第一个分支,但结果却不是
Interruped:true
?
Thread.interrupted()
为
Thread
的静态方法,调用它首先会返回当前线程的中断状态(如果当前线程上调用了
interrupt()
方法,则返回
true
,否则为
false
),然后再清除当前线程的中断状态,即将中断状态设置为
false
。换句话说,如果连续两次调用该方法,则第二次调用将返回
false
。
而
isInterrupted()
方法为实例方法,测试线程是否已经中断,并不会清除当前线程中断状态。
所以这里应该使用
isInterrupted()
实例方法,就可以修复该问题。
56.
惰性初始化
public
class
Lazy {
private
static
boolean
initial
=
false
;
static
{
Thread t =
new
Thread(
new
Runnable() {
public
void
run() {
System.
out
.println("befor...");//
此句会输出
/*
*
由于使用
Lazy.initial
静态成员,又因为
Lazy
还未
初
*
始化完成,所以该线程会在这里等待主线程初始化完成
*/
initial
=
true
;
System.
out
.println("after...");//
此句不会输出
}
});
t.start();
try
{
t.join();//
主线程等待
t
线程结束
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
public
static
void
main(String[] args) {
System.
out
.println(
initial
);
}
}
看看上面变态的程序,一个静态变量的初始化由静态块里的线程来初始化,最后的结果怎样?
当一个线程访问一个类的某个成员的时候,它会去检查这个类是否已经被初始化,在这一过程中会有以下四种情况:
1、
这个类尚未被初始化
2、
这个类正在被当前线程初始化:这是对初始化的递归请求,会直接忽略掉(另,请参考《构造器中静态常量的引用问题》一节)
3、
这个类正在被其他线程而不是当前线程初始化:需等待其他线程初始化完成再使用类的
Class
对象,而不会两个线程都会去初始化一遍(如果这样,那不类会初始化两遍,这显示不合理)
4、
这个类已经被初始化
当主线程调用
Lazy.main
,它会检查
Lazy
类是否已经被初始化。此时它并没有被初始化(情况
1
),所以主线程会记录下当前正在进行的初始化,并开始对这个类进行初始化。这个过程是:主线程会将
initial
的值设为
false
,然后在静态块中创建并启动一个初始化
initial
的线程
t
,该线程的
run
方法会将
initial
设为
true
,然后主线程会等待
t
线程执行完毕,此时,问题就来了。
由于
t
线程将
Lazy.initial
设为
true
之前,它也会去检查
Lazy
类是否已经被初始化。这时,这个类正在被另外一个线程(
mian
线程)进行初始化(情况
3
)。在这种情况下,当前线程,也就是
t
线程,会等待
Class
对象直到初始化完成,可惜的是,那个正在进行初始化工作的
main
线程,也正在等待
t
线程的运行结束。因为这两个线程现在正相互等待,形成了死锁。
修正这个程序的方法就是让主线程在等待线程前就完成初始化操作:
public
class
Lazy {
private
static
boolean
initial
=
false
;
static
Thread
t
=
new
Thread(
new
Runnable() {
public
void
run() {
initial
=
true
;
}
});
static
{
t
.start();
}
public
static
void
main(String[] args) {
//
让
Lazy
类初始化完成后再调用
join
方法
try
{
t
.join();//
主线程等待
t
线程结束
}
catch
(InterruptedException e) {
e.printStackTrace();
}
System.
out
.println(
initial
);
}
}
虽然修正了该程序挂起问题,但如果还有另一线程要访问
Lazy
的
initial
时,则还是很有可能不等
initial
最后赋值就被使用了。
总之,在类的初始化期间等待某个线程很可能会造成死锁,要让类初始化的动作序列尽可能地简单。
57.
继承内部类
一般地,要想实例化一个内部类,如类
Inner1
,需要提供一个外围类的实例给构造器。一般情况下,它是隐式地传递给内部类的构造器,但是它也是可以以
expression.super(args)
的方式即通过调用超类的构造器显式的传递。
public
class
Outer {
class
Inner1
extends
Outer{
Inner1(){
super
();
}
}
class
Inner2
extends
Inner1{
Inner2
(){
Outer.
this
.
super
();
}
Inner2(Outer outer){
outer.
super
();
}
}
}
class
WithInner {
class
Inner {}
}
class
InheritInner
extends
WithInner.Inner {
// ! InheritInner() {} //
不能编译
/*
*
这里的
super
指
InheritInner
类的父类
WithInner.Inner
的默认构造函数,而不是
* WithInner
的父类构造函数,这种特殊的语法只在继承一个非静态内部类时才用到,
*
表示继承非静态内部类时,外围对象一定要存在,并且只能在
第一行调用,而且一
*
定要调用一下。为什么不能直接使用
super()
或不直接写出呢?最主要原因就是每个
*
非静态的内部类都会与一个外围类实例对应,这个外围类实例是运行时传到内
*
部类里去的,所以在内部类里可以直接使用那个对象(比如
Outer.this
),但这里
*
是在外部内外
,使用时还是需要存在外围类实例对象,所以这里就显示的通过构造
*
器传递进来,并且在外围对象上显示的调用一下内部类的构造器,这样就确保了在
*
继承至一个类部类的情况下
,外围对象一类会存在的约束。
*/
InheritInner(WithInner wi) {
wi.
super
();
}
public
static
void
main(String[] args) {
WithInner wi =
new
WithInner();
InheritInner
ii
=
new
InheritInner(wi);
}
}
58.
Hash
集合序列化问题
class
Super
implements
Serializable{
// HashSet
要放置在父类中会百分百机率出现
//
放置到子类中就不一定会出现问题了
final
Set set =
new
HashSet();
}
class
Sub
extends
Super {
private
int
id;
public
Sub(
int
id) {
this
.id = id;
set.add(
this
);
}
public
int
hashCode() {
return
id;
}
public
boolean
equals(Object o) {
return
(o
instanceof
Sub) && (id == ((Sub) o).id);
}
}
public
class
SerialKiller {
public
static
void
main(String[] args)
throws
Exception {
Sub sb =
new
Sub(888);
System.
out
.println(sb.set.contains(sb));// true
ByteArrayOutputStream bos =
new
ByteArrayOutputStream();
new
ObjectOutputStream(bos).writeObject(sb);
ByteArrayInputStream bin =
new
ByteArrayInputStream(bos.toByteArray());
sb = (Sub)
new
ObjectInputStream(bin).readObject();
System.
out
.println(sb.set.contains(sb));// false
}
}
Hash
一类集合都实现了序列化的
writeObject()
与
readObject()
方法。这里错误原因是由
HashSet
的
readObject
方法引起的。在某些情况下,这个方法会间接地调用某个未初始化对象的被覆写的方法。为了组装正在反序列化的
HashSet
,
HashSet.readObject
调用了
HashMap.put
方法,而
put
方法会去调用键的
hashCode
方法。由于整个对象图正在被反序列化,并没有什么可以保证每个键在它的
hashCode
方法被调用时已经被完全初始化了,因为
HashSet
是在父类中定义的,而在序列化
HashSet
时子类还没有开始初始化(这里应该是序列化)子类,所以这就造成了在父类中调用还没有初始完成(此时
id
为
0
)的被子类覆写的
hashCode
方法,导致该对象重新放入
hash
表格的位置与反序列化前不一样了。
hashCode
返回了错误的值,相应的键值对条目将会放入错误的单元格中,当
id
被初始化为
888
时,一切都太迟了。
这个程序的说明,包含了
HashMap
的
readObject
方法的序列化系统总体上违背了不能从类的构造器或伪构造器(如序列化的
readObject
)中调用可覆写方法的规则。
如果一个
HashSet
、
Hashtable
或
HashMap
被序列化,那么请确认它们的内容没有直接或间接地引用它们自身,即正在被序列化的对象。
另外,在
readObject
或
readResolve
方法中,请避免直接或间接地在正在进行反序列化的对象上调用任何方法,因为正在反序列化的对象处于不稳定状态。
59.
迷惑的内部类
public
class
Twisted {
private
final
String name;
Twisted(String name) {
this
.name = name;
}
//
私有的不能被继承,但能被內部类直接访问
private
String name() {
return
name;
}
private
void
reproduce() {
new
Twisted("reproduce") {
void
printName() {
// name()
为外部类的,因为没有被继承过来
System.
out
.println(name());// main
}
}.printName();
}
public
static
void
main(String[] args) {
new
Twisted("main").reproduce();
}
}
在顶层的类型中,即本例中的
Twisted
类,所有的本地的、内部的、嵌套的长匿名的类都可以毫无限制地访问彼此的成员。
另一个原因是私有的不能被继承。
60.
编译期常量表达式
第一个
PrintWords
代表客户端,第二个
Words
代表一个类库:
class
PrintWords {
public
static
void
main(String[] args) {
System.
out
//
引用常量变量
.println(Words.
FIRST
+ " "
+ Words.
SECOND
+ " "
+ Words.
THIRD
);
}
}
class
Words {
//
常量变量
public
static
final
String
FIRST
= "the";
//
非常量变量
public
static
final
String
SECOND
=
null
;
//
常量变量
public
static
final
String
THIRD
= "set";
}
现在假设你像下面这样改变了那个库类并且重新编译了这个类,但并不重新编译客户端的程序
PrintWords
:
class
Words {
public
static
final
String
FIRST
= "physics";
public
static
final
String
SECOND
= "chemistry";
public
static
final
String
THIRD
= "biology";
}
此时,端的程序会打印出什么呢?结果是
the chemistry set
,不是
the null set
,也不是
physics chemistry biology
,为什么?原因就是
null
不是一个编译期常量表达式,而其他两个都是。
对于常量变量(如上面
Words
类中的
FIRST
、
THIRD
)的引用(如在
PrintWords
类中对
Words.FIRST
、
Words.THIRD
的引用)会在编译期被转换为它们所表示的常量的值(即
PrintWords
类中的
Words.FIRST
、
Words.THIRD
引用会替换成
"the"
与
"set"
)。
一个常量变量(如上面
Words
类中的
FIRST
、
THIRD
)的定义是,一个在编译期被常量表达式(即编译期常量表达式)初始化的
final
的原生类型或
String
类型的变量。
那什么是“编译期常量表达式”?精确定义在
[JLS 15.28]
中可以找到,这样要说的是
null
不是一个编译期常量表达式。
由于常量变量会编译进客户端,
API
的设计者在设计一个常量域之前应该仔细考虑一下是否应该定义成常量变量。
如果你使用了一个非常量的表达式去初始化一个域,甚至是一个
final
或,那么这个域就不是一个常量。下面你可以通过将一个常量表达式传给一个方法使用得它变成一个非常量:
class
Words {
//
以下都成非常量变量
public
static
final
String
FIRST
=
ident
("the");
public
static
final
String
SECOND
=
ident
(
null
);
public
static
final
String
THIRD
=
ident
("set");
private
static
String ident(String s) {
return
s;
}
}
总之,常量变量将会被编译进那些引用它们的类中。一个常量变量就是任何常量表达式初始化的原生类型或字符串变量。且
null
不是一个常量表达式。
61.
打乱数组
class
Shuffle {
private
static
Random
rd
=
new
Random();
public
static
void
shuffle(Object[] a) {
for
(
int
i = 0; i < a.length; i++) {
swap
(a, i,
rd
.nextInt(a.length));
}
}
public
static
void
swap(Object[] a,
int
i,
int
j) {
Object tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
public
static
void
main(String[] args) {
Map
map =
new
TreeMap
();
for
(
int
i = 0; i < 9; i++) {
map.put(i, 0)
;
}
//
测试数组上的每个位置放置的元素是否等概率
for
(
int
i = 0; i < 10000; i++) {
Integer[] intArr =
new
Integer[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
shuffle
(intArr);
for
(
int
j = 0; j < 9; j++) {
map.put(j,(Integer)map.get(j)+intArr[j])
;
}
}
System.
out
.println(map);
for
(
int
i = 0; i < 9; i++) {
map.put(i,(Integer) map.get(i)/10000f)
;
}
System.
out
.println(map);
}
}
上面的算法不是很等概率的让某个元素打乱到其位置,程序运行了多次,大致的结果为:
{0=36031, 1=38094, 2=39347, 3=40264, 4=41374, 5=41648, 6=41780, 7=41188, 8=40274}
{0=3.6031, 1=3.8094, 2=3.9347, 3=4.0264, 4=4.1374, 5=4.1648, 6=4.178, 7=4.1188, 8=4.0274}
如果某个位置上等概率出现这
9
个值的话,则平均值会趋近于
4
,但测试的结果表明:开始的时候比较低,然后增长超过了平均值,最后又降下来了。
如果改用下面算法:
public
static
void
shuffle(Object[] a) {
for
(
int
i = 0; i < a.length; i++) {
swap
(a, i, i +
rd
.nextInt(a.length - i));
}
}
多次测试的结果大致如下:
{0=40207, 1=40398, 2=40179, 3=39766, 4=39735, 5=39710, 6=40074, 7=39871, 8=40060}
{0=4.0207, 1=4.0398, 2=4.0179, 3=3.9766, 4=3.9735, 5=3.971, 6=4.0074, 7=3.9871, 8=4.006}
所以修改后的算法是合理的。
另一种打乱集合的方式是通过
Api
中的
Collections
工具类:
public
static
void
shuffle(Object[] a) {
Collections.
shuffle
(Arrays.
asList
(a));
}
其实算法与上面的基本相似,当然我们使用
API
中提供的会更好,会在效率上获得最大的受益。
[Java解惑]应用
标签:
原文地址:http://www.cnblogs.com/jiangzhengjun/p/4257679.html
踩
(
0
)
赞
(
0
)
举报
评论
一句话评论(
0
)
登录后才能评论!
分享档案
更多>
2021年07月29日 (22)
2021年07月28日 (40)
2021年07月27日 (32)
2021年07月26日 (79)
2021年07月23日 (29)
2021年07月22日 (30)
2021年07月21日 (42)
2021年07月20日 (16)
2021年07月19日 (90)
2021年07月16日 (35)
周排行
更多
Spring Cloud 从入门到精通(一)Nacos 服务中心初探
2021-07-29
基础的排序算法
2021-07-29
SpringBoot|常用配置介绍
2021-07-29
关于 .NET 与 JAVA 在 JIT 编译上的一些差异
2021-07-29
C语言常用函数-toupper()将字符转换为大写英文字母函数
2021-07-29
《手把手教你》系列技巧篇(十)-java+ selenium自动化测试-元素定位大法之By class name(详细教程)
2021-07-28
4-1 YAML配置文件 注入 JavaBean中
2021-07-28
【python】 用来将对象持久化的 pickle 模块
2021-07-28
马拉车算法
2021-07-28
用Python进行冒泡排序
2021-07-28
友情链接
兰亭集智
国之画
百度统计
站长统计
阿里云
chrome插件
新版天听网
关于我们
-
联系我们
-
留言反馈
© 2014
mamicode.com
版权所有 联系我们:gaon5@hotmail.com
迷上了代码!