JAVA- Details01

本文章仅用于记录学习Java时遇到的部分细节(01偏理论学习) - Felix


目录

> + 号的使用

>浮点数使用陷阱

> 字符编码表

> 基本数据类型转换

> String->char

> swith语句

> 接受一个char

> 区分对象及其引用

> Java内存结构分析

> 方法重载

> 可变参数

> this访问构造器

> 继承的构造器问题

> this 与 super 的区别

> 继承细节

> 向上/向下转型

> ==和equals方法

> equals方法重写

> toString方法

> 类变量

> 用到final的情况

> 自定义枚举

> enum关键字实现枚举

>三元运算符

> final

> 看起来String对象可变的幻觉

> String、StringBuffer和StringBuilder

>String和StringBuffer的转换

> 自定义排序

> BigInteger 保存较大的整型

> BigDecimal 保存精度较高的浮点型(小数)

> Iterator(迭代器)的使用

>List的三种遍历方式

> List接口的实现类比较

> 集合的选择


> + 号的使用

  • 当数值两边都是数值型时,做加法运算
  • 当左右两边有一方为字符串,则作拼接运算
System.out.printIn(100+98);//198
System.out.printIn("100"+98)://10098
System.out.printIn(100+3+"hello");//103hello 
System.out.printIn("hello"+ 100 +3); //hello1003

>浮点数使用陷阱

看下面的案例:

可以看到num2得到的值是一个接近2.7的小数,而非2.7。无法得到相等结论。可知:

当我们对运算结果是小数进行相等判断时,要小心,应该是以两个数的差值的绝对值,在某个精度范围内判断

if(Math.abs(num1-num2) < 0.00001){
    System.out.println("差值非常小,到我的规定精度,认为相等");
}

> 字符编码表

  • utf-8 (编码表,大小可变的编码)字母使用一个字节,汉字使用三个字节
  • gbk(可以表示汉字,而且范围广)字母使用一个字节,汉字使用两个字节
  • gb2312(可以表示汉字,gb2312 < gbk)
  • big5 码(繁体中文,台湾,香港)

> 基本数据类型转换

  • 自动提升原则:表达式结果的类型自动提升为 操作数中最大的类型
  • byte、short、char 三者可以计算,在计算时首先转换成int类型
byte b = 16; //ok
short s = 14; //ok
short t = s + b; //错误,int->short

> String->char

  • 字符串转成字符char -> 含义是指把字符串的第一个字符得到
String s = "123";
//s.charAt(0) 得到s字符串的第一个字符'1' 索引从0开始
System.out.println(s.charAt(0));

> swith语句

  1. 表达式数据类型,应与case后的常量类型一致,或者是可以自动转成可以相互比较的类型,比如输入的是字符,而常量是int
  2. swith(表达式) 中,表达式的返回值必须是:byte、short、int、char、enum、String
  3. case子句中的值必须是常量,而不能是变量
  4. default子句是可选的,当没有匹配的case时,执行default
  5. break语句用来在执行完一个case分支后使程序跳出switch语句块;如果没有写break,程序会顺序执行到switch结尾,除非遇到break

> 接受一个char

Scanner类没有nextChar()方法,因此需要读入char型时,可采用:
1.用next()代替,返回值为string
2.写一个类继承Scannar,添加一个方法,这个方法需要用到next()方法,返回值为char型;

Scanner in = new Scanner(System.in);
char key = in.next().charAt(0);

> 区分对象及其引用

//p 是对象名(对象引用)
//new Person() 创建的对象空间(数据) 才是真正的对象
  Person p = new Person();


> Java内存结构分析

  • 栈:一般存放基本数据类型(局部变量)
  • 堆:存放对象(Cat cat,数组等)
  • 方法区:常量池(常量,比如字符串),类加载信息

> 方法重载

  • 方法名:必须相同
  • 形参列表:必须不同(形参类型、个数或顺序,至少有一样不同,参数名无要求)
  • 返回类型:无要求

