Java笔记 ·

Java基础小结(一)

修饰符

访问控制修饰符

1、default (即缺省,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。

2、private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)

3、public : 对所有类可见。使用对象:类、接口、变量、方法

4、protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。

小结

1、权限级别public>protected>default>private

2、使用默认访问修饰符声明的变量和方法,对同一个包内的类是可见的。
接口里的变量都隐式声明为 public static final
接口里的方法默认情况下访问权限为 public

3、类和接口不能声明为 private

4、protected 访问修饰符不能修饰类和接口,方法和成员变量能够声明为 protected,但是接口的成员变量和成员方法不能声明为 protected。

继承的规则

1、父类中声明为 public 的方法在子类中也必须为 public。

2、父类中声明为 protected 的方法在子类中要么声明为 protected,要么声明为 public,不能声明为 private

3、父类中声明为 private 的方法,不能够被继承。

非访问修饰符

static 修饰符

1、用来修饰类方法和类变量。

2、对类变量和方法的访问可以直接使用 classname.variablename 和 classname.methodname 的方式访问。

静态变量:

static 关键字用来声明独立于对象的静态变量,无论一个类实例化多少对象,它的静态变量只有一份拷贝。 静态变量也被称为类变量。局部变量不能被声明为 static 变量。

静态方法:

static 关键字用来声明独立于对象的静态方法。静态方法不能使用类的非静态变量。静态方法从参数列表得到数据,然后计算这些数据。

final 修饰符

1、用来修饰类、方法和变量,

2、final 修饰的类不能够被继承,

3、修饰的方法不能被继承类重新定义,

4、修饰的变量为常量,是不可修改的。

final 变量:

1、final 变量能被显式地初始化并且只能初始化一次。

2、被声明为 final 的对象的引用不能指向不同的对象。

3、但是 final 对象里的数据可以被改变。也就是说 final 对象的引用不能改变,但是里面的值可以改变。如:

public class Modifler {
    // final修饰基本类型的变量
    public static final char CHAR = '中';
    public static final Integer num = 123;
    // final修饰引用类型的变量
    public static final StringBuffer a = new StringBuffer("StringBuffer");
    public static final  Student student = new Student();
public static void main(String[] args) {
        /*
         * 问题:使用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变
         * 答:
         * 使用final关键字修饰一个变量时,是指引用变量不能变,引用变量所指向的对象中的内容还是可以改变的。
         */
        //Modifler.num = 234;//编译报错,引用不能变
        //Modifler.CHAR = "2";//编译报错,引用不能变
        System.out.println("Modifler.a old:"+Modifler.a);
        Modifler.a.append("123");
        System.out.println("Modifler.a new:"+Modifler.a);
        System.out.println("——————————————————————————————");



        System.out.println("Modifer student old name;"+Modifler.student.getName()+", Age:"+Modifler.student.getAge());
        Modifler.student.setName("Change");
        Modifler.student.setAge(12);
        System.out.println("Modifer student old name;"+Modifler.student.getName()+", Age:"+Modifler.student.getAge());
    }
}

输出结果

Modifler.a old:StringBuffer
Modifler.a new:StringBuffer123
——————————————————————————————
Modifer student old name;null, Age:null
Modifer student old name;Change, Age:12

资料:
1、 面试题:使用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变?
2、final修饰引用时的问题

final 方法

1、类中的 final 方法可以被子类继承,但是不能被子类修改

2、声明 final 方法的主要目的是防止该方法的内容被修改

final 类

final 类不能被继承,没有类能够继承 final 类的任何特性。

abstract 修饰符

抽象类:

1、抽象类不能用来实例化对象,声明抽象类的唯一目的是为了将来对该类进行扩充

2、一个类不能同时被 abstract 和 final 修饰。如果一个类包含抽象方法,那么该类一定要声明为抽象类,否则将出现编译错误。

3、抽象类可以包含抽象方法和非抽象方法
实例:

