java相关问题_Java相关问题

数据溢出

整型中,每个类型都有一定的表示范围,但是,在程序中有些计算会导致超出表示范围,即溢出

/**

* 数据溢出

* byte:byte用1个字节来存储,范围为-128(-2^7)到127(2^7-1),在变量初始化的时候,byte类型的默认值为0。

* short:short用2个字节存储,范围为-32,768 (-2^15)到32,767 (2^15-1),在变量初始化的时候,short类型的默认值为0,一般情况下,因为Java本身转型的原因,可以直接写为0。

* int:int用4个字节存储,范围为-2,147,483,648 (-2^31)到2,147,483,647 (2^31-1),在变量初始化的时候,int类型的默认值为0。

* long:long用8个字节存储,范围为-9,223,372,036,854,775,808 (-2^63)到9,223,372,036, 854,775,807 (2^63-1),在变量初始化的时候,long类型的默认值为0L或0l,也可直接写为0。

*/

public static void valueOverflow(){

int a = Integer.MAX_VALUE;

int b = Integer.MAX_VALUE;

int addResult = a+b;

System.out.println("a = "+ a +",b = " + b + " ,a+b = " + addResult);

}

a66deb684bb1

计算结果

自动拆装箱与缓存

public static void main(String[] args) {

Integer integer1 = 3;

Integer integer2 = 3;

if (integer1 == integer2){

System.out.println("integer1 == integer2");

}else {

System.out.println("integer1 != integer2");

}

Integer integer3 = 300;

Integer integer4 = 300;

if (integer3 == integer4){

System.out.println("integer3 == integer4");

}else {

System.out.println("integer3 != integer4");

}

}

a66deb684bb1

执行结果

Java中,==比较的是对象应用,equals比较的是值。

产生上述结果的原因

Integer中的缓存机制。Java5中,在Integer的操作上引入了一个新的功能来节省内存和提高性能。整型对象通过使用相同的对象引用实现了缓存和重用。

自动装箱本质

Integer i = 100;

Integer i_truth = Integer.valueOf(100);

PS:两条命令相等

a66deb684bb1

valueOf Java底层实现

在创建对象之前先从IntegerCache.cache中寻找。如果没找到才使用new新建对象。

IntegerCache Class

IntegerCache是Integer类中定义的一个private static的内部类。

/**

* Cache to support the object identity semantics of autoboxing for values between

* -128 and 127 (inclusive) as required by JLS.

*

* The cache is initialized on first usage. The size of the cache

* may be controlled by the {@code -XX:AutoBoxCacheMax=} option.

* During VM initialization, java.lang.Integer.IntegerCache.high property

* may be set and saved in the private system properties in the

* sun.misc.VM class.

*/

private static class IntegerCache {

static final int low = -128;

static final int high;

static final Integer cache[];

static {

// high value may be configured by property

int h = 127;

String integerCacheHighPropValue =

sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");

if (integerCacheHighPropValue != null) {

try {

int i = parseInt(integerCacheHighPropValue);

i = Math.max(i, 127);

// Maximum array size is Integer.MAX_VALUE

h = Math.min(i, Integer.MAX_VALUE - (-low) -1);

} catch( NumberFormatException nfe) {

// If the property cannot be parsed into an int, ignore it.

}

}

high = h;

cache = new Integer[(high - low) + 1];

int j = low;

for(int k = 0; k < cache.length; k++)

cache[k] = new Integer(j++);

// range [-128, 127] must be interned (JLS7 5.1.7)

assert IntegerCache.high >= 127;

}

private IntegerCache() {}

}

其中的javadoc详细的说明了缓存支持-128到127之间的自动装箱过程。最大值127可以通过-XX:AutoBoxCacheMax=size修改。 缓存通过一个for循环实现。从低到高并创建尽可能多的整数并存储在一个整数数组中。这个缓存会在Integer类第一次被使用的时候被初始化出来。以后,就可以使用缓存中包含的实例对象,而不是创建一个新的实例(在自动装箱的情况下)。

实际上这个功能在Java 5中引入的时候,范围是固定的-128 至 +127。后来在Java 6中,可以通过java.lang.Integer.IntegerCache.high设置最大值。这使我们可以根据应用程序的实际情况灵活地调整来提高性能。到底是什么原因选择这个-128到127范围呢?因为这个范围的数字是最被广泛使用的。 在程序中,第一次使用Integer的时候也需要一定的额外时间来初始化这个缓存。

PS:

