X32专项练习部分11

StringBuffer扩容规则

		public static void main(String[] args) {
        StringBuffer s1 = new StringBuffer(10);
        s1.append("1234");
        System.out.println(s1.length()); // 4
        System.out.println(s1.capacity()); // 10
        /*
        length 返回当前长度
        如果字符串长度没有初始化长度大,capacity返回初始化的长度
        如果append后的字符串长度超过初始化长度,capacity返回增长后的长度

        StringBuffer和StringBuilder的默认大小为16
        ArrayList和LinkedList的默认大小10
         */
        StringBuffer sb = new StringBuffer("abcd");
        StringBuffer buffer1 = new StringBuffer(10);
        StringBuffer buffer2 = new StringBuffer(3);
        StringBuffer buffer3 = new StringBuffer(1);
        buffer1.append("abcd");
        buffer2.append("abcd"); // 扩容的长度为原来的2倍+2
        buffer3.append("abcd"); // 2*1<4  扩容长度为4
        System.out.println(buffer1.length() + " && " + buffer2.length() + " && " + buffer3.length() + " && " + sb.length());
        System.out.println(buffer1.capacity() + " && " + buffer2.capacity() + " && " + buffer3.capacity() + " && " + sb.capacity());
        /*
        输出结果:
        4 && 4 && 4 && 4
        10 && 8 && 4 && 20

        这是扩容规则
        if(str.length>2*x+2) capacity = str.length;
        else capacity = 2*x+2;
         */
    }

Math方法返回值类型

		public static void main(String[] args) {
        double d = Math.floor(-8.5);
        System.out.println(d); // (double)-9.0

        double d1 = Math.ceil(-1.5);
        System.out.println(d1); // (double)-1.0

        long l = Math.round(-2.6);
        System.out.println(l); // (long)-3

        // String s;
        // System.out.println("s=" + s); // 不能通过编译,必须初始化
	    }

临时修改引用

/*
参数修改问题
    关于Java中参数传递的说法,哪个是错误的
    正确答案: D
    在方法中,修改一个基础类型的参数不会影响原始参数值
    在方法中,改变一个对象参数的引用不会影响到原始引用
    在方法中,修改一个对象的属性会影响原始对象参数
    在方法中,修改集合和Maps的元素不会影响原始集合参数
 */
class Test2 {
    /*
    A 选项
     */
    public static void main(String []args){
        int i = 5;
        func(i);
        System.out.println(i);
    }
    static void func(int j){
        j = 10;
    }

    /*
    输出结果 5
    所以不会影响原始参数值
     */

    /*
    B选项
    在方法中改变了对象的引用,指的是仅仅改变引用而已,对象还是那个对象
    就相当于你银行卡丢了,你重新补办换了一张卡,变的是银行卡,而你卡里的钱一分都没少

    public static void main(String []args){
        User rabbiter = new User();
        rabbiter.setName("rabbiter");
        func(rabbiter);
        System.out.println(rabbiter.getName());
    }
    static void func(User user){
        user = new User();
        user.setName("zhangsan");
    }

    输出结果 rabbiter
     */

    /*
    C选项
    public static void main(String []args){
        User rabbiter = new User();
        rabbiter.setName("rabbiter");
        func(rabbiter);
        System.out.println(rabbiter.getName());
    }
    static void func(User user){
        user.setName("zhangsan");
    }

    输出结果 zhangsan
     */

    /*
    D选项
    Map和集合也是引用
    C和D选项与B不同
    临时修改了引用
    不会改变原有引用指向的值
     */
}

类的初始化过程(复杂)