> 可变参数

访问修饰符 返回类型 方法名(数据类型... 形参名){
}
  • 可变参数的实参可以为0个或任意多个
  • 可变参数的实参可以为数组
  • 可变参数的本质就是数组
  • 可变参数可以和普通类型的参数一起放在形参列表 但必须保证可变参数在最后
  • 一个形参列表中只能出现一个可变参数
class T{
    public void f1(int...nums){
        System.out.println("长度="+nums.length);
    }
    //细节:可变参数可以和普通类型的参数一起放在形参列表 但必须保证可变参数在最后
    public void f1(String str,double...nums){
    }
    //细节:一个形参列表中只能出现一个可变参数
//    public void f1(String...str,double...nums){
//    }
}

> this访问构造器

访问构造器语法:this(参数列表);注意只能在构造器中使用(即只能在构造器中访问另一个构造器,必须放在第一条语句

class D{
    //细节:访问构造器语法:this(参数列表);注意只能在构造器中使用
    //     即只能在构造器中访问另一个构造器
    //注意:如果有构造器语法:this(参数列表);必须放置第一条语句
    public D(){
        this("jack",100);
        System.out.println("D()构造器");
        //这里去访问D(String name,int age)
    }
    public D(String name,int age){
        System.out.println("D(String name,int age)构造器");
    }
}

> 继承的构造器问题

  • 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器super()。如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过
  • super在使用时,必须放在构造器第一行
  • 结合上一个知识点可知,super()和this()这两个方法不能共存在一个构造器,因为他们都只能放在构造器第一行
  • 父类构造器的调用不限于直接父类,将一直往上追溯直到Object类(顶级父类)

> this 与 super 的区别


> 继承细节

  • 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类             
  •  ↑ eg: 父类返回类型是Objec,子类方法返回类型是String
  • 子类方法不能缩小父类方法的访问权限

> 向上/向下转型

多态的向上转型
1)本质: 父类的引用指向了子类的对象
2)语法: 父类类型  引用名 =new 子类类型()
3)特点: 编译类型看左边,运行类型看右边。
            可以调用父类中的所有成员(需遵守访问权限),不能调用子类中特有成员;
            最终运行效果看子类的具体实现!

多态的向下转型
1) 语法:子类类型 引用名 = (子类类型) 父类引用;
2)只能强转父类的引用,不能强转父类的对象
3)要求父类的引用必须指向的是当前目标类型的对象
4)当向下转型后,就可以调用子类类型中所有的成员


> ==和equals方法

==是一个比较运算符
1.既可以判断基本类型,又可以判断引用类型
2.如果判断基本类型,判断的是值是否相等
3.如果判断引用类型,判断的是地址是否相等

equals是Object类中的方法
1.只能判断引用类型
2.默认判断地址是否相等,子类中往往重写该方法,用于判断内容是否相等。如Interge,String


> equals方法重写

应用实例:判断两个Person对象的内容是否相等,如果两个Person对象的各个属性值都一样,则返回true,反之false.

//重写Object的equals方法
public boolean equals(Object obj) {
    //判断如果比较的两个对象是同一个对象,直接返回true
    if(this == obj){
        return true;
    }
    //类型判断
    if(obj instanceof Person){ //是Person,我们才比较
        //进行向下转型,因为需要得到obj的各个属性
        Person p = (Person)obj;
        return this.name.equals(p.name) && this.age == p.age 
                                        && this.gender == p.gender;
    }
    //如果不是Person,则直接返回false
    return false;
}

> toString方法

  • 默认返回:全类名+@+哈希值的十六进制,子类往往重写toString方法,用于返回对象的属性信息
  • 重写toString方法,打印或拼接对象时,都会自动调用该对象的toString形式
  • 当直接输出一个对象时,toString方法都会被默认的调用。比如System.out.println(monster),就会默认调用monster.toString()