abstract class Caravan{
   private double price;
   private String model;
   private String year;
   public abstract void goFast(); //抽象方法
   public abstract void changeColor();
}
抽象方法:

1、抽象方法是一种没有任何实现的方法,该方法的的具体实现由子类提供
2、抽象方法不能被声明成 final 和 static
3、任何继承抽象类的子类必须实现父类的所有抽象方法,除非该子类也是抽象类
4、如果一个类包含若干个抽象方法,那么该类必须声明为抽象类。抽象类可以不包含抽象方法。
5、抽象方法的声明以分号结尾,例如:public abstract sample();
实例:

public abstract class SuperClass{
    abstract void m(); //抽象方法
}
 
class SubClass extends SuperClass{
     //实现抽象方法
      void m(){
          .........
      }
}

synchronized 修饰符

1、synchronized 关键字声明的方法同一时间只能被一个线程访问。

2、synchronized 修饰符可以应用于四个访问修饰符。

3、synchronized不仅保证可见性,而且还保证原子性,因为,只有获得了锁的线程才能进入临界区,从而保证临界区中的所有语句都全部执行。多个线程争抢synchronized锁对象时,会出现阻塞。

volatile 修饰符

1、volatile 修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。

2、一个 volatile 对象引用可能是 null。

3、volatile关键字的作用是:使变量在多个线程间可见(可见性)

4、volatile关键字具有非原子性。

5、volatile关键字修饰的变量不会被指令重排序优化

volatile与synchronized对比

1、volatile轻量级,只能修饰变量。synchronized重量级,还可修饰方法。

2、volatile只能保证数据的可见性,不能用来同步,因为多个线程并发访问volatile修饰的变量不会阻塞。synchronized不仅保证可见性,而且还保证原子性.

3、仅仅使用volatile并不能保证线程安全性。而synchronized则可实现线程的安全性

资料
JAVA多线程之volatile 与 synchronized 的比较
Java 修饰符

transient 修饰符

1、序列化的对象包含被 transient 修饰的实例变量时,java 虚拟机(JVM)跳过该特定的变量。
2、该修饰符包含在定义变量的语句中,用来预处理类和变量的数据类型。
实例:

public class People implements Serializable{
    private String name;
    private transient Integer age;

    public People() {
        System.out.println("People");
    }
    {
        System.out.println("People static beffer");
    }
    public People(String name,int age){
        this.name = name;
        this.age = age;
    }
    static {
        System.out.println("People static");
    }

    {
        System.out.println("People static after");
    }

