java提高

1、在java中使用Comparable实现排序,类实现这个接口,重写equal和compareTo方法

int index1 = list.indexOf(student);
int index2 = Collections.binarySearch(list, student);
 按理说两者index是一致的,因为他们检索的是同一个对象,但是结果:
 index1=0
 index2=1
 原因:indexOf和binarySearch的实现机制不同,前者是基于equal实现的,后者是基于compareTo实现
 结论:compareTo是判断元素在排序中位置是否相等,equal是判断元素是否相等。

2、java的四舍五入

目前java支持7中舍入法:
    1、 ROUND_UP:远离零方向舍入。向绝对值最大的方向舍入,只要舍弃位非0即进位
    2、 ROUND_DOWN:趋向零方向舍入。向绝对值最小的方向输入,所有的位都要舍弃,不存在进位情况。
    3、 ROUND_CEILING:向正无穷方向舍入。向正最大方向靠拢。若是正数,舍入行为类似于ROUND_UP,若为负数,舍入行为类似于ROUND_DOWN。Math.round()方法就是使用的此模式。
     4、 ROUND_FLOOR:向负无穷方向舍入。向负无穷方向靠拢。若是正数,舍入行为类似于ROUND_DOWN;若为负数,舍入行为类似于ROUND_UP。
     5、 HALF_UP:最近数字舍入(5进)。这是我们最经典的四舍五入。
     6、 HALF_DOWN:最近数字舍入(5舍)。在这里5是要舍弃的。
     7、 HAIL_EVEN:银行家舍入法。

3、使用序列化实现对象的拷贝

1、java中存在Cloneable,实现该接口的类都会具有被拷贝的能力,拷贝过程是发生在内存中的,在性能方面比我们直接通过new对象要来的快。拷贝还分为深拷贝和浅拷贝,浅拷贝目前存在着对对象属性拷贝不彻底的问题。
Email email = new Email("请参加会议","请与今天12:30到二会议室参加会议..."); 
Person person1 =  new Person("张三",email);
Person person2 =  person1.clone();
person2.setName("李四");
Person person3 =  person1.clone();
person3.setName("王五");

System.out.println(person1.getName() + "的邮件内容是:" + person1.getEmail().getContent());
System.out.println(person2.getName() + "的邮件内容是:" + person2.getEmail().getContent());
System.out.println(person3.getName() + "的邮件内容是:" + person3.getEmail().getContent());

--------------------
Output:
张三的邮件内容是:请与今天12:30到二会议室参加会议...
李四的邮件内容是:请与今天12:30到二会议室参加会议...
王五的邮件内容是:请与今天12:30到二会议室参加会议...

修改张三的内容。person1.getEmail().setContent("请与今天12:00到二会议室参加会议...");
--------------------
Output:
张三的邮件内容是:请与今天12:00到二会议室参加会议...
李四的邮件内容是:请与今天12:00到二会议室参加会议...
王五的邮件内容是:请与今天12:00到二会议室参加会议...

问题:李四和王五的时间也发生了改变?
原因:clone()方法是使用Object类的clone()方法,该方法的缺陷是他不会将对象的所有属性全部拷贝过来,而是有选择行的拷贝,基本规则如下:
   1)基本类型
   如果变量是基本类型,则拷贝其值,int float等
   2)对象
   如果变量是一个实例对象,则拷贝其地址引用
   3)String字符串
   若变量是String,则拷贝其地址引用。但是在修改时,它会从字符串池中重新生成一个新的字符,原有字符串对象保持不变。
