java基础知识

拜拜、爱过 提交于 2020-03-01 22:47:55

1.final 关键字主要用在三个地方:变量、方法、类。

对于一个 final 变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
当用 final 修饰一个类时,表明这个类不能被继承。final 类中的所有成员方法都会被隐式地指定为 final 方法。
使用 final 方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;

2.static 关键字主要有以下四种使用场景?
修饰成员变量和成员方法: 被 static 修饰的成员属于类,不属于单个这个类的某个对象,被类中所有对象共享,可以并且建议通过类名调用。被static 声明的成员变量属于静态成员变量,静态变量 存放在 Java 内存区域的方法区。调用格式:类名.静态变量名 类名.静态方法名()
静态代码块: 静态代码块定义在类中方法外, 静态代码块在非静态代码块之前执行(静态代码块—>非静态代码块—>构造方法)。 该类不管创建多少对象,静态代码块只执行一次.
静态内部类(static修饰类的话只能修饰内部类): 静态内部类与非静态内部类之间存在一个最大的区别: 非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围类,但是静态内部类却没有。没有这个引用就意味着:1. 它的创建是不需要依赖外围类的创建。2. 它不能使用任何外围类的非static成员变量和方法。
静态导包(用来导入类中的静态资源,1.5之后的新特性): 格式为:import static 这两个关键字连用可以指定导入某个类中的指定静态资源,并且不需要使用类名调用类中静态成员,可以直接使用类中静态成员变量和成员方法。

3.BIO,NIO,AIO 有什么区别?
BIO (Blocking I/O): 同步阻塞 I/O 模式,数据的读取写入必须阻塞在一个线程内等待其完成。
NIO (New I/O): NIO 是一种同步非阻塞的 I/O 模型,在 Java 1.4 中引入了 NIO 框架,对应 java.nio 包,提供了 Channel , Selector,Buffer 等抽象。NIO 中的 N 可以理解为 Non-blocking,不单纯是 New。
AIO (Asynchronous I/O):异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作.
4.get和post的区别
详细介绍:
https://www.zhihu.com/question/28586791
GET产生一个TCP数据包;POST产生两个TCP数据包。
对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
GET在浏览器回退时是无害的,而POST会再次提交请求。
GET产生的URL地址可以被Bookmark,而POST不可以。
GET请求会被浏览器主动cache,而POST不会,除非手动设置。
GET请求只能进行url编码,而POST支持多种编码方式。
GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
GET请求在URL中传送的参数是有长度限制的,而POST么有。
对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
GET参数通过URL传递,POST放在Request body中。
4.Collections 工具类和 Arrays 工具类常见方法
void reverse(List list)//反转
void shuffle(List list)//随机排序
void sort(List list)//按自然排序的升序排序
void sort(List list, Comparator c)//定制排序,由Comparator控制排序逻辑
void swap(List list, int i , int j)//交换两个索引位置的元素
void rotate(List list, int distance)//旋转。当distance为正数时,将list后distance个元素整体移到前面。当distance为负数时,将 list的前distance个元素整体移到后面。
int binarySearch(List list, Object key)//对List进行二分查找,返回索引,注意List必须是有序的
int max(Collection coll)//根据元素的自然顺序,返回最大的元素。 类比int min(Collection coll)
int max(Collection coll, Comparator c)//根据定制排序,返回最大元素,排序规则由Comparatator类控制。类比int min(Collection coll, Comparator c)
void fill(List list, Object obj)//用指定的元素代替指定list中的所有元素。
int frequency(Collection c, Object o)//统计元素出现次数
int indexOfSubList(List list, List target)//统计target在list中第一次出现的索引,找不到则返回-1,类比int lastIndexOfSubList(List source, list target).
boolean replaceAll(List list, Object oldVal, Object newVal), 用新元素替换旧元素。
5.Arrays类的常见操作
排序 : sort()
查找 : binarySearch()
比较: equals()
填充 : fill()
转列表: asList()
转字符串 : toString()
复制: copyOf()
6.深拷贝 vs 浅拷贝
浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝。
深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。
实现深拷贝常用的实现方式有2种:Serializable,Cloneable。
Serializable方式就是通过java对象的序列化和反序列化的操作实现对象拷贝的一种比较常见的方式。本来java对象们都待在虚拟机堆中,通过序列化,将源对象的信息以另外一种形式存放在了堆外。这时源对象的信息就存在了2份,一份在堆内,一份在堆外。然后将堆外的这份信息通过反序列化的方式再放回到堆中,就创建了一个新的对象,也就是目标对象。