    @Override
    public String toString() {
        return "People{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class PeopleTest {
    public static void main(String[] args) {
        People p = new People("WindCoder",5);
        System.out.println(p);//打印对象的值
        try {
            ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("d://people.txt"));
            os.writeObject(p);//写入文件(序列化)
            os.close();
            ObjectInputStream is = new ObjectInputStream(new FileInputStream("d://people.txt"));
            p = (People) is.readObject();//将文件数据转换为对象(反序列化)
            System.out.println(p); // 年龄 数据未定义
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

输出内容:

People static
People static beffer
People static after
People{name='WindCoder', age=5}
People{name='WindCoder', age=null}

Java序列化与反序列化

1、Java序列化是指把Java对象转换为字节序列的过程;

2、Java反序列化是指把字节序列恢复为Java对象的过程。

3、一个对象只要实现了Serilizable接口,这个对象就可以被序列化。

基本数据类型与包装类

基本数据类型是不具备对象的特性的,比如基本类型不能调用方法、功能简单。。。,为了让基本数据类型也具备对象的特性, Java 为每个基本数据类型都提供了一个包装类,这样我们就可以像操作对象那样来操作基本数据类型。

所有的包装类(Integer、Long、Byte、Double、Float、Short)都是抽象类 Number 的子类。

编译器特别支持的包装称为装箱;

当内置数据类型被当作对象使用的时候,编译器会把内置类型装箱为包装类;

编译器也可以把一个对象拆箱为内置类型;

包装类主要提供了两大类方法:

  1. 将本类型和其他基本类型进行转换的方法
  2. 将字符串和本类型及包装类互相转换的方法

其他

浮点数的默认类型为double类型,而不是float;

instanceof运算符
该运算符用于操作对象实例,检查该对象是否是一个特定类型(类类型或接口类型)。
所以可以用于类似:

Double d1 = 1.23;
d1 instanceof Double;

Float f1 = 1.22f;
f1 instanceof Float;

而不可以用于

double d2 = 1.33;
d1 instanceof double

float f2 = 1.22f; 
f2 instanceof float

y+1<y成立条件

当y+1越界溢出时,如

int sh1 = 2147483647;
System.out.println("int y:"+(sh1+1));

打印结果为:

int y:-2147483648

Integer注意事项

        /**
         * 对于–128到127(默认是127)之间的值,被装箱后,会被放在内存里进行重用
         * 但是如果超出了这个值,系统会重新new 一个对象
         */
        Integer l1 = 122;
        Integer l2 = 122;

        /**
         *注意 == 与 equals的区别
         *== 它比较的是对象的地址
         *equlas 比较的是对象的内容
         */
        if (l1 == l2)System.out.println("l1 == l2 true");
        else System.out.println("l1 == l2  false");

        if (l1.equals(l2) )System.out.println("l1.equals(l2) true");
        else System.out.println("l1.equals(l2)  false");


        Integer l3 = 200;
        Integer l4 = 200;
        if (l3 == l4)System.out.println("l3 == l4 true");
        else System.out.println("l3 == l4  false");
        if (l3.equals(l4))System.out.println("l3.equals(l4) true");
        else System.out.println("l3.equals(l4)  false");

打印结果:

l1 == l2 true
l1.equals(l2) true
l3 == l4  false
l3.equals(l4) true

String

String 类是不可改变的,所以你一旦创建了 String 对象,那它的值就无法改变了,例如:

String s = "Google";
System.out.println("s = " + s);

s = "Runoob";
System.out.println("s = " + s);

输出结果为:

Google
Runoob

实例中的 s 只是一个 String 对象的引用,并不是对象本身,当执行 s = "Runoob"; 创建了一个新的对象 "Runoob",而原来的 "Google" 还存在于内存中。

String相关方法均是生成新的字符串,不会在原基础上改变,如上面的s和d:

        System.out.println("s.concat(d)= " +   s.concat(d));
        System.out.println("s concat after= " + s);
        System.out.println("s.substring(3)= " + s.substring(3));
        System.out.println("s substring after= " + s);
        System.out.println("s.replace(3)= " + s.replace("o","T"));
        System.out.println("s replace after= " + s);

输出结果为:

s.concat(d)= RunoobGoogle
s concat after= Runoob
s.substring(3)= oob
s substring after= Runoob
s.replace(3)= RunTTb
s replace after= Runoob

String、 StringBuffer 和 StringBuilder总汇

  1. String 类是不可改变的,每次修改实际都是创建了新的对象。
  2. StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。
  3. StringBuilder 相较于 StringBuffer 有速度优势,但其方法不是线程安全的(不能同步访问)。
  4. 由于StringBuilder 的速度优势,多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。

简单说是

  1. String 长度大小不可变
  2. StringBuffer 和 StringBuilder 长度可变
  3. StringBuffer 线程安全 StringBuilder 线程不安全
  4. StringBuilder 速度快

数组

数组转list
使用Arrays.asList(arr),如

String[] arr = new String[] {"1", "2","3"};
        List list = Arrays.asList(arr);
        System.out.println("list" + list.toString());

输出结果:

list[1, 2, 3]

日期

日期比较

三种方式:
1、 使用 getTime() 方法获取两个日期(自1970年1月1日经历的毫秒数值),然后比较这两个值。
2、使用方法 before(),after() 和 equals()。
3、 使用 compareTo() 方法,它是由 Comparable 接口定义的,Date 类实现了这个接口。
示例:

        Date now = new Date(2017,9,11);
        Date old = new Date(2017,8,11);
        long dec = now.getTime()-old.getTime();
        /**
         * dec > 0   now 晚于 old
         * dec = 0   now 等于 old
         * dec < 0   now 早于 old
         */
        System.out.println("getTime() now-old: "+dec);

        System.out.println("——————————————————————————————");
        /**
         *  before(),after() 和 equals()
         *  状态为:true或false
         */
        System.out.println("old.before(now): "+old.before(now));

        System.out.println("——————————————————————————————");
        /**
         *  大于0  old晚于now
         *  等于0  old等于now
         *  小于0  old早于now
         */
        System.out.println("old.compareTo(now): "+old.compareTo(now));

输出结果

getTime() now-old: 2592000000
——————————————————————————————
old.before(now): true
——————————————————————————————
old.compareTo(now): -1

判断是否为闰年

Calendar类实现了公历日历,GregorianCalendar是Calendar类的一个具体实现。里面isLeapYear()方法可用来判断是否为闰年,如:

GregorianCalendar gcalendar = new GregorianCalendar();
int  year =  gcalendar.get(Calendar.YEAR);
if (gcalendar.isLeapYear(year))System.out.println("当前年份:"+year+",是闰年");
else System.out.println("当前年份:"+ year+",不是闰年");

输出结果:

当前年份:2017,不是闰年

方法

方法重载

重载的方法必须拥有不同的参数列表。你不能仅仅依据修饰符或者返回类型的不同来重载方法。

可变参数

在方法声明中,在指定参数类型后加一个省略号(...) 。
一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明。

Java 流

  1. 流分为字节流和字符流。
  2. 字节流也称为原始数据,需要用户读入后进行相应的编码转换。
  3. 字符流的实现是基于自动转换的,读取数据时会把数据按照JVM的默认编码自动转换成字符。
  4. 如果数据流中最小的数据单元是字节,那么称这种流为字节流;
  5. 如果数据流中最小的数据单元是字符,那么称这种流为字符流。

字符流

从Reader和Writer派生出的一系列类,这类流以16位的Unicode码表示的字符为基本处理单位。

Reader ---输入

Writer ---输出

字节流

从InputStream和OutputStream派生出来的一系列类。这类流以字节(byte)为基本处理单位。

InputStream ---输入

FileInputStream

该流用于从文件读取数据,它的对象可以用关键字 new 来创建。

有多种构造方法可用来创建对象。

可以使用字符串类型的文件名来创建一个输入流对象来读取文件:

InputStream f = new FileInputStream("C:/java/hello");

也可以使用一个文件对象来创建一个输入流对象来读取文件。我们首先得使用 File() 方法来创建一个文件对象:

File f = new File("C:/java/hello");
InputStream out = new FileInputStream(f);

OutputStream ---输出

FileOutputStream

该类用来创建一个文件并向文件中写数据。

如果该流在打开文件进行输出前,目标文件不存在,那么该流会创建该文件。

有两个构造方法可以用来创建 FileOutputStream 对象。

使用字符串类型的文件名来创建一个输出流对象:

OutputStream f = new FileOutputStream("C:/java/hello")

也可以使用一个文件对象来创建一个输出流来写文件。我们首先得使用File()方法来创建一个文件对象:

File f = new File("C:/java/hello");
OutputStream f = new FileOutputStream(f);

Java中的目录

创建目录

File类中有两个方法可以用来创建文件夹:

  1. mkdir( )方法创建一个文件夹,成功则返回true,失败则返回false。失败表明File对象指定的路径已经存在,或者由于整个路径还不存在,该文件夹不能被创建。
  2. mkdirs()方法创建一个文件夹和它的所有父文件夹。

读取目录

  1. 一个目录其实就是一个 File 对象,它包含其他文件和文件夹。
  2. 如果创建一个 File 对象并且它是一个目录,那么调用 isDirectory() 方法会返回 true。
  3. 可以通过调用该对象上的 list() 方法,来提取它包含的文件和文件夹的列表。

删除目录或文件

删除文件可以使用 java.io.File.delete() 方法。

Scanner

通过 Scanner 类的 next()nextLine() 方法获取输入的字符串,在读取前我们一般需要 使用 hasNexthasNextLine 判断是否还有输入的数据。

next() 与 nextLine() 区别

next():

1、一定要读取到有效字符后才可以结束输入。

2、对输入有效字符之前遇到的空白,next() 方法会自动将其去掉。

3、只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符。

4、next() 不能得到带有空格的字符串。

nextLine():

1、以Enter为结束符,也就是说 nextLine()方法返回的是输入回车之前的所有字符。

2、可以获得空白。

另:如果要输入 int 或 float 类型的数据,在 Scanner 类中也有支持,但是在输入之前最好先使用 hasNextXxx() 方法进行验证,再使用 nextXxx() 来读取。

资料

理解Java中字符流与字节流的区别 

Java中的流、字符流和字节流 

字节流与字符流的区别详解 

Java 流(Stream)、文件(File)和IO

异常

异常和错误的区别:异常能被程序本身可以处理,错误是无法处理。

try/catch

1、finally永远都会在catch的return前被执行

2、对于try/catch捕获异常的形式来说,对于异常的捕获,可以有多个catch。

3、如果有匹配的catch,它就会忽略掉这个catch后面所有的catch。

4、在写异常处理的时候,一定要把异常范围小的放在前面,范围大的放在后面,,Exception这个异常的根类一定要放在最后一个catch里面,如果放在前面或者中间,任何异常都会和Exception匹配的,就会报已捕获到...异常的错误。

(十一):Java中异常的捕获顺序(多个catch)

finally关键字

1、finally 关键字用来创建在 try 代码块后面执行的代码块。

2、无论是否发生异常,finally 代码块中的代码总会被执行。

3、在 finally 代码块中,可以运行清理类型等收尾善后性质的语句。

throws/throw 关键字

1、throws 关键字来声明异常,放在方法签名的尾部。

2、使用 throw 关键字抛出一个异常,无论它是新实例化的还是刚捕获到的。

3、一个方法可以声明抛出多个异常,多个异常之间用逗号隔开。

4、throws表示一个方法声明可能抛出一个异常,throw表示此处抛出一个已定义的异常(可以是自定义需继承Exception,也可以是java自己给出的异常类)。

import java.io.*;
public class className
{
  public void deposit(double amount) throws RemoteException,InsufficientFundsException
  {
    // Method implementation
    throw new RemoteException();
  }
  //Remainder of class definition
}

异常名词解释

1、检查性异常: 不处理编译不能通过

2、非检查性异常:不处理编译可以通过,如果有抛出直接抛到控制台

3、运行时异常: 就是非检查性异常

4、非运行时异常: 就是检查性异常

错误与异常

1、error---错误 : 是指程序无法处理的错误,表示应用程序运行时出现的重大错误。例如jvm运行时出现的OutOfMemoryError以及Socket编程时出现的端口占用等程序无法处理的错误。

2、Exception --- 异常 :异常可分为运行时异常跟编译异常。

1)运行时异常:即RuntimeException及其之类的异常。这类异常在代码编写的时候不会被编译器所检测出来,是可以不需要被捕获,但是程序员也可以根据需要进行捕获抛出。常见的RUNtimeException有:NullpointException(空指针异常),ClassCastException(类型转换异常),IndexOutOfBoundsException(数组越界异常)等。

2)编译异常:RuntimeException以外的异常。这类异常在编译时编译器会提示需要捕获,如果不进行捕获则编译错误。常见编译异常有:IOException(流传输异常),SQLException(数据库操作异常)等。

3、java处理异常的机制:抛出异常以及捕获异常 ,一个方法所能捕捉的异常,一定是Java代码在某处所抛出的异常。简单地说,异常总是先被抛出,后被捕捉的

参与评论