适用于整数值区间-128~+127

只适用于自动装箱。使用构造函数创建对象不适用。

其他缓存的对象

这种缓存行为不仅适用于Integer对象。我们针对所有的整数类型的类都有类似的缓存机制。

ByteCache用于缓存Byte对象

ShortCache用于缓存Short对象

LongCache用于缓存Long对象

CharacterCache用于缓存Character对象

Byte, Short, Long有固定范围: -128 到 127。对于Character, 范围是 0 到 127。除了Integer以外,这个范围都不能改变。

阿里巴巴《Java开发手册》明确说明:

a66deb684bb1

包装类对象值比较

单例模式问题

单列模式——保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。

单列模式 VS 序列化、反序列化

/**

* 双重校验单列类

*/

public class Singleton implements Serializable {

private volatile static Singleton singleton;

private Singleton (){}

public static Singleton getSingleton() {

if (singleton == null) {

synchronized (Singleton.class) {

if (singleton == null) {

singleton = new Singleton();

}

}

}

return singleton;

}

}

测试类

public class SingletonTest {

public static void main(String[] args) throws IOException, ClassNotFoundException {

//Write Obj to file

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("tempFile"));

oos.writeObject(Singleton.getSingleton());

//Read Obj from file

File file = new File("tempFile");

ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));

Singleton newInstance = (Singleton) ois.readObject();

//判断是否是同一个对象

System.out.println(newInstance == Singleton.getSingleton());

}

}

结果:false

变种后的单列模式

/**

* 双重校验单列类

*/

public class Singleton implements Serializable {

private volatile static Singleton singleton;

private Singleton (){}

public static Singleton getSingleton() {

if (singleton == null) {

synchronized (Singleton.class) {

if (singleton == null) {

singleton = new Singleton();

}

}

}

return singleton;

}

private Object readResolve() {

return singleton;

}

}

PS:运行测试类 结果为 true

原因:序列化会通过反射调用无参数的构造方法创建一个新的对象

变种后的单例模式 通过在readResolve方法中指定要返回的对象的生成策略,即可防止单例被破坏。

SimpleDateFormat线程安全问题

a66deb684bb1

阿里开发手册

SimpleDateFormat线程不安全验证

/**

* @author 4869

* SimpleDateFormat 线程不安全验证

*/

public class PoolThreadTest {

/**

* 定义一个全局的SimpleDateFormat

*/

private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

/**

* 使用ThreadFactoryBuilder定义一个线程池

*/

private static ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()

.setNameFormat("demo-pool-%d").build();

private static ExecutorService pool = new ThreadPoolExecutor(5, 200,

0L, TimeUnit.MILLISECONDS,

new LinkedBlockingQueue(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());

/**

* 定义一个CountDownLatch,保证所有子线程执行完之后主线程再执行

*/

private static CountDownLatch countDownLatch = new CountDownLatch(100);

public static void main(String[] args) throws Exception{

//定义一个线程安全的HashSet

Set dates = Collections.synchronizedSet(new HashSet());

for (int i = 0; i < 100; i++) {

//获取当前时间

Calendar calendar = Calendar.getInstance();

int finalI = i;

pool.execute(() -> {

//时间增加

calendar.add(Calendar.DATE, finalI);

//通过simpleDateFormat把时间转换成字符串

String dateString = simpleDateFormat.format(calendar.getTime());

//把字符串放入Set中

dates.add(dateString);

//countDown

countDownLatch.countDown();

});

}

//阻塞,直到countDown数量为0

countDownLatch.await();

//输出去重后的时间个数

System.out.println(dates.size());

// System.exit(0);

}

}

PS : 不进行手动停止 主线程会一直处于阻塞状态;原因是启动的线程数小于100, countDown 不可能为0;

线程不安全的原因:

SimpleDateFormat中的format方法在执行过程中,会使用一个成员变量calendar来保存时间。声明SimpleDateFormat的时候,使用的是static定义的。那么SimpleDateFormat就是一个共享变量,SimpleDateFormat中的calendar也就可以被多个线程访问到。

解决原理:不要把SimpleDateFormat作为一个共享变量使用。

解决方法:1. SimpleDateFormat 变成局部变量使用 2. 加同步锁 3. ThreadLocal

JDK8 提供如下解决方案:可以使用Instant代替Date,LocalDateTime代替Calendar,DateTimeFormatter代替SimpleDateFormat,官方给出的解释:simple beautiful strong immutable thread-safe。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值