public static Object cloneObjBySerialization(Serializable src)
    {
        Object dest = null;
        try
        {
            ByteArrayOutputStream bos = null;
            ObjectOutputStream oos = null;
            try
            {
                bos = new ByteArrayOutputStream();
                oos = new ObjectOutputStream(bos);
                oos.writeObject(src);
                oos.flush();
            }
            finally
            {
                oos.close();
            }
            byte[] bytes = bos.toByteArray();
            ObjectInputStream ois = null;
            try
            {
                ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
                dest = ois.readObject();
            }
            finally
            {
                ois.close();
            }
        }
        catch(Exception e)
        {
            e.printStackTrace();//克隆失败
        }
        return dest;
    }

源对象类型及其成员对象类型需要实现Serializable接口,一个都不能少。

import java.io.Serializable;

public class BattleShip implements Serializable
{
    String name;
    ClonePilot pilot;
    BattleShip(String name, ClonePilot pilot)
    {
        this.name = name;
        this.pilot = pilot;
    }
}
//ClonePilot类型实现了Cloneable接口,不过这对通过Serializable方式拷贝对象没有影响
public class ClonePilot implements Serializable,Cloneable
{
    String name;
    String sex;
    ClonePilot(String name, String sex)
    {
        this.name = name;
        this.sex = sex;
    }
    public ClonePilot clone()
    {
        try
        {
            ClonePilot dest = (ClonePilot)super.clone();
            return dest;
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        return null;
    }
}

另外一种方式是Cloneable,核心是Object类的native方法clone()。通过调用clone方法,可以创建出一个当前对象的克隆体,但需要注意的是,这个方法不支持深拷贝。如果对象的成员变量是基础类型,那妥妥的没问题。但是对于自定义类型的变量,就有问题了。你会发现源对象和目标对象的自定义类型成员变量是同一个对象,也就是浅拷贝,浅拷贝就是对对象引用(地址)的拷贝。这样的话源对象和目标对象就不是彼此独立,而是纠缠不休了。为了弥补clone方法的这个不足。需要我们自己去处理非基本类型成员变量的深拷贝。

public class Cruiser implements Cloneable
{
    String name;
    ClonePilot pilot;
    Cruiser(String name, ClonePilot pilot)
    {
        this.name = name;
        this.pilot = pilot;
    }