解决:基于上面上面的规则,我们很容易发现问题的所在,他们三者公用一个对象,张三修改了该邮件内容,则李四和王五也会修改,所以才会出现上面的情况。对于这种情况我们还是可以解决的,只需要在clone()方法里面新建一个对象,然后张三引用该对象即可:
protected Person clone() {
        Person person = null;
        try {
            person = (Person) super.clone();
            person.setEmail(new Email(person.getEmail().getObject(),person.getEmail().getContent()));
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return person;
    }
2、利用序列化实现对象的拷贝
如何利用序列化来完成对象的拷贝呢?在内存中通过字节流的拷贝是比较容易实现的。把母对象写入到一个字节流中,再从字节流中将其读出来,这样就可以创建一个新的对象了,并且该新对象与母对象之间并不存在引用共享的问题,真正实现对象的深拷贝。
public class CloneUtils {
    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T clone(T obj){
        T cloneObj = null;
        try {
            //写入字节流
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ObjectOutputStream obs = new ObjectOutputStream(out);
            obs.writeObject(obj);
            obs.close();

            //分配内存,写入原始对象,生成新对象
            ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(ios);
            //返回生成的新对象
            cloneObj = (T) ois.readObject();
            ois.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return cloneObj;
    }
}
使用该工具类的对象必须要实现Serializable接口,否则是没有办法实现克隆的。
public class Person implements Serializable{
    private static final long serialVersionUID = 2631590509760908280L;

    ..................
    //去除clone()方法

}

public class Email implements Serializable{
    private static final long serialVersionUID = 1267293988171991494L;

    ....................
}
 所以使用该工具类的对象只要实现Serializable接口就可实现对象的克隆,无须继承Cloneable接口实现clone()方法。
 public class Client {
    public static void main(String[] args) {
        //写封邮件
        Email email = new Email("请参加会议","请与今天12:30到二会议室参加会议...");

        Person person1 =  new Person("张三",email);

        Person person2 =  CloneUtils.clone(person1);
        person2.setName("李四");
        Person person3 =  CloneUtils.clone(person1);
        person3.setName("王五");
        person1.getEmail().setContent("请与今天12:00到二会议室参加会议...");

        System.out.println(person1.getName() + "的邮件内容是:" + person1.getEmail().getContent());
        System.out.println(person2.getName() + "的邮件内容是:" + person2.getEmail().getContent());
        System.out.println(person3.getName() + "的邮件内容是:" + person3.getEmail().getContent());
    }
}
-------------------
Output:
张三的邮件内容是:请与今天12:00到二会议室参加会议...
李四的邮件内容是:请与今天12:30到二会议室参加会议...
王五的邮件内容是:请与今天12:30到二会议室参加会议...

4、抽象类和接口

抽象类与接口是java语言中对抽象概念进行定义的两种机制,正式由于他们的存在才赋予java强大的面向对象的能力。

5、为什么使用内部类?

使用内部类最吸引人的原因是:
1)每个内部类都能独立地继承一个接口的实现,所以无论外围类是否已经继承了某个(接口)的实现,对内部类都没有影响。
2)内部类提供了更好的封装,除了该外围类,其他类都不能访问

6、关键字static

static:全局或者静态的意思。用来修饰成员变量或者成员方法,或者修饰代码块。
java内存主要分为:堆内存和栈内存。
堆内存:存放一些基本类型的变量,数组和对象的引用。
栈内存:存放一些对象。
总结:无论是变量,方法,还是代码块,只要用static修饰,就是类加载时就已经“追备好了”

7、代码块

1)普通代码块
public class Test {
    public void test(){
        System.out.println("普通代码块");
    }
}
2)静态代码块
public class Test {
    static{
        System.out.println("静态代码块");//目的:是对静态属性进行初始化
    }
}
3)同步代码块
使用synchronized关键字修饰,并使用{}括起来的代码块,他表示同一时间只能有一个线程进入到该该方法块中,是一种多线程保护机制。
4)构造代码块
在勒种直接定义没有任何修饰符,前缀,后缀的代码块即为构造代码块。
    /**
     * 构造代码
     */
    {
        System.out.println("执行构造代码块...");
   }
   运行结果:总是先执行构造代码块,在执行构造函数,有一点需要注意构造代码不是在构造函数之前运行的,他是依托构造函数运行
   静态代码块,构造代码块,构造函数执行顺序:
     1)静态代码块:它是随着类的加载而被执行,只要类加载就会执行,而且只会加载一次,主要用于给类进行初始化。
     2)构造代码块,每创建对象时就会执行一次,切优于构造函数,主要用于初始化不同对象共性的初始化内容和初始化环境。
     3)构造函数,每创建一个对象时就会执行一次,同时构造函数是给特定对象进行初始化,而构造代码块是给所有对象进行初始化,作用区域不同。

8、字符串

1)String
String不是一个基本数据类型,而是一个对象
2)StringBuffer 线程安全
append():追加
insert:插入内容
delete:移除对象中的内容
3)StringBuilder 线程不安全
4)字符串拼接的三种方法:
+ ,concat(),append()
append() 的速度最快,concat()次之,+ 号最慢

你可能感兴趣的:(java提高)