class Test1 {
    public static void main(String[] args) {
        System.out.println(new B().getValue());
        /*
        main方法执行后
        创建B类实例
        掉用B的无参构造

        B的无参构造显式调用了A类构造函数
        但由于是B类实例
        所以实际上调用的是B类重写的setValue方法
        但B实例的setValue调用的是父类的setValue方法
        把B类成员变量value设置为2*5 = 10

        --- 这里为了提高可读性分割一下 ---   --- 这里为了提高可读性分割一下 ---
        然后B类构造函数中super(5);
        执行完毕
        继续执行下面的setValue(getValue() - 3);
        语句

        先执行getValue方法
        B类中没有重写getValue方法
        因此调用父类A的getValue方法

        value++执行后
        B的成员变量value值为11
        此时开始执行到return语句
        把11这个值作为getValue方法的返回值返回出去

        但是由于getValue块被try finally块包围
        因此finally中的语句无论如何都将被执行
        所以步骤2中11这个返回值会先暂存起来
        到finally语句块执行完毕后
        再真正返回出去

        --- 这里为了提高可读性分割一下 ---   --- 这里为了提高可读性分割一下 ---
        finally语句块中 
        this.setValue(value)方法调用的是B类的setValue方法
        因为此刻B类构造函数还未执行完毕
        正在初始化的是B类的一个对象,运行时多态
        就像最开始第一步提到的一样
        而且这里用了使用了this关键词显式指明了调用当前对象的方法
        而当前对象就是B类正在初始换的对象

        因此,此处会再次调用B类的setValue方法
        同上,super关键词显式调用A的setValue方法
        把B的value值设置成为了2 * 11 = 22
        因此第1个打印项为22

        此时finally语句执行完毕
        会把刚刚暂存起来的11返回出去
        也就是说这么经历了这么一长串的处理
        getValue方法最终的返回值是11

        回到前面B类构造函数的代码语句
        其最终结果为setValue(11-3)=>setValue(8)
        会是B的setValue方法
        B的value值再次变成了2*8 = 16

        --- 这里为了提高可读性分割一下 ---   --- 这里为了提高可读性分割一下 ---
        到了这里
        B类已经初始化完成
        调用getValue方法之前
        B的成员变量value值为16
        B类没有getValue方法
        只能调用A类的getValue方法

        value++ 执行后, B的成员变量value值为17
        此时执行到return语句,会将17这个值作为getValue方法的返回值返回出去
        但是由于getValue块被try finally块包围而finally中的语句无论如何都一定会被执行
        所以步骤2中17这个返回值会先暂存起来,到finally语句块执行完毕后再真正返回出去。
        finally语句块中继续和上面说的一样

        this.setValue(value)方法调用的是B类的setValue()方法将B的value值设置成为了2 * 17 = 34。
        因此第二个打印项为34
        finally语句执行完毕 会把刚刚暂存起来的17返回出去。
        因此new B().getValue()最终的返回值是17

        到这里
        运行main方法的打印方法
        将刚刚返回的值打印出来,也就是第三个打印项:17
         */
    }

    static class A {
        protected int value;

        public A(int v) {
            setValue(v);
        }

        public void setValue(int value) {
            this.value = value;
        }

        public int getValue() {
            try {
                value++;
                return value;
            } finally {
                this.setValue(value);
                System.out.println(value);
            }
        }
    }

    static class B extends A {
        public B() {
            super(5);
            setValue(getValue() - 3);
        }

        public void setValue(int value) {
            super.setValue(2 * value);
        }
    }
}

Java异常易混点

/*
以下关于JAVA语言异常处理描述正确的有?
正确答案: C D
throw关键字可以在方法上声明该方法要抛出的异常。
throws用于抛出异常对象。
try是用于检测被包住的语句块是否出现异常,如果有异常,则捕获异常,并执行catch语句。
finally语句块是不管有没有出现异常都要执行的内容。
在try块中不可以抛出异常

throw用于抛出异常。
throws关键字可以在方法上声明该方法要抛出的异常,然后在方法内部通过throw抛出异常对象。
try是用于检测被包住的语句块是否出现异常,如果有异常,则抛出异常,并执行catch语句。
cacth用于捕获从try中抛出的异常并作出处理。
finally语句块是不管有没有出现异常都要执行的内容

补充异常知识
	异常分为两种,一种为运行异常RuntimeException
    另一种为检查异常CheckedException
    对于运行异常,编译器没有强制对其进行捕获
    会把异常一直往上层抛出,直到遇到处理代码为止
    常见的运行异常有:NullPointerException(空指针异常),IndexOutOfBoundException(数组下标越界越界异常)
    检查异常,所有继承自Exception并且不是运行异常的都是检查异常,在程序中需要用try catch进行捕获
    常见的有IO异常和SQL异常
 */

instanceof方法