    //Object.clone方法是protected修饰的,无法在外部调用。所以这里需要重载clone方法,改为public修饰,并且处理成员变量浅拷贝的问题。
    public Cruiser clone()
    {
        try
        {
            Cruiser dest = (Cruiser)super.clone();
            dest.pilot = this.pilot.clone();
            return dest;
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        return null;
    }
}
public class ClonePilot implements Serializable,Cloneable
{
    String name;
    String sex;
    ClonePilot(String name, String sex)
    {
        this.name = name;
        this.sex = sex;
    }
    //因为所有成员变量都是基本类型,所以只需要调用Object.clone()即可
    public ClonePilot clone()
    {
        try
        {
            ClonePilot dest = (ClonePilot)super.clone();
            return dest;
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        return null;
    }
}

7.BigDecimal
浮点数之间的等值判断,基本数据类型不能用==来比较,包装数据类型不能用 equals 来判断。

float a = 1.0f - 0.9f;
float b = 0.9f - 0.8f;
System.out.println(a);// 0.100000024
System.out.println(b);// 0.099999964
System.out.println(a == b);// false
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");
BigDecimal x = a.subtract(b);// 0.1
BigDecimal y = b.subtract(c);// 0.1
System.out.println(x.equals(y));// true
BigDecimal m = new BigDecimal("1.255433");
BigDecimal n = m.setScale(3,BigDecimal.ROUND_HALF_DOWN);
System.out.println(n);// 1.255

在这里插入图片描述
8.基本数据类型与包装数据类型的使用标准
强制】所有的 POJO 类属性必须使用包装数据类型。
【强制】RPC 方法的返回值和参数必须使用包装数据类型。
【推荐】所有的局部变量使用基本数据类型。
比如我们如果自定义了一个Student类,其中有一个属性是成绩score,如果用Integer而不用int定义,一次考试,学生可能没考,值是null,也可能考了,但考了0分,值是0,这两个表达的状态明显不一样.

说明 :POJO 类属性没有初值是提醒使用者在需要使用时,必须自己显式地进行赋值,任何 NPE 问题,或者入库检查,都由使用者来保证。

正例 : 数据库的查询结果可能是 null,因为自动拆箱,用基本数据类型接收有 NPE 风险。

反例 : 比如显示成交总额涨跌情况,即正负 x%,x 为基本数据类型,调用的 RPC 服务,调用不成功时,返回的是默认值,页面显示为 0%,这是不合理的,应该显示成中划线。所以包装数据类型的 null 值,能够表示额外的信息,如:远程调用失败,异常退出。
9.Arrays.asList()将数组转换为集合后,底层其实还是数组在这里插入图片描述
传递的数组必须是对象数组,而不是基本类型。
Arrays.asList()是泛型方法,传入的对象必须是对象数组。当传入一个原生数据类型数组时,Arrays.asList() 的真正得到的参数就不是数组中的元素,而是数组对象本身!此时List 的唯一元素就是这个数组。
如何正确的将数组转换为ArrayList?
最简便的方法(推荐)

List list = new ArrayList<>(Arrays.asList("a", "b", "c"))

使用 Java8 的Stream(推荐)

Integer [] myArray = { 1, 2, 3 };
List myList = Arrays.stream(myArray).collect(Collectors.toList());
//基本类型也可以实现转换(依赖boxed的装箱操作)
int [] myArray2 = { 1, 2, 3 };
List myList = Arrays.stream(myArray2).boxed().collect(Collectors.toList());

Collection.toArray()方法使用的坑&如何反转数组

String [] s= new String[]{
    "dog", "lazy", "a", "over", "jumps", "fox", "brown", "quick", "A"
};
List<String> list = Arrays.asList(s);
Collections.reverse(list);
s=list.toArray(new String[0]);//没有指定类型的话会报错

在这里插入图片描述
不要在 foreach 循环里进行元素的 remove/add 操作
在这里插入图片描述
在这里插入图片描述
泛型的实际应用
自己设计一个泛型的获取数组最小值的函数.并且这个方法只能接受Number的子类并且实现了Comparable接口。
//注意:Number并没有实现Comparable

private static <T extends Number & Comparable<? super T>> T min(T[] values) {
    if (values == null || values.length == 0) return null;
    T min = values[0];
    for (int i = 1; i < values.length; i++) {
        if (min.compareTo(values[i]) > 0) min = values[i];
    }
    return min;
}

自动拆装箱
对于基本类型和包装类型之间的转换,通过xxxValue()和valueOf()两个方法完成自动拆装箱,使用jad进行反编译可以看到该过程:

public class Demo {
  public static void main(String[] args) {
    int x = new Integer(10);  // 自动拆箱
    Integer y = x;            // 自动装箱
  }
}

反编译后结果:

public class Demo
{
    public Demo(){}

    public static void main(String args[])
    {
        int i = (new Integer(10)).intValue();   // intValue()拆箱
        Integer integer = Integer.valueOf(i);   // valueOf()装箱
    }
}

熟悉Arrays.asList(T…)用法的小伙伴都应该知道,asList()方法传入的参数不能是基本类型的数组,必须包装成包装类型再使用,否则对应生成的列表的大小永远是1,传入基本类型的数组后,会被转换成一个二维数组,而且是**new int[1][arr.length]**这样的数组,调用list.size()当然返回1。

注解
Java中的类、接口、枚举、注解都可以看做是类类型。使用jad来看一下@interface被转换成什么:在这里插入图片描述

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!