精华内容
下载资源
问答
  • 今天重写了一下appium得自动化框架,去除po模型,采用关键字模型,使用数据驱动模式提高自动化覆盖效率,让自动化过程更加依赖框架,将使用难度降到低点。 框架结构如下: 一下大体思路,首先是appium服务这里,...

    今天重写了一下appium得自动化框架,去除po模型,采用关键字模型,使用数据驱动模式提高自动化覆盖效率,让自动化过程更加依赖框架,将使用难度降到低点。

    框架结构如下:
    在这里插入图片描述
    串一下大体思路,首先是appium服务这里,使用命令行封装,运行命令启动appium server以及启动过程中获取到driver得配置信息,deviceName,port,bp,systemPort,platformVersion,udid,等动态配置写入yaml配置文件,等待启动driver时,读取刚写入yaml的信息,进行driver驱动,driver封装在baseDriver中,在框架中只在mytools的封装定位模块中使用,将定位信息交于关键字处理,关键字通过映射获取动作,对指定定位元素,做指定动作,输入指定数据,执行指定效验,回写测试结果,生成测试报告,使用完毕杀掉appium进程。

    关键字设计思路,ini文件配置好元素模块,元素名,通过这两者,找到定位方法和对应值,excel文件时用例,元素设计如下,可自定更改
    在这里插入图片描述
    在这里插入图片描述
    主要思路在,从excel中的执行动作,这个动作就是关键字,所有的动作都要封装如关键字中,excel取出动作后通过映射获取其中的方法,通过元素模块,元素名,去ini找到元素定位方法及值,传给定位方法,转化为元素返回运行文件,运行文件对该元素执行映射回来的指定动作

    展开全文
  • java 内存模型 final 关键字-08

    万次阅读 2018-12-12 21:04:44
    基础知识 基本用法 修饰类 当用final修饰一个类时,表明这个类不能被继承。 也就是说,如果一个类你永远不会让他被继承,就可以用final...第一个原因是方法锁定,以防任何继承类修改它的含义; 第二个原因是效率...

    基础知识

    基本用法

    • 修饰类

    当用final修饰一个类时,表明这个类不能被继承。

    也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。

    final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。

    • 修饰方法

    使用final方法的原因有两个。

    第一个原因是把方法锁定,以防任何继承类修改它的含义;

    第二个原因是效率。

    在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。

    在最近的Java版本中,不需要使用final方法进行这些优化了。

    • 修饰变量

    对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;

    如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。

    匿名内部类

    匿名内部类使用外部变量时为何要强制使用 final 修饰

    private void initViews() {
        final int a = 3; // Compilation error if remove final
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (a > 1) {
                    // volala
                }
            }
        }
    }
    

    那么,有没有想过为什么?

    而像其他支持完整闭包的语言如JavaScript,Python等,是没有这等限制的。

    究其原因,是Java对闭包支持不够完整,或者说它并不是像动态语言那样的完整闭包。

    对于匿名内部类来说,编译器会创建一个命名类(OutClass$1之类的),然后把匿名类所在的[能捕获的变量](https://en.wikipedia.org/wiki/Closure_(computer_programming),以构造参数的形式传递给内部类使用,这样一样,外部的变量与内部类看到的变量是不同的,虽然它们的值是相同的,因此,如果再允许外部修改这些变量,或者内部类里面修改这些变量,都会造成数据的不一致性(因为它们是不同的变量),所以Java强制要求匿名内部类访问的外部变量要加上final来修饰。

    对于其他语言,匿名内部类,持有的是外部变量的一个包装的引用(wrapper reference),这可以能看不懂,但是理解起来就是内部类能直接访问外部变量,外部与闭包内部访问的是同一个变量,因此外部修改了,内部能看到变化,内部修改了,外部也能看到变化。

    一句话总结就是,Java 内部类与外部持有的是值相同的不同的变量;其他支持闭包的语言则持有的是相同的变量。

    ps: Jdk 1.8+ 就没有这种限制了。

    JLS 规范

    各种博客内容大都人云亦云,此处从官方文档进行再次学习。

    final class

    如果未声明 final 的类被更改为要声明 final,那么如果加载了该类已有子类的二进制文件,则抛出 VerifyError,
    因为 final 类不能有子类;对于广泛分布的类,不推荐这样的更改。

    更改已声明为 final 的类,不再被声明为 final,不会破坏与已有二进制文件的兼容性。

    final methods

    将已声明为 final 的方法更改为不再声明为final不会破坏与已有二进制文件的兼容性。

    更改未声明为 final 的实例方法可能会破坏与依赖于重写方法能力的现有二进制文件的兼容性。

    • 普通版本
    class Super { void out() { System.out.println("out"); } }
    class Test extends Super {
        public static void main(String[] args) {
            Test t = new Test();
            t.out();
        }
        void out() { super.out(); }
    }
    
    • final 版本
    class Super { final void out() { System.out.println("!"); } }
    

    如果Super被重新编译,但没有进行测试,那么使用已有的测试二进制文件运行新的二进制文件将导致VerifyError,因为类测试不正确地尝试覆盖实例方法。

    更改未声明为final的类(static)方法不会破坏与现有二进制文件的兼容性,因为该方法不可能被重写。

    final & static

    final fields

    JMM final

    与前面介绍的锁和 volatil e相比较,对 final 域的读和写更像是普通的变量访问。

    对于 final 域,编译器和处理器要遵守两个重排序规则:

    • 在构造函数内对一个 final 域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。

    • 初次读一个包含 final 域的对象的引用,与随后初次读这个 final 域,这两个操作之间不能重排序。

    实例

    • FinalExample.java
    public class FinalExample {
        int i;                            //普通变量
        final int j;                      //final变量
        static FinalExample obj;
    
        public void FinalExample () {     //构造函数
            i = 1;                        //写普通域
            j = 2;                        //写final域
        }
    
        public static void writer() {    //写线程A执行
            obj = new FinalExample ();
        }
    
        public static void reader() {       //读线程B执行
            FinalExample object = obj;       //读对象引用
            int a = object.i;                //读普通域
            int b = object.j;                //读final域
        }
    }
    

    这里假设一个线程 A 执行 writer() 方法,随后另一个线程 B 执行 reader() 方法。

    下面我们通过这两个线程的交互来说明这两个规则。

    写 final 域的重排序规则

    写 final 域的重排序规则禁止把 final 域的写重排序到构造函数之外。这个规则的实现包含下面 2 个方面:

    • JMM 禁止编译器把 final 域 的写重排序到构造函数之外。

    • 编译器会在 final 域的写之后,构造函数 return 之前,插入一个 StoreStore 屏障。这个屏障禁止处理器把 final 域的写重排序到构造函数之外。

    现在让我们分析 writer() 方法。

    writer() 方法只包含一行代码:finalExample = new FinalExample()

    这行代码包含两个步骤:

    1. 构造一个 FinalExample 类型的对象;

    2. 把这个对象的引用赋值给引用变量 obj。

    假设线程 B 读对象引用与读对象的成员域之间没有重排序(马上会说明为什么需要这个假设),下图是一种可能的执行时序:

    时间线:----------------------------------------------------------------->
    线程A:执行构造函数 写j=2 StoreStore屏障 构造函数结束 构造函数的引用赋值给引用变量obj (...线程B...) 写i=1      
    线程B:读对象引用obj 读对象普通域i(×) 读对象final域j(√)  
    

    在以上流程中,写普通域的操作被编译器重排序到了构造函数之外,读线程 B 错误的读取了普通变量i初始化之前的值。

    而写 final 域的操作,被写 final 域的重排序规则“限定”在了构造函数之内,读线程 B 正确的读取了 final 变量初始化之后的值。

    写 final 域的重排序规则可以确保:
    在对象引用为任意线程可见之前,对象的final域已经被正确初始化过了,而普通域不具有这个保障。

    以上图为例,在读线程 B “看到”对象引用 obj 时,很可能 obj 对象还没有构造完成(对普通域i的写操作被重排序到构造函数外,此时初始值 2 还没有写入普通域 i)。

    读 final 域的重排序规则

    读 final 域的重排序规则如下:

    • 在一个线程中,初次读对象引用与初次读该对象包含的 final 域,JMM禁止处理器重排序这两个操作(注意,这个规则仅仅针对处理器)。
      编译器会在读 final 域操作的前面插入一个 LoadLoad 屏障。

    初次读对象引用与初次读该对象包含的 final 域,这两个操作之间存在间接依赖关系。

    由于编译器遵守间接依赖关系,因此编译器不会重排序这两个操作。

    大多数处理器也会遵守间接依赖,大多数处理器也不会重排序这两个操作。

    但有少数处理器允许对存在间接依赖关系的操作做重排序(比如alpha处理器),这个规则就是专门用来针对这种处理器。

    reader() 方法包含三个操作:

    1. 初次读引用变量 obj;

    2. 初次读引用变量 obj 指向对象的普通域 j。

    3. 初次读引用变量 obj 指向对象的 final 域 i。

    现在我们假设写线程 A 没有发生任何重排序,同时程序在不遵守间接依赖的处理器上执行,下面是一种可能的执行时序:

    时间线:------------------------------------------------------------------------->
    线程A:执行构造函数 写i=1 写j=2 StoreStore屏障 构造函数结束 构造函数的引用赋值给引用变量obj
    线程B:读对象普通域i(×) (...A执行完...) 读对象引用obj LoadLoad屏障 读对象final域j(√)  
    

    在上图中,读对象的普通域的操作被处理器重排序到读对象引用之前。
    读普通域时,该域还没有被写线程A写入,这是一个错误的读取操作。
    而读final域的重排序规则会把读对象final域的操作“限定”在读对象引用之后,此时该final域已经被A线程初始化过了,这是一个正确的读取操作。

    读 final 域的重排序规则可以确保:
    在读一个对象的 final 域之前,一定会先读包含这个final域的对象的引用。

    在这个示例程序中,如果该引用不为null,那么引用对象的final域一定已经被A线程初始化过了。

    如果 final 域是引用类型

    上面我们看到的 final 域是基础数据类型,下面让我们看看如果 final 域是引用类型,将会有什么效果?

    请看下列示例代码:

    public class FinalReferenceExample {
    
        final int[] intArray;                  //final是引用类型
    
        static FinalReferenceExample obj;
    
        public FinalReferenceExample () {      //构造函数
            intArray = new int[1];             //1
            intArray[0] = 1;                   //2
        }
    
        public static void writerOne () {        //写线程A执行
            obj = new FinalReferenceExample ();  //3
        }
    
        public static void writerTwo () {        //写线程B执行
            obj.intArray[0] = 2;                 //4
        }
    
        public static void reader () {            //读线程C执行
            if (obj != null) {                    //5
                int temp1 = obj.intArray[0];      //6
            }
        }
    }
    

    这里 final 域为一个引用类型,它引用一个int型的数组对象。

    对于引用类型,写final域的重排序规则对编译器和处理器增加了如下约束:

    • 在构造函数内对一个 final 引用的对象的成员域的写入,与随后在构造函数外把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。

    对上面的示例程序,我们假设首先线程 A 执行 writerOne() 方法,执行完后线程 B 执行 writerTwo() 方法,
    执行完后线程 C 执行 reader() 方法。

    下面是一种可能的线程执行时序:

    时间线:-------------------------------------------------------------------------------------------->
    线程A:执行构造函数 1.写final引用 2.写final引用的对象成员域 StoreStore屏障 构造函数结束 3.构造函数的引用赋值给引用变量obj
    线程B:4.写final引用的对象的成员域
    线程C:5.读对象引用obj LoadLoad屏障 6.读final引用的对象成员域
    

    在上图中,1 是对 final 域的写入,2 是对这个 final 域引用的对象的成员域的写入,3 是把被构造的对象的引用赋值给某个引用变量。
    这里除了前面提到的 1 不能和 3 重排序外,2 和 3 也不能重排序。

    JMM 可以确保读线程 C 至少能看到写线程 A 在构造函数中对 final 引用对象的成员域的写入。
    即 C 至少能看到数组下标 0 的值为 1。
    而写线程 B 对数组元素的写入,读线程 C 可能看的到,也可能看不到。
    JMM 不保证线程 B 的写入对读线程 C 可见,因为写线程 B 和读线程 C 之间存在数据竞争,此时的执行结果不可预知。

    如果想要确保读线程 C 看到写线程 B 对数组元素的写入,写线程 B 和读线程 C 之间需要使用同步原语(lock 或 volatile)来确保内存可见性。

    为什么 final 引用不能从构造函数内“逸出”

    前面我们提到过,写 final 域的重排序规则可以确保:在引用变量为任意线程可见之前,该引用变量指向的对象的final域已经在构造函数中被正确初始化过了。
    其实要得到这个效果,还需要一个保证:

    在构造函数内部,不能让这个被构造对象的引用为其他线程可见,也就是对象引用不能在构造函数中“逸出”。

    为了说明问题,让我们来看下面示例代码:

    public class FinalReferenceEscapeExample {
    
        final int i;
    
        static FinalReferenceEscapeExample obj;
    
        public FinalReferenceEscapeExample () {
            i = 1;                              //1 写final域
            obj = this;                         //2 this引用在此“逸出”
        }
    
        public static void writer() {
            new FinalReferenceEscapeExample ();
        }
    
        public static void reader {
            if (obj != null) {                    // 3
                int temp = obj.i;                 // 4
            }
        }
    }
    

    假设一个线程 A 执行 writer() 方法,另一个线程 B 执行 reader() 方法。
    这里的操作 2 使得对象还未完成构造前就为线程 B 可见。
    即使这里的操作 2 是构造函数的最后一步,且即使在程序中操作 2 排在操作 1 后面,执行 read() 方法的线程仍然可能无法看到 final 域被初始化后的值,
    因为这里的操作 1 和操作 2 之间可能被重排序。

    实际的执行时序可能如下图所示:

    时间线:-------------------------------------------------------------------------------------------->
    线程A:执行构造函数 2.obj=this;被构造对象的引用在此处“溢出” 1.i=1;对final域初始化 构造函数结束
    线程B:3.if(obj!=null);读取不为null的引用a  4.temp=obj.i;这里将读取到final域初始化之前的值
    

    final 语义在处理器中的实现

    现在我们以 x86 处理器为例,说明 final 语义在处理器中的具体实现。

    上面我们提到,写 final 域的重排序规则会要求译编器在 final 域的写之后,构造函数 return 之前,插入一个 StoreStore 障屏。

    读 final 域的重排序规则要求编译器在读 final 域的操作前面插入一个 LoadLoad 屏障。

    由于 x86 处理器不会对写-写操作做重排序,所以在 x86 处理器中,写 final 域需要的 StoreStore 障屏会被省略掉。

    同样,由于 x86 处理器不会对存在间接依赖关系的操作做重排序,所以在 x86 处理器中,
    读 final 域需要的 LoadLoad 屏障也会被省略掉。

    也就是说在 x86 处理器中,final 域的读/写不会插入任何内存屏障!

    JSR-133 为什么要增强 final 的语义

    在旧的 Java 内存模型中 ,最严重的一个缺陷就是线程可能看到final域的值会改变。

    比如,一个线程当前看到一个整形final域的值为0(还未初始化之前的默认值),过一段时间之后这个线程再去读这个final域的值时,却发现值变为了1(被某个线程初始化之后的值)。
    最常见的例子就是在旧的Java内存模型中,String的值可能会改变。

    为了修补这个漏洞,JSR-133专家组增强了final的语义。通过为final域增加写和读重排序规则,可以为java程序员提供初始化安全保证:只要对象是正确构造的(被构造对象的引用在构造函数中没有“逸出”),那么不需要使用同步(指lock和volatile的使用),就可以保证任意线程都能看到这个final域在构造函数中被初始化之后的值。

    • String 的值可能会改变
    String s1 = "/usr/tmp";
    String s2 = s1.substring(4); 
    

    字符串s2的偏移量为4,长度为4。

    但是,在旧的模型下,另一个线程可以看到偏移量的默认值为0,然后再看到正确的值4,它将显示为字符串“/usr”变为“/tmp”。

    参考资料

    • final 基础

    http://toughcoder.net/blog/2016/11/12/understanding-java-keyword-final/

    https://juejin.im/entry/58c4811161ff4b005d94fed2

    https://www.cnblogs.com/dolphin0520/p/3736238.html

    http://www.importnew.com/7553.html

    https://blog.csdn.net/ch717828/article/details/46922777

    • jls

    jls-17.5

    final Classes

    final methods

    final Fields and static Constant Variables

    final Field Semantics

    final Variables

    • jmm

    http://www.infoq.com/cn/articles/java-memory-model-6

    展开全文
  • 前不久写了篇利用httpmodule来实现防sql的注入,其主要思想就是在页面访问只初就对Get和post两种方式提交的数据进行验证,今天发现老赵写的关键字过滤解决方案,思想基本一样,但写得比较深入,特此转载原文地址:...

        前不久写了篇利用httpmodule来实现防sql的注入,其主要思想就是在页面访问只初就对Get和post两种方式提交的数据进行验证,今天发现老赵写的关键字过滤解决方案,思想基本一样,但写得比较深入,特此转载

    原文地址:http://www.cnblogs.com/JeffreyZhao/archive/2008/12/24/filter-forbidden-word-solution-2.html

     

    问题远没结束

      上面的问题解决了没有?哦哦,我是指采取命名约定的方式来改变过滤行为。当然有问题,不过我这里提一下比较重要的两个:

      首先,就是“改名”这种行为——究竟是否方便?还记得我们的需求吗(提示一下:方便、通用……)?如果采取上面的命名约定方案,我们可能就需要在页面的前端和后端都不断地改名,一会儿加-noffw,一会儿加-json。如果项目只由您来负责这还好办,只是麻烦一些,但是如果您的团队中的前台开发人员性格古怪,固执己见,不愿配合怎么办(打架我喜欢,可惜不能直接解决问题)?再者,假如您除了一个FilterForbiddenWordModule之外还有类似的“FilterScriptInjectionModule”怎么办(别真写这么一个HttpModule,不合适,老赵只是想不出一个恰当的例子了)?如果两个Module都采取命名约定的方式,那么如何制定一个两者能同时认同的约定就也是个麻烦事。

      再者,命名真是我们可以控制的吗?某些情况下好说,但是假如您在使用WebForms中的控件怎么办?WebForm中的一个重要特性就是用过Naming Container来避免客户端ID的冲突。假设我们的页面是放在一个Master Page中ID为Main的ContentPlaceHolder中,那么ID为txtPassword的文本框在客户端里生成的HTML便会如下所示——那么我们又能有什么办法可以做到“命名约定”吗?

    <input name="ctl00$Main$txtPassword" id="ctl00_Main_txtPassword"></input>

      嘿,看来这种命名约定的方式有时候真不是那么通用啊。那么我就来设法解决WebForm这个问题。

      其实如果要解决WebForm这个问题,说白了就是要设法可以让服务器端明确指定一些字段的处理方式。这种“特殊”则意味着对于过滤方式的判断必须与特定的Page——泛化一下,HttpHandler进行绑定。这里我先谈一下我的第一个想法:使用Custom Attribute进行标记的方式。我们构造一个FilterForbiddenWordAttribute,其中包含一个抽象GetFilterType方法根据key来指定过滤方式:

    public enum FilterForbiddenWordType
    {
        Ignored,
        Normal,
        Json,
        Html
    }
    
    public abstract class FilterForbiddenWordAttribute : Attribute
    {
        public abstract FilterForbiddenWordType GetFilterType(string key);
    }
    

      我们如果有特别的需求,就可以通过定义一个FilterForbiddenWordHandlerAttribute的子类,重载GetFilterType方法,然后标记在HttpHandler上。如下:

    public class DefaultFilterForbiddenWordAttribute :
        FilterForbiddenWordAttribute
    {
        public override FilterForbiddenWordType GetFilterType(string key)
        {
            if (key.EndsWith("txtPassword"))
            {
                return FilterForbiddenWordType.Ignored;
            }
    
            return FilterForbiddenWordType.Normal;
        }
    }
    
    [DefaultFilterForbiddenWord]
    public partial class Default : System.Web.UI.Page
    {
        ...
    }
    

      当然,我们还需要对FilterForbiddenWordModule进行一些修改才能使之生效(朋友们可以先不要看代码,想想这次改变的关键在哪里?):

    public class FilterForbiddenWordModule : IHttpModule
    {
        ...
    
        void IHttpModule.Init(HttpApplication context)
        {
            context.PostMapRequestHandler += new EventHandler(OnPostMapRequestHandler);
        }
    
        private static void OnPostMapRequestHandler(object sender, EventArgs e)
        {
            var context = (sender as HttpApplication).Context;
            var handlerType = context.Handler.GetType();
            var filter = ((FilterForbiddenWordAttribute[])handlerType.GetCustomAttributes(
                typeof(FilterForbiddenWordAttribute), true)).FirstOrDefault(); 
    
            ProcessCollection(context.Request.QueryString, filter);
            ProcessCollection(context.Request.Form, filter);
        }
    
        private static void ProcessCollection(
            NameValueCollection collection,
            FilterForbiddenWordAttribute filter)
        {
            var copy = new NameValueCollection();
    
            foreach (string key in collection.AllKeys)
            {
                var filterType = (filter == null) ? FilterForbiddenWordType.Normal
                    : filter.GetFilterType(key);
    
                Array.ForEach(
                    collection.GetValues(key),
                    v => copy.Add(key, ForbiddenWord.Filter(v, filterType)));
            }
    
            ...
        }
    }

      修改示例。例如我们在页面上放置两个文本框txtPassword和txtNormal:

    <asp:TextBox ID="txtPassword" runat="server" TextMode="MultiLine" />
    <asp:TextBox ID="txtNormal" runat="server" TextMode="MultiLine" />
    <asp:Button ID="Button1" runat="server" Text="Click" />
    

      点击,效果不言而喻:

    2.jpg

      公布答案:因为我们需要等到确认了HttpHandler类型才能获得FilterForbiddenWordAttribute标记信息,所以这次更新的关键是我们必须推迟进行过滤的时机。推迟到哪个阶段?自然是能够确定HttpHandler类型的最早时机,PostMapRequestHandler。我们通过反射来获取Handler类型上的FilterForbiddenWordAttribute子类的信息,作为Filter传入带有额外参数的ProcessCollection方法中。ProcessCollection方法内部会调用根据filter参数来确定某个key的过滤方式:正常(当作纯文本进行过滤)、忽略(不过滤)、JSON(只过滤JSON内元素的值)以及HTML(忽视tag和attribute,并考虑文字内的HTML Encode)。其余不变。

      顺便说一句,以上代码其实只是为了写这些内容而在10分钟内写好的,不考虑性能、缓存、同步、边界等情况——因为我相信看了下面的文字您一定会抛弃这种做法。

    继续改进

      上面的做法(相对使用命名约定的方式)改进了什么地方?很简单,之前提到的命名约定的缺点就是上述做法的优点:

    1. 不同Page(Http Handler)可以自行指定字段所需要的过滤逻辑。
    2. 无需前端改名,只需后端标记。
    3. 避免复杂的命名约定,使多种横切型的过滤功能可以轻易共存。

      真是美妙地嗷嗷的,但是有没有朋友看出问题来?我提示一下:GetFilterType方法中使用了一个常量字符串txtPassword。

      估计有朋友会问:“咦,这有什么问题?”粗看似乎没有,不过老赵看到代码中出现常量总是要警惕一番(自觉是个好喜欢):为啥要是txtPassword而不是txtPassWord(一个常见的拼写错误)?为啥代码中用0而不用-1?这里的问题倒不是说一个常量在代码中到处使用时最好使用一个const——不不,是readonly字段来代替(为啥用const不太好?)。而是……再提示一下,如果某人将页面上的txtName文本框改为txtUserName那会出现何种情况?

      嗯嗯,那么Attribute中的GetFilterType方法当然还是在判断一个key是否由txtName结尾,而我们修改后的页面中Post内容中已经变成了txtUserName,咋整?但是可悲的是,我们尊敬的Attribute,就算你拿刀威胁它它也没法知道该替换什么啊。唉,那又有谁才能知道呢?不用多想,当然是页面本身了。

      .NET中Custom Attribute的特性深入人心,大大增强了.NET中反射机制的可用性,也因此Kent Beck认为NUnit的设计和使用较JUnit更为优雅。老赵的项目中也到处可见Custom Attribute的存在,写出的代码也简单优美强大地很。不过用多了Custom Attribute也造成了一种思维定势,一些“附加功能”往往都喜欢往上靠,很多问题往往一个功能出来三秒不到脑子里就浮出一个利用Custom Attribute的解决方案。古语有云,“世界如此美好,我却如此浮躁,这样不好,不好……”。事实上ASP.NET框架中已经有了不使用Custom Attribute进行“标记”的现成示例。例如,您知道IRequiresSessionState接口和INamingContainer接口的作用吗?

      如果您翻过IRequriedSessionState和INamingContainer接口的文档,就会发现它们有个共同的特点——没有任何成员。这意味着什么呢?这意味着实现了这样的接口的类,唯一的作用就是“别人知道你实现了这个接口”。有点拗口,对吧?其实就是指,这两个接口只起到了标记的作用。使用Custom Attribute或使用接口对一个类进行标记和扩展的优劣取舍,我打算用额外的一篇文章来讨论这个问题(要不现在大家来Brain Storm一下如何?)。目前,朋友们只需关心一点,如果不用Custom Attirubte而使用接口,我们该如何改写上面的程序。并且,这种改变带来了什么好处?

      如果在某些情况中,我们也可以把对象本身作为参数传入Custom Attribute的方法中,Attribute方法内部根据参数的属性来实现逻辑,可惜的是,Page类内部的控件成员是protected变量,无法从外部访问。对于我们来说,使Http Handler(即页面)直接实现某个接口的最大(唯一?)好处,就是让该接口的成员可以访问页面内部的非公开成员了。这点就是问题关键,我们现在不必直接使用txtPassword这个常量,而是能够访问页面中的txtPassword控件来获取它相关的属性(ID)。不再赘述,修改如下:

    public interface IForbiddenWordFilter
    {
        FilterForbiddenWordType GetFilterType(string key);
    }
    
    public partial class Default : System.Web.UI.Page, IForbiddenWordFilter
    {
        ...
    
        FilterForbiddenWordType IForbiddenWordFilter.GetFilterType(string key)
        {
            if (key.EndsWith(this.txtPassword.ID)) return FilterForbiddenWordType.Ignored;
            return FilterForbiddenWordType.Normal;
        }
    }
    

      至于HttpModule上的修改,相信不会难道您,老赵就不在这里说帖太多代码浪费带宽了。可以看出,现在的代码中已经没有了txtPassword这个常量,取而代之的是对txtPassword对象ID的访问。现在如果在aspx中修改了这个控件的ID,那么在aspx.cs中的变量也会被重构至对应名字,这大大提高了开发效率,降低了出错可能。

      差点忘说了一句,大家千万不要忘了对于WebForms模型,有几个特定的key是不能替换的例如“__VIEWSTATE”和“__VIEWSTATEENCRYPTED”。关于这点,老赵的作法是忽略所有以两条下划线作为开头的Key以保护WebForms模型内部需求。

     

      结合上一篇文章《一个较完整的关键字过滤解决方案(上)》,这似乎就是个较为完整的解决方案,不过这个话题结束了吗?当然没有。在下一篇文章《一个较完整的关键字过滤解决方案(下)》里,我们将讨论几个额外的话题,例如:

    • 这个解决方案的适用场合?不适用场合?
    • 输入过滤?输出过滤?
    • 我们一定要使用HttpModule进行过滤吗?
    • 性能?

      此外,我想大家在看了这篇文章后来一起思考一些问题,而我对于这些问题的看法也会在下一篇文章中谈到:

    • 在WebForms模型中,Page即是一个Handler,于是可以实现IForbiddenWordFilter。那么Page里Control所需要过滤的内容呢?动态加载的Control呢?
    • 这篇文章的示例里有个陷阱,您看的出是在哪里吗?
    展开全文
  • 在C++哪4与类型转换相关的关键字? n多书里面推荐要养成使用转型关键字的习惯,几年过去了,感觉还是不习惯,C风格的转型操作实在太强大太方便了。 1、const_cast 号称唯一具有常量性移除的转型操作符,这...

    一,在C++中,有哪4个与类型转换相关的关键字?

        好多书籍,推荐使用类型转换的关键字,但是c风格的类型转换操作,确实很方便,但是不易掌握。

    1、const_cast

    号称唯一具有常量性移除的转型操作符,这个说法实在很废话,不解释。平时几乎没有用过,遇到需要这个关键字的时候,都是直接修改了接口的类型,也不会去用这个关键字,一般来说老接口设计有问题啊。明明是const的,非得转成non-const实在别扭。

    2、dynamic_cast

    号称安全向下转型(safe downcasting),就是把一个父类型转成它的子类型,如果不是父子关系则会返回0,比如一种用法:

    assert(dynamic_cast<derived*>(pBase));

    曾经认为是唯一好用又常用的转型操作符,但在吃过亏后发现也要三思而后用,比较喜欢无脑,所以不再喜欢它了。

    不止一本书上说这个操作符有性能问题,但是它们没有给出具体的度量值,也不会告诉你性能分析软件没法将它的耗时与语句直接对应上,比如会把使用这个操作符的语句耗时显示在unknown分组中,太操蛋了。google的C++编码规范中也明确禁用此关键字,可惜我仍然还没反应过来,吃了大亏。

    总之,热点程序里面不要用。

    3、static_cast

    把编译器隐式执行的转型搞成显式的,特别是有告警的类型转换加上它就ok啦,比如double转int。偶尔用用,敲这么多字,还是C风格省心……

    4、reinterpret_cast

    对操作数的位模式做转化,比如把一个结构体转成char*。从来没用过,这名字实在陌生得紧,不看书真心想不起来。一般都会把源操作内存块转成void,然后使用的地方再找到想要的字段,转成想要的类型,工作中还没见过代码直接用的。

    二,

    定义一个空的类型,里面没有任何成员函数和成员变量。对这个类型求sizeof,得到的结果是1.

    因为当声明该类型的实例的时候,对象模型要求在内存当中占用一定的空间,否者无法使用这个实例,至于占用多大由编译器规定的对象模型决定。vs g++都占用1字节的空间,详细见深入理解C++对象模型。

    如果加入了构造函数和析构函数的话,这个类型的实例化变量的大小还是1,因为这两个函数的地址只与类型相关,而与类型的实例不相关,C++对象模型不会为这两个函数在实例化变量里面添加任何额外的信息。

    如果把析构函数改成虚拟函数的话,类里面有虚函数,就会为该类型生成虚函数表,并在该类型的每个实例化变量中添加一个指向虚函数表的指针,

    32位,求sizeof=4

    64位,求sizeof=8


    三,赋值运算符函数

    可以参考这个这个完整的实现了String

    String类的实现

    C++,拷贝构造函数的参数必须用引用,否者的话,如果你用了值传递,因为把形参复制到实参会调用拷贝构造函数,这样子就是在拷贝构造函数里面调用了拷贝构造函数,就会无休止的递归调用,从而导致栈溢出。

    因此C++不允许拷贝构造函数传值参数,而应该用A(const A & other);


    围绕构造函数,析构函数,运算符重载,

    四,赋值运算符函数

    根据CMyString类声明,请为该类型添加赋值运算符函数,

    class CMyString
    {
    public:
        CMyString(char* pData = NULL);
        CMyString(const CMyString& str);
        ~CMyString(void);
          
    private:
        char* m_pData;
    };

    注意点:

    1.把返回值的类型声明为该类型的引用,并且返回自身的引用*this,这样子才可以做连续赋值。

    2.把传入的类型声明为常量的引用。如果是值传递的话会调用拷贝构造函数,这样子可以避免无所谓的消耗。同时我们在赋值运算符函数中不会改变传入的实例的状态,因此应该用const修饰。

    3.分配新的内存之前需要释放实例自身已有的内存。否者程序将出现内存泄漏。

    4.判断传入的参数和当前实例(*this)是不是同一个实例,如果是同一个不必进程赋值操作,如果没有判断,释放了自身的内存的时候会导致严重的问题。


    写代码的过程中发现了一个问题 NULL在那个头文件中的问题:

    NULL定义在stddef.h中,isstream与vector通过包含cstdef包含了stddef.h

    定义如下
    #undef NULL 
    #if defined(__cplusplus)
    #define NULL 0
    #else
    #define NULL ((void *)0)
    #endif
    可以看出c++中 NULL为(int)0 , C中NULL为(void*)0


    MyString.h

    #ifndef _MyString_H_
    #define _MyString_H_
    
    #include <iostream>
    
    /*MyString对外的接口*/
    class MyString
    {
    public:
        MyString(char* pData = NULL);
        MyString(const MyString& str);
        ~MyString(void);
    
        MyString& operator = (const MyString& str);
    
        void Print();
          
    private:
        char* m_pData;
    };
    
    #endif /*_MyString_H_*/

    MyString.cpp

    #include "MyString.h"
    #include <iostream>
    #include <cstring>
    
    using namespace std;
    
    /*构造函数*/
    MyString::MyString(char *pData)
    {
        if(pData == NULL)
        {
            m_pData = new char[1];
            m_pData[0] = '\0';
        }
        else
        {
            int length = strlen(pData);
            m_pData = new char[length + 1];
            strcpy(m_pData, pData);
        }
    }
    
    /*拷贝构造函数*/
    MyString::MyString(const MyString &str)
    {
        int length = strlen(str.m_pData);
        m_pData = new char[length + 1];
        strcpy(m_pData, str.m_pData);
    }
    
    /*析构函数*/
    MyString::~MyString()
    {
        delete[] m_pData;
    }
    
    /*等号运算符的重载*/
    /*MyString& MyString::operator = (const MyString& str)
    {
        if(this == &str)
            return *this;
    
        delete []m_pData;
        m_pData = NULL;
    
        m_pData = new char[strlen(str.m_pData) + 1];
        strcpy(m_pData, str.m_pData);
    
        return *this;
    }*/
    
    MyString& MyString::operator = (const MyString& str)
    {
        if(this != &str)
        {
            /*调用拷贝构造函数*/
            MyString temp(str);
    
            char * pTemp = temp.m_pData;
            temp.m_pData = m_pData;
            m_pData = pTemp;
        }
        return *this;
    }
    
    /*打印函数*/
    void MyString::Print()
    {
        cout<<m_pData;
    }

    程序运行到if的外面就除了temp这个变量的作用域,就会自动调用temp的析构函数,把temp.m_pData所指向的内存释放掉。这个指针指向的内存就是实例之前m_pData的内存。



    main.cpp

    #include "MyString.h"
    
    #include <iostream>
    #include <string.h>
    
    using namespace std;
    
    /*赋值操作*/
    void Test1()
    {
        cout<<"Test1 begins:"<<endl;
    
        /*注意这个指针指向常量区*/
        /* 警告: 不建议使用从字符串常量到‘char*’的转换---这里会有警告*/
        char* text = "Hello world";
        MyString str1(text);
        MyString str2;
        str2 = str1;
    
        cout<<"The expected result is: "<<text<<endl;
        cout<<"The actual result is: ";
        str2.Print();
        cout<<endl;
    }
    
    // 赋值给自己
    void Test2()
    {
        cout<<"Test2 begins:"<<endl;
    
        /*注意这个指针指向常量区*/
        char* text = "coding my life";
        MyString str1(text);
        str1 = str1;
    
        cout<<"The expected result is: "<<text<<endl;
        cout<<"The actual result is: ";
        str1.Print();
        cout<<endl;
    }
    
    // 连续赋值
    void Test3()
    {
        cout<<"Test3 begins:"<<endl;
    
        char* text = "Hello world boy!";
    
        MyString str1(text);
        MyString str2, str3;
        str3 = str2 = str1;
    
        cout<<"The expected result is: "<<text<<endl;
        cout<<"The actual result is: ";
        str2.Print();
        cout<<endl;
    
        cout<<"The expected result is: "<<text<<endl;
        cout<<"The actual result is: ";
        str3.Print();
        cout<<endl;
    }
    
    int main(int argc, char* argv[])
    {
        Test1();
        Test2();
        Test3();
    
        return 0;
    }
    
    

    Makefile

    .PHONY:clean
    CPP=g++
    CFLAGS=-Wall -g
    BIN=test
    OBJS=main.o MyString.o
    LIBS=
    $(BIN):$(OBJS)
    	$(CPP) $(CFLAGS) $^ -o $@ $(LIBS)
    %.o:%.c
    	$(CPP) $(CFLAGS) -c $< -o $@
    clean:
    	rm -f *.o $(BIN)
    

    测试环境是Ubuntu 12.04.2 LTS



    展开全文
  • volatile关键字和synchronize关键字

    千次阅读 2018-02-01 18:52:46
    在java多线程估计会经常见到volatile和synchronize这两个关键字...它可以去修饰方法或者代码块,可以一个对象去做锁或者用类class做锁,也就是对象锁和类锁,最好不要使用常量做锁的关键字例如字符,这样很可能会
  • Swift关键字总结下篇

    千次阅读 2017-12-29 10:35:01
    在Swift官方文档的词汇结构, 非常多的关键字, 它们被用于声明、语句、表达式、类、模式, 还有以数字符号#开头的关键字, 以及特定上下文环境使用的关键字。本文涉及的代码可以在这里下载代码资源。 ...
  • 前不久写了篇利用httpmodule来实现防sql的注入,其主要思想就是在页面访问只初就对Get和post两种方式提交的数据进行验证,今天发现老赵写的关键字过滤解决方案,思想基本一样,但写得比较深入,特此转载原文地址:...
  • C++ 的static关键字

    千次阅读 多人点赞 2014-10-11 11:15:09
    在讲解肯定不恰当之处,请大家大胆地扔砖,不要手软,文中的内容引用了不少网上的资料。  static从宏观上讲主要两种用法,、是面向过程设计;二是面向对象设计。前主要是涉及变量与函数的用法;后者呢包含...
  • JSONModel-服务器返回的数据中有id等关键字 ...我第次用JSONModel这第三方框架处理数据,返回的数据中有id关键字、description与NSObject自带的description方法冲突。 A010C11F-1FE9-4D3E-A6E5-75D75E33FE4A.png
  • synchronized 关键字详解

    千次阅读 热门讨论 2021-04-07 14:08:27
    synchronized 关键字1. synchronized 关键字的三种使用方法1.1. 修饰实例方法2.2. 修饰静态方法3.3. 修饰代码块2. 双重检验锁方式实现单例模式3....synchronized 关键字解决的是多线程之间访问资源的同
  • ABAP里的IS BOUND, IS NOT INITIAL和IS ASSIGNED这组关键字,如果平时不留心,很容易理解地似是而非。今天我们就来说说它们的区别。 先SAP帮助文档抄过来: IS BOUND It checks whether a reference variable ...
  • matlab关键字

    千次阅读 2015-02-07 10:02:44
    a a abs 绝对值, 模 acos 反余弦 acosh 反双曲余弦 acot 反余切 acoth 反双曲余切 acsc 反余割 acsch 反双曲余割 ...any 非零元则为真 area 面域图 asec 反正割 asech 反双曲正割 asin 反正弦 asin
  • 主题:不确定性算法在C++的表达:一个简化模型作者:wang tianxing摘要:本文建立了一个简单化的不确定性计算模型,并用 C++ 语言实现了一个支持以这种模型进行计算的库,并以计算 24 的程序展示了这个模型...
  • iOS开发各种关键字的区别

    千次阅读 2017-10-19 09:37:10
    1.浅Copy:指针的复制,只是多了一个指向这块内存的指针,共用一块内存。 深Copy:内存的复制,两块内存是完全不同的, 也就是两个对象指针分别指向不同的内存,互不干涉。 2.atomic是Objc使用的一种线程保护...
  • C语言和C++关键字总结——篇就够了

    千次阅读 多人点赞 2019-05-27 23:09:25
    C语言和C++关键字总结 C语言和C 关键字总结 、auto * 1、C语言 * 2、C 二、struct 三、static * 1、程序的内存分配 * 2、局部静态变量 * 3、全局静态变量 * 4、静态函数 * 5、类的静态成员变量 * 6、类的静态...
  • 不得不说,这深度学习框架更新太快了尤其到了Keras2.0版本,快到Keras中文版好多都是错的,快到官方文档也旧的没更新,... 笔者先学的caffe,从使用来看,比caffe简单超级多,非常好用,特别是重新训练一个模型,但是
  • 阿里面试失败后,气之下我图解了Java18

    万次阅读 多人点赞 2021-06-17 23:21:47
    目录 乐观锁和悲观锁 独占锁和共享锁 互斥锁和读写锁 公平锁和非公平锁 ...悲观锁对应于生活悲观的人,悲观的人总是想着...回到代码世界一个共享数据加了悲观锁,那线程每次想操作这个数据前都会假设其他线程.
  • synchronized关键字实现同步

    千次阅读 2018-04-16 00:15:43
    Java语言提供了synchronized关键字可以给方法或代码块进行加锁,从而实现同步。 synchronized关键字取的锁都是对象锁,而不是代码块或方法当做锁。主要以下几种场景: 同步化类的非静态方法,取的调用该方法...
  • Java内存管理-探索Java字符String(十二)

    千次阅读 多人点赞 2019-05-04 23:18:39
    一个积极的人 编码、改bug、提升自己 我有一个乐园,面向编程,春暖花开! 文章目录一、初识String类二、字符的不可变性三、字符常量池和 intern 方法四、面试题1、 String s1 = new String("hello");这句话...
  • 分词关键字提取-jieba

    千次阅读 2017-10-27 12:37:30
    全模式,句子所有的可以成词的词语都扫描出来, 速度非常快,但是不能解决歧义; 搜索引擎模式,在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词。主要功能分词 jieba.cut 方法接受三...
  • iOS开发 关键字的区别

    千次阅读 2017-08-13 09:00:20
    什么情况使用 weak 关键字,相比 assign 什么不同? 什么情况使用 weak 关键字? 在 ARC ,在可能出现循环引用的时候,往往要通过让其中一端使用 weak 来解决,比如: delegate 代理属性 ...
  • 数据类型及关键字

    千次阅读 2018-05-08 12:59:47
    一、表: 表是DB用来存储数据的结构模型, 表是一个二维结构。 行:也叫记录(record) 列:也叫字段(Field)二、SQL:结构化查询语言,用途就是通过DBMS来操作DB的。 里面含有 (1)DDL语言:数据定义语言 ...
  • @property属性关键字详解

    千次阅读 2017-05-16 18:38:56
    @property的常用属性关键字有nonatomic、atomic、readonly、writeonly、readwrite、assign,copy、strong、weak、看着挺多的,但是经常用的也就几notomic: 默认关键字,也就是说如果什么都不写,默认就是这。...
  • Bigtable:一个分布式的结构化数据存储系统 本文的英文原文为Google在2006年发布的Google Bigtable paper 本文的翻译版本由Alex完成,原文地址为: http://blademaster.ixiezi.com/ 这是我很长时间以来...
  • c51 reentrant 关键字详解

    千次阅读 2019-08-20 10:00:07
    51 为端口映射 但亦可以扩展为内存映射 那样就是混合映射 大部分的非51 cpu为内存映射 端口映射的CPU需要对c语言进行扩展,扩展端口映射语句,sfr Register = addr; 内存映射的CPU直接用标准c就可。 与硬件相关时...
  • 解析OBJ模型并将其加载到Unity3D场景

    万次阅读 热门讨论 2015-11-23 10:19:54
    今天想和大家交流的是解析obj模型并将其加载到Unity3D场景,虽然我们知道Unity3D是可以直接导入OBJ模型的,可是时候我们并不能保证我们目标客户知道如何使用Unity3D的这套制作流程,可能对方最终提供给我们的...
  • c++关键字---大集合

    2020-01-31 11:25:56
    本编文章主要是将作者在学习c++遇到的关键字进行了总结,作者对比较陌生的关键字进行了解释 alignas 设置类和struct的字节对齐方式 默认取值为:0 1 2 4 6 8 alignof 区分sizeof(),alignof得到字节对齐的字节数 ...
  • Oracle 基础总结:关键字总结

    千次阅读 2019-04-07 22:10:18
    Oracle 基础总结:关键字总结、SQL基础查询语句1、SELECT二、过滤和排序数据2、DISTINCT3、WHERE4、BETWEEN…AND…5、IN6、like:使用like运算选择类似的值,选择条件可以包含字符或数字。7、ESCAPE:转义符,回避...
  • java过滤关键字(DFA算法)

    千次阅读 2018-12-25 09:38:10
    转:https://my.oschina.net/magicalSam/blog/1528428 https://my.oschina.net/magicalSam/blog/1528524 项目中有使用过滤关键字的地方,在此自己记录一下. 无需其他java包,main方法直接执行,项目具体使用的话,一般...
  • java简单类+this关键字用法

    千次阅读 2019-01-04 14:06:01
    今天开始学习java,第次自学其他语言,真是有些小兴奋呢,这些与C++不同的特性或是不熟悉的特性都希望能将其记录下来,日后便于查阅复习,顺便见证下自己的成长轨迹,嘿嘿嘿。。。 今日问题 搞定 java简单类是...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 102,504
精华内容 41,001
关键字:

有一个模型可以把一个串中关键字