class Test4 {
    public static void main(String[] args) {
        // 向上转型 父类引用指向子类对象
        A obj = new D();

        System.out.println(obj instanceof B); // true

        System.out.println(obj instanceof C); // false

        System.out.println(obj instanceof D); // true

        System.out.println(obj instanceof A); // true
    }
    /*
    instanceof是判断前者是否可以类型可以转化为后者
    可以转化即为true
    分为向上转型和向下转型BD都是A的子类向下转型
    D属于B,D属于A,D属于D,D不属于C
    所以instanceof是一条直线链判断是否在链上
     */
}

构造方法内部调用构造方法

/*
在JAVA中,假设A有构造方法A(int a)
则在类A的其他构造方法中调用该构造方法和语句格式应该为()

this.A(x)
this(x)
super(x)
A(x)

正确答案:B
 */
class MyClass {
    /*
    构造方法可以重载
    这个没什么大的原理,this()和super()在构造器中就是这么用的
    一个构造器的中默认的第一行就是super()

    这就是为什么一旦一个类的直接父类没有无参构造的情况下
    子类构造器必须显式调用父类或者子类中别的构造器

    构造方法里面调用其它构造方法
    格式为this(param)
     */
    public MyClass(){}

    public MyClass(int i){
        this(1.2);
    }
    public MyClass(double l){}
}

修饰符列表

/*
要使某个类能被同一个包中的其他类访问
但不能被这个包以外的类访问,可以

正确答案: A
让该类不使用任何关键字
使用private关键字
使用protected关键字
使用void关键字

default指代的就是缺省修饰符
子类的话也有可能在外面的包里
外部包 > 子类 > 本包 > 该类内部
public > protected > default > private
 */

文本文件和二进制文件

/*
对于文件的描述正确的是( )

正确答案: D
文本文件是以“.txt”为后缀名的文件,其他后缀名的文件是二进制文件。
File类是Java中对文件进行读写操作的基本类。
无论文本文件还是二进制文件,读到文件末尾都会抛出EOFException异常。
Java中对于文本文件和二进制文件,都可以当作二进制文件进行操作。

A.  文件分为文本文件和二进制文件,计算机只认识二进制,所以实际上都是二进制的不同解释方式
    文本文件是以不同编码格式显示的字符,例如ASCII、Unicode等
    window中文本文件的后缀名有".txt",".log",各种编程语言的源码文件等

    二进制文件就是用文本文档打开是看不懂乱码,
    下一句话很重要
    只要能用文本打开的文件都可以算是文本文件
    只是显示的结果不是你想要的
    二进制文件只有用特殊的应用才能读懂的文件,例如".png",".bmp"等
    计算机中大部分的文件还是二进制文件。

B.  File类是对文件整体或者文件属性操作的类
    例如创建文件、删除文件、查看文件是否存在等功能,不能操作文件内容;文件内容是用IO流操作的。

C.  当输入过程中意外到达文件或流的末尾时,抛出EOFException异常
    正常情况下读取到文件末尾时,返回一个特殊值表示文件读取完成,例如read()返回-1表示文件读取完成。

D.  上面A选项已经说了,不论是文本文件还是二进制文件
    在计算机中都是以二进制形式存储的,所以都当做二进制文件读取
 */

编译器优化与堆栈数据共享(复杂)

