java安全编码指南之:声明和初始化说明

简介

在java对象和字段的初始化过程中会遇到哪些安全性问题呢?一起来看看吧。

初始化顺序

根据JLS(Java Language Specification)中的定义,class在初始化过程中,需要同时初始化class中定义的静态初始化程序和在该类中声明的静态字段(类变量)的初始化程序。

而对于static变量来说,如果static变量被定义为final并且它值是编译时常量值,那么该static变量将会被优先初始化。

那么使用了final static变量,是不是就没有初始化问题了呢?

我们来看下面一个例子:

public class StaticFiledOrder {
  private final int result;
  private static final StaticFiledOrder instance = new StaticFiledOrder();
  private static final int intValue=100;
  public StaticFiledOrder(){
    result= intValue - 10;
  }

  public static void main(String[] args) {
    System.out.println(instance.result);
  }
}

输出结果是什么呢?

答案是90。 根据我们提到的规则,intValue是final并且被编译时常量赋值,所以是最先被初始化的,instance调用了StaticFiledOrder类的构造函数,最终导致result的值是90。

接下来,我们换个写法,将intValue改为随机变量:

public class StaticFiledOrder {
  private final int result;
  private static final StaticFiledOrder instance = new StaticFiledOrder();
  private static final int intValue=(int)Math.random()* 1000;
  public StaticFiledOrder(){
    result= intValue - 10;
  }

  public static void main(String[] args) {
    System.out.println(instance.result);
  }
}

运行结果是什么呢?

答案是-10。为什么呢?

因为instance在调用StaticFiledOrder构造函数进行初始化的过程中,intValue还没有被初始化,所以它有一个默认的值0,从而导致result的最终值是-10。

怎么修改呢?

将顺序调换一下就行了:

public class StaticFiledOrder {
  private final int result;
  private static final int intValue=(int)Math.random()* 1000;
  private static final StaticFiledOrder instance = new StaticFiledOrder();
  public StaticFiledOrder(){
    result= intValue - 10;
  }

  public static void main(String[] args) {
    System.out.println(instance.result);
  }
}

循环初始化

既然static变量可以调用构造函数,那么可不可以调用其他类的方法呢?

看下这个例子:

public class CycleClassA {
  public static final int a = CycleClassB.b+1;
}
public class CycleClassB {
  public static final int b = CycleClassA.a+1;
}

上面就是一个循环初始化的例子,上面的例子中CycleClassA中的a引用了CycleClassB的b,而同样的CycleClassB中的b引用了CycleClassA的a。

这样循环引用虽然不会报错,但是根据class的初始化顺序不同,会导致a和b生成两种不同的结果。

所以在我们编写代码的过程中,一定要避免这种循环初始化的情况。

不要使用java标准库中的类名作为自己的类名

java标准库中为我们定义了很多非常优秀的类,我们在搭建自己的java程序时候可以很方便的使用。

但是我们在写自定义类的情况下,一定要注意避免使用和java标准库中一样的名字。

这个应该很好理解,就是为了避免混淆。以免造成不必要的意外。

这个很简单,就不举例子了。

不要在增强的for语句中修改变量值

我们在遍历集合和数组的过程中,除了最原始的for语句之外,java还为我们提供了下面的增强的for循环:

for (I #i = Expression.iterator(); #i.hasNext(); ) {
  {VariableModifier} TargetType Identifier =
    (TargetType) #i.next();
  Statement
}

在遍历的过程中,#i其实相当于一个本地变量,对这个本地变量的修改是不会影响到集合本身的。

我们看一个例子:

  public void noncompliantUsage(){
    int[] intArray = new int[]{1,2,3,4,5,6};
    for(int i: intArray){
      i=0;
    }
    for(int i: intArray){
      System.out.println(i);
    }
  }

我们在遍历过程中,尝试将i都设置为0,但是最后输出intArray的结果,发现没有任何变化。

所以,一般来说我们需要在增强的for语句中,将#i设置成为final,从而消除这种不必要的逻辑误会。

  public void compliantUsage(){
    int[] intArray = new int[]{1,2,3,4,5,6};
    for(final int i: intArray){
    }
    for(int i: intArray){
      System.out.println(i);
    }
  }

本文的例子:

learn-java-base-9-to-20/tree/master/security

补充知识:Java中String字符串初始化细节

Java中String类型细节

一 . String两种初始化方式

1 . String str1= “abc”;//String类特有的创建字符对象的方式,更高效

在字符串缓冲区中检测”abc”是否存在

若存在则不重复创建,将地址赋值给str1.