> 类变量

  • 类变量是在类加载时就初始化了,也就是说,即使你没有创建对象,只要类加载了,就可以使用类变量了
  • 类变量的生命周期是随类的加载开始,随着类消亡而销毁

> 用到final的情况

  • 当不希望类被继承时
  • 当不希望父类的某个方法被子类覆盖/重写时
  • 当不希望类的某个属性的值被修改时
  • 当不希望某个局部变量被修改时

> 自定义枚举

  • 不需提供setXXX方法,因为枚举对象值通常为只读
  • 对枚举对象/属性使用final+static共同修饰,实现底层优化
  • 枚举对象名通常使用全部大写,常量的命名规范

> enum关键字实现枚举

  • 当我们使用enum 关键字开发一个枚举类时,默认会继承Enum类,而且是一个final类
  • 传统的 public static final Season2 SPRING = new Season2(春天”"温暖");简化成 SPRING(“春天”,"温暖”),这里必须知道,它调用的是哪个构造器
  • 如果使用无参构造器 创建 枚举对象,则实参列表和小括号都可以省略
  • 当有多个枚举对象时,使用,间隔,最后有一个分号结尾
  • 枚举对象必须放在枚举类的行首

>三元运算符

public class eg1 {
    public static void main(String[] args) {
        Object object = true? new Integer(1):new Double(2.0);
        System.out.println(object);
    }
}

输出结果为1.0而非1。

因为三元运算符是一个整体,整体优先级先提升到了Double,再进行的运算。


> final

当使用final修饰基本类型变量时,不能对基本类型变量重新赋值,因此基本类型变量不能被改变。但对于引用类型变量而言,它保存的仅仅是一个引用,final只保证这个引用变量所引用的地址不会改变,即一直引用同一个对象,但这个对象完全可以发生改变。例如某个指向数组的final引用,它必须从此至终指向初始化时指向的数组,但是这个数组的内容完全可以改变。


> 看起来String对象可变的幻觉

String中提供了一些看似可以改变String对象的方法,但实际上它们已经是指向了一个新建的对象

   public static void main(String[] args) {
        String str1 = "hello";
        // 打印str1的内存地址
        System.out.println("str1的内存地址:" + System.identityHashCode(str1));
        String str2 = "world";
        str1 += str2;
        // str1的内存地址已经改变了
        System.out.println("执行+=后str1的内存地址:" + System.identityHashCode(str1));
        System.out.println("拼接之后str1的值:" + str1);
 
        String str3 = "123";
        // 创建一个新的对象来保存拼接之后的值
        String str4 = str3.concat("456");
        // concat操作不会改变原来str3的值
        System.out.println("str3的值:" + str3);
        System.out.println("str4的值:" + str4);
    }
//运行结果
str1的内存地址:1922154895
执行+=后str1的内存地址:883049899
拼接之后str1的值:helloworld
str3的值:123
str4的值:123456

 str1+=str2实际上是执行了str1=(new StringBuilder()).append(str2).toString();前后实际额外产生了一个StringBuilder与一个helloworld的字符串常量。


> String、StringBuffer和StringBuilder

  • String:不可变字符序列,效率低,但复用率高
  • StringBuffer:可变字符序列,效率较高(增删),线程安全
  • StringBuilder:可变字符序列,效率最高,但线程不安全
  • 如果字符串存在大量的修改操作,一般使用 StringBuffer 或StringBuilder
  • 如果字符串存在大量的修改操作,并在单线程的情况,使用 StringBuilder
  • 如果字符串存在大量的修改操作,并在多线程的情况,使用 StringBuffer
  • 如果我们字符串很少修改,被多个对象引用,使用String,比如配置信息等

>String和StringBuffer的转换

//String->StringBuffer
  String str = "hello tom";
  //方式1 使用构造器
  //注意:返回的才是StringBuffer对象,对str本身无影响
  StringBuffer stringBuffer = new StringBuffer(str);
  //方式2 使用的是append方法
  StringBuffer stringBuffer1 = new StringBuffer();
  stringBuffer1 = stringBuffer1.append(str);
//StringBuffer->String
  StringBuffer stringBuffer2 = new StringBuffer("仪宝仪宝");
  //方式1 使用StringBuffer提供的to String方法
  String s = stringBuffer2.toString();
  //方式2 使用构造器
  String s1 = new String(stringBuffer2);

> 自定义排序

Array中的sort方法默认提供从小到大的排序,若需要从大到小和其他自定义排序,可通过实现Comparator接口匿名内部类定制排序。

//1.price从小到大
Arrays.sort(books, new Comparator() {
    @Override
    public int compare(Object o1, Object o2) {  
        Book book1 = (Book)o1;  
        Book book2 = (Book)o2;
        //因为继承的是int,不能修正为double(否则不是重写了)
        //所以利用下面进行转换
        double priceVal = book1.price-book2.price;
        if(priceVal>0)
            return 1;
        else if(priceVal<0)
            return -1;
        else
            return 0;
    }
}

//2.自定义排序(此处以书的长度排序为例子)
Arrays.sort(books, new Comparator() {
    @Override
    public int compare(Object o1,Object o2) {
        Book book1 = (Book1)o1;    //向下转型
        Book book2 = (Book1)o2;
        //1-2 -> 从小到大    2-1 -> 从大到小
        return book1.getName().length()-book2.getName().length();
    }
}

> BigInteger 保存较大的整型

BigInteger bigInteger = new BigInteger("2378888888889999999999999");
System.out.println(bigInteger);

> BigDecimal 保存精度较高的浮点型(小数)

BigDecimal bigDecimal = new BigDecimal(1999.111111111111111111999999);
System.out.println(bigDecimal);

常见方法:add加   subtract减   multiply乘   divide除

// 除 可能抛出异常ArithmeticException(因为精度很高,但又除不尽)
//解决方法:在调用divide方法时,指定精度即可
//BigDecimal.ROUND_CEILING 若有无限循环小数,就会保留分子的精度
System.out.println(bigDecimal.divide(bigDecimal2,BigDecimal.ROUND_CEILING));

> Iterator(迭代器)的使用

所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象,即可以返回一个迭代器。Iterator仅用于遍历集合,本身并不存放对象

Collection col = new ArrayList();
col.add(new Book("三国演义","罗贯中",10.1));
col.add(new Book("小李飞刀","古龙",5.1));

//遍历col集合
//1.先得到col对应的迭代器
Iterator iterator = col.iterator();
//2.使用while循环遍历
while(iterator.hasNext()){//判断是否还有数据
    // 返回下一个元素,类型是Object
    Object obj =iterator.next();
    System.out.println("obj="+obj);
}
//3.如果希望再次遍历,需要重置我们的迭代器
iterator =col.iterator(); //重置
System.out.println("===第二次遍历===");
while(iterator.hasNext()){
    Object obj =iterator.next();
    System.out.println("obj="+obj);
}

>List的三种遍历方式

1)方式一:使用iterator

Iterator iter = list.iterator();
while(iter.hasNext(){
      Object o = iter.next();
      System.out.println(o);
}

2)方式二:使用增强for

for(Object o:list){
    System.out.println(o);
}

3)方式三:使用普通for

for(int i=0;i<list.size();i++){
     System.out.println(list.get(i));
}

> List接口的实现类比较

List接口的实现类底层结构增删效率改查的效率线程安全
ArrayList可变数组较低,数组扩容(1.5倍)较高不安全
LinkedList双向链表较高,通过链表追加

较低

不安全
Vector可变数组较低,数组扩容(2倍)较低安全
  • 改查操作多 -> ArrayList/vector
  • 增删操作多 -> LinkedList

> 集合的选择


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值