// 下列程序的输出结果是
class StringDemo{
    private static final String MESSAGE="taobao";
    public static void main(String [] args) {
        String a ="tao"+"bao";
        String b="tao";
        String c="bao";
        System.out.println(a==MESSAGE); // true
        System.out.println((b+c)==MESSAGE); // false
    }
    /*
    补充:栈内存数据共享
    栈内存的一个特点是数据共享,这样设计是为了减小内存消耗
    前面定义了i=1,i和1都在栈内存内,如果再定义一个j=1,此时将j放入栈内存,然后查找栈内存中是否有1
    如果有则j指向1。如果再给j赋值2,则在栈内存中查找是否有2,如果没有就在栈内存中放一个2,然后j指向2
    也就是如果常量在栈内存中,就将变量指向该常量
    如果没有就在该栈内存增加一个该常量,并将变量指向该常量

    如果j++,这时指向的变量并不会改变,而是在栈内寻找新的常量(比原来的常量大1)
    如果栈内存有则指向它,如果没有就在栈内存中加入此常量并将j指向它
    这种基本类型之间比较大小和我们逻辑上判断大小是一致的
    如定义i和j是都赋值1,则i==j结果为true
    ==用于判断两个变量指向的地址是否一样
    i==j就是判断i指向的1和j指向的1是同一个吗,当然是了

    对于直接赋值的字符串常量
    (如String s="Hello World";中的Hello World)
    也是存放在栈内存中,而new出来的字符串对象(即String对象)是存放在堆内存中
    如果定义String s=“Hello World”和String w=“Hello World”,s==w吗
    肯定是true,因为他们指向的是同一个Hello World

    --- 这里为了提高可读性分割一下 --- --- 这里为了提高可读性分割一下 ---
    堆内存没有数据共享的特点
    前面定义的String s = new String("Hello World");
    变量s在栈内存内,Hello World这个String对象在堆内存内
    如果定义String w = new String("Hello World");
    则会在堆内存创建一个新的String对象,变量w存放在栈内存,w指向这个新的String对象
    堆内存中不同对象(指同一类型的不同对象)的比较如果用==则结果肯定都是false
    比如s==w 当然不等,s和w指向堆内存中不同的String对象
    如果判断两个String对象相等呢,用equals方法

    --- 这里为了提高可读性分割一下 --- --- 这里为了提高可读性分割一下 ---
    String a =  "tao" + "bao" ;和String a =  "taobao";
    编译出的字节码是一样的
    所以等到运行时,根据上面说的栈内存是数据共享原则
    a和MESSAGE指向的是同一个字符串
    对于后面的(b+c)又是什么情况呢
    b+c只能等到运行时才能判定是什么字符串,编译器不会优化,想想这也是有道理的
    编译器怕你对b的值改变,所以编译器不会优化

    运行时b+c计算出来的"taobao"和栈内存里已经有的"taobao"是一个吗
    不是
    b+c计算出来的"taobao"应该是放在堆内存中的String对象
    这可以通过System.out.println((b+c)== MESSAGE);的结果为false来证明这一点
    如果计算出来的b+c也是在栈内存,那结果应该是true

    Java对String的相加是通过StringBuffer实现的
    先构造一个StringBuffer里面存放”tao”,然后调用append()方法追加”bao”
    然后将值为”taobao”的StringBuffer转化成String对象
    StringBuffer对象在堆内存中,那转换成的String对象理所应当的也是在堆内存中

    --- 这里为了提高可读性分割一下 --- --- 这里为了提高可读性分割一下 ---
    这里改造一下
    下面改造一下这个语句
    System.out.println((b+c).intern()== MESSAGE);
    结果是true
    是因为intern方法把原本在堆内存中的字符串引用
    放到了常量池

    再改造一下
    再把变量b和c的定义改一下,
    final String b = "tao";
    final String c = "bao";
    System.out.println((b+c)== MESSAGE);
    现在b和c不可能再次赋值了,所以编译器将b+c编译成了”taobao”
    因此,这时的结果是true

    在字符串相加中,只要有一个是非final类型的变量,编译器就不会优化
    因为这样的变量可能发生改变,所以编译器不可能将这样的变量替换成常量
    例如将变量b的final去掉,结果又变成了false
    这也就意味着会用到StringBuffer对象,计算的结果在堆内存中
     */

对象地址改变

class foo {
    public static void main(String sgf[]) {

        StringBuffer a=new StringBuffer("A");

        StringBuffer b=new StringBuffer("B");

        operate(a,b);

        System.out.println(a+"."+b); // AB.B
    }
    final static void operate(StringBuffer x,StringBuffer y) {
        x.append(y);
        y=x; // 这一步有个坑,y和b都是指针,这里只改了y的引用,对b毫无影响
    }
}
/*
引用a指向对象A 引用b指向对象B 引用x指向对象A 引用y指向对象B
在operate方法中
引用x指向的对象A被连接了B,对象A也就被改变为AB
然后又把引用y指向了x所指向的对象地址
也就是此时引用a,x,y指向同一个对象AB

x进行对象操作,所指向对象地址没变,对象内容变成了AB
而y进行赋值,使指向的对象地址变了,变成了现在x指向的地址
而其原来指向地址的对象还是那样
即b所指向的对象B并没有变
如果加入b = a;
则程序输出AB.AB
 */

总目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

muskfans

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值