从应用层而言我们所说的地址当然是指虚拟地址.而对于中间语言(java的byteCode/.NET的IL)而言是托管地址.我们不必关心实际的内存地址如何和它们对应,这由作业系统和应用环境来决定,你想关心也关心不了.我们要关心的是理解作业系统和应用环境提供给我们的可访问地址的内存布局.
对于集合类参数,如果传入方法后在方法外重新赋值参数本身,这和其它引用参数一样不会影响方法内的参数.但如果对集合中元素重新赋值则改变了方法内的集合中的元素,因为方法外和方法内的集合就是本身是同一对象. class MyRun{
public static void exec(String[] args){ for(int i=0;i static void check(String str) throws Exception{ if(str.equals(\"s2\")) throw new Exception(\"error1\"); } static void invoke(String str){ System.out.println(\"执行的语句是\"+str); } } 这段程序设计是否有错?如果有错,如何修改? 这是我在bea论坛上贴出来的一段程序,最初没有一个人能说明有什么错,更别说如何修改.(我知道有很多水平很高的高手根本不去bea论坛所以没有看到这个问题) 更可悲的是我把问题展示了很多根本看不懂的人说我在胡说八道,这就是中国程序员的现状,他没有能力理解和不知道的东西都叫胡说八道,当他上小学时他说初中,高中,大学,研究生的知识是胡说八道的. 这个问题的展示很简单: 当你设计了上面的类以后, 那么我作为调用者,我可以任何方式调用你的类,你都应该是安全的. 好,我现在这样调用: 先设计一个用来改变数据的线程: class ModifyThread extends Thread{ private String[] arr; public ModifyThread(String[] arr){ this.arr = arr; } public void run(){ try{ Thread.currentThread().sleep(20); }catch(Exception e){} arr[0] = \"s2\"; } } 为了说明问题,我在你设计的类中插入一段sleep来模拟线程运行到那里时被切换到其它线程运行,然后又切换回来到本线程运行的情况: class MyRun{ public static void exec(String[] args){ for(int i=0;i //为了说明问题,在这儿sleep(100)来模拟运行到这里时线切换到其它线程去运行 Thread.currentThread().sleep(100); }catch(Exception e){} //然后又回到这个这线程继续运行. invoke(args[ i ]); } } static void check(String str) throws Exception{ if(str.equals(\"s2\")) throw new Exception(\"error1\"); } static void invoke(String str){ System.out.println(\"执行的语句是\"+str); } } 然后调用: public class Main { /** Creates a new instance of Main */ public static void main(String[] args) { // TODO code application logic here String[] strs = {\"s1\ new ModifyThread(strs).start(); MyRun.exec(strs); } } 试试看,我利用一个辅助线程就把s2传进去执行了. 好,有人说要同步: class MyRun{ public static void exec(String[] args){ for(int i=0;i //为了说明问题,在这儿sleep(100)来模拟运行这里时线切换到其它线程去运行了. //然后又回到这个这线程继续运行. Thread.currentThread().sleep(100); }catch(Exception e){} invoke(args[ i ]); } } } static void check(String str) throws Exception{ if(str.equals(\"s2\")) throw new Exception(\"error1\"); } static void invoke(String str){ System.out.println(\"执行的语句是\"+str); } } 再调用看看,s2仍然越过了check.因为同步只能保证明多个MyRun.exec()方法在执行时只有一个线程能访问方法内的args数组,根本无法保证从方法外修改strs, 因为方法外的代码没有和方法内共同竞争同一对象锁,.我用sleep来模拟线程间执行的切换.其实只要我用足够多的ModifyThread线程在不同时刻运行起来,和MyRun.exec一起运行,产生这种切换的可能性就非常大.简单说我只要用足够多的辅助线程就能绕过你设计的check. 其实要解决问题非常容易: class MyRun{ public static void exec(String[] args){ //将传入变量复制为方法内的本地变量,打断与方法外的联系. //然后只能本地变量操作 String[]temp = new String[args.length]; System.arraycopy(args, 0,temp, 0, args.length); for(int i=0;i //为了说明问题,在这儿sleep(100)来模拟运行这里时线切换到其它线程去运行了. //然后又回到这个这线程继续运行. Thread.currentThread().sleep(100); }catch(Exception e){} invoke(temp[ i ]); //} } } static void check(String str) throws Exception{ if(str.equals(\"s2\")) throw new Exception(\"error1\"); } static void invoke(String str){ System.out.println(\"执行的语句是\"+str); } } 如果你还不能理解或者不相信我,我们来看看JDK(1.6)是如何处理的.Runtime.exec最终调用了ProcessBuilder的start()方法.在ProcessBuilder中,外部命令可以通过command(List 这样外部传入一个保存了多个命令的List后,从外部仍然可以访问方法内的List中的内容.所以在start()方法中JDK这样处理,而且加上了这样的注释: // Must convert to array first -- a malicious user-supplied // list might try to circumvent the security check. String[] cmdarray = command.toArray(new String[command.size()]); 然后对方法内的cmdarray 进行操作,用户修改外部的list就不会影响到cmdarray 中的元素. 这不仅是安全的问题.设计一个不变类,如果传入参数是数据结构的容器类,那就不能保证是不变类,但只要在处理前复制为方法内的局部变量就可以保证类的不变性. 因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- 69lv.com 版权所有 湘ICP备2023021910号-1
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务