若不存在,则在字符串缓冲区中创建对象并赋地址给str1.

2 . String str1= new String( “abc”); //构造函数初始化

 或者

 char [] ch={‘a','b','c'};

 String str1=new String (ch);

先有 “abc” 对象,然后拷贝给构造函数创建的对象(相当于str1得到的是构造函数的副本)

String对象是不可变的,它的内容不能改变,而在程序中字符串频繁使用,为了提高效率,对具有相同字符串序列的字符串直接量使用同一个实例,这样的实例被称之为限定的(interned)

注意,第二种方式的参数只支持字符串直接量或字符数组创建,这种方式是错误的 String strA = “asd”;

String strbB = new Strint(strA);

比较两种创建方式,第一种更高效,只创建了一个对象,第二种创建了两个对象。

二 . 初始化细节

栈中保存基本类型与对象的引用,基本类型在创建前会查看Stack中是否已经有, 有则赋值指向, 没有则创建。

String str1= “abc”;

String str1= new String( “abc”);

前者首先在栈中创建一个引用型变量str1,然后查看栈中是否存在“abc”如果没有,则将“abc”存放进栈,并令引用变量str指向它;如果有,则直接令str1指向它;后者是java中标准的对象创建方式,其创建的对象将直接放置到堆中,每调用一次就会创建一个新的对象。

String str = “abc”+”def”;

这条语句创建对象个数? 1个。

编译器会自己调用Stringbuilder的append方法来合成abcdef,最后只生成一个对象.

实际上,字符串直接量属于常量,在编译的时候已确定,两个常量相加,先检测栈内存中是否有”abcdef” 如有有,指向已有的栈中的”abcdef”空间,若没有,则创建。

package stringDemo;
public class stringInitial
{
  public static void main(String[] args)
  {
  String str1 = "abc";
  String str2 = new String("abc");
  // String str2 = new String(new char[] { 'a', 'b', 'c' });
  // String str2 = new String(str1);错误写法

  System.out.println(str1 == str2);// false
  System.out.println(str1.equals(str2));// true这里的equals()方法已经被覆盖,比较的是字符串不是地址

  String str3 = "123";
  String str4 = "abc123";
  String str5 = "abc" + "123";
  String str = str1 + str3;
  System.out.println(str4 == str5);// true
  System.out.println(str4 == str1+"123");// false
  System.out.println(str4 == str);// false

  }
}

可以看出,只要是+中出现非字符串直接量,就会在堆中产生新的对象,并不会检测栈内存

三.关于String str=null;String str;String str=”“;

String str=null;

声明了一个String的引用型变量并初始化为空,及未指向任何地址,不占用任何空间

String str;

只是声明了一个String的引用型变量,并未初始化(作为对象属性时会有默认的隐式初始化str=null),如果后面未用此变量编译会通过

String str=”“;

正常的字符串初始化,只不过字符串内容为空。

以上这篇java安全编码指南之:声明和初始化说明就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • java安全编码指南之:Number操作详解

    简介 java中可以被称为Number的有byte,short,int,long,float,double和char,我们在使用这些Nubmer的过程中,需要注意些什么内容呢?一起来看看吧. Number的范围 每种Number类型都有它的范围,我们看下java中Number类型的范围: 考虑到我们最常用的int操作,虽然int的范围够大,但是如果我们在做一些int操作的时候还是可能超出int的范围. 超出了int范围会发送什么事情呢?看下面的例子: public void testIntege

  • java安全编码指南之:Mutability可变性详解

    简介 mutable(可变)和immutable(不可变)对象是我们在java程序编写的过程中经常会使用到的. 可变类型对象就是说,对象在创建之后,其内部的数据可能会被修改.所以它的安全性没有保证. 而不可变类型对象就是说,对象一旦创建之后,其内部的数据就不能够被修改,我们可以完全相信这个对象. 虽然mutable对象安全性不够,但是因为其可以被修改,所以会有效的减少对该对象的拷贝. 而immutable对象因为不可改变,所以尝试对该对象的修改都会导致对象的拷贝,从而生成新的对象. 我们最常使用

  • Java开发中常用的 Websocket 技术参考

    1. 前言 Websocket是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议.WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据,当然也支持客户端发送数据到服务端.通常用来社交聊天.弹幕.多玩家游戏.协同编辑.股票基金实时报价.资讯自动更新等场景,那么今天就简单聊一下在 Java 开发中对Websocket的技术选型. 技术选型是结合自身业务选择最适合的技术方案,并不存在褒贬. 2. 常用的 Websocket 技术 2.1

  • java安全编码指南之:表达式规则说明

    简介 在java编写过程中,我们会使用到各种各样的表达式,在使用表达式的过程中,有哪些安全问题需要我们注意的呢?一起来看看吧. 注意表达式的返回值 我们在使用JDK库的时候,一定要注意认真的读一下JDK中方法的含义和它的返回值. 有些返回值可能表示这个操作是否成功,有的返回值可能是方法操作的结果.我们看两个常见的例子: public void deleteFileWrong(){ File file= new File("/tmp/www.jb51.net.txt"); file.de

  • Java 基于AQS实现一个同步器

    前面说了这个多,我们可以自己尝试实现一个同步器,我们可以简单的参考一下ReentrantLock这个类的实现方式,我们就简单的实现一个不可重入的独占锁吧! 一.简单分析ReentrantLock的结构 下图所示,直接实现了Lock这个接口,然后定义了一个内部类继承AQS,暂时不考虑公平锁和非公平锁,前面说AQS的时候说过,留有tryAcquire,tryRelease这两个方法在具体子类中根据实际情况实现的,可想而知这个内部类主要的是实现tryAcquire,tryRelease: 我们看看Lo

  • java安全编码指南之:声明和初始化说明

    简介 在java对象和字段的初始化过程中会遇到哪些安全性问题呢?一起来看看吧. 初始化顺序 根据JLS(Java Language Specification)中的定义,class在初始化过程中,需要同时初始化class中定义的静态初始化程序和在该类中声明的静态字段(类变量)的初始化程序. 而对于static变量来说,如果static变量被定义为final并且它值是编译时常量值,那么该static变量将会被优先初始化. 那么使用了final static变量,是不是就没有初始化问题了呢? 我们来

  • 浅谈java安全编码指南之死锁dead lock

    不同的加锁顺序 我们来看一个不同加锁顺序的例子: public class DiffLockOrder { private int amount; public DiffLockOrder(int amount){ this.amount=amount; } public void transfer(DiffLockOrder target,int transferAmount){ synchronized (this){ synchronized (target){ if(amount< tr

  • 详解java安全编码指南之可见性和原子性

    不可变对象的可见性 不可变对象就是初始化之后不能够被修改的对象,那么是不是类中引入了不可变对象,所有对不可变对象的修改都立马对所有线程可见呢? 实际上,不可变对象只能保证在多线程环境中,对象使用的安全性,并不能够保证对象的可见性. 先来讨论一下可变性,我们考虑下面的一个例子: public final class ImmutableObject { private final int age; public ImmutableObject(int age){ this.age=age; } }

  • java安全编码指南之:对象构建操作

    简介 程序员肯定是不缺对象的,因为随时都可以构建一个,对象多了肯定会出现点安全问题,一起来看看在java的对象构建中怎么保证对象的安全性吧. 构造函数的异常 考虑下面的一个例子: public class SensitiveOperation { public SensitiveOperation(){ if(!doSecurityCheck()){ throw new SecurityException("Security check failed!"); } } //Securit

  • 浅谈java安全编码指南之堆污染

    产生堆污染的例子 有同学可能会问了,既然JDK5引入了泛型,为什么还会出现堆污染呢? 这是一个好问题,让我们看一个例子: public void heapPollution1(){ List normalList= Arrays.asList("www.flydean.com",100); List<Integer> integerList= normalList; } 上面的例子中,我们使用Arrays.asList创建了一个普通的List. 这个List中包含了int和

  • 养成良好java代码编码规范

    一.基本原则 强制性原则: 1.字符串的拼加操作,必须使用StringBuilder: 2.try-catch的用法 try{ }catch{Exception e e.printStackTrace(); }finally{ }//在最外层的Action中可以使用,其它地方一律禁止使用: try{ //程序代码 }catch(Exception e){ //为空,什么都不写 }//在任何场景中都禁止使用 try{ }catch{Exception e throw new runtimeExce

  • Java类的加载连接和初始化实例分析

    本文实例讲述了Java类的加载连接和初始化.分享给大家供大家参考,具体如下: 一 点睛 1 类加载 当程序主动使用某个类时,如果该类还未被加载到内存中,系统会通过加载.连接.初始化三个步骤来对该类进行初始化,如果没有意外,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载或类初始化. 类加载指的是将类的class文件读入内存,并为之创建一个java.lang.Class对象,也就是说当程序使用任何类时,系统都会为之建立一个java.lang.Class对象. 2 类数据的来源 通过

随机推荐