java基础总结1


数据类型

基本数据类型

  • byte (-128 , 127): 8位,用在大型数组中节约空间,主要代替整数,因为 byte 变量占用的空间只有 int 类型的四分之一
  • short (-2^15 , 2^15 - 1): 16位
  • int (2^-31 , 2^31 - 1 ) : 32位 , 一般地整型变量默认为 int 类型
  • long (-2^63 , 2^63 - 1) : 64位,长整数 ,默认值 0L
  • float : 单精度,32位,默认0.0f , 不能表示精确的值,如货币
  • double : 双精度 , 64位 , 默认0.0d , 不能表示精确的值
  • boolean : true / false , 默认值false
  • char : 16位Unicode字符 , 可以存储任意字符

引用数据类型

  • 对象、数组等都是引用数据类型,引用类型指向一个对象
  • 所有引用类型的默认值都为null

数据类型转换

  • 不能对boolean类型进行类型转换。
  • 不能把对象类型转换成不相关类的对象。
  • 在把容量大的类型转换为容量小的类型时必须使用强制类型转换。
  • 转换过程中可能导致溢出或损失精度
  • 浮点数到整数的转换是通过舍弃小数得到,而不是四舍五入
public class QiangZhiZhuanHuan{
    public static void main(String[] args){
        int i =128;   
        byte b = (byte)i; // 损失精度   
        (int)23.7 == 23;   // true 舍弃小数转换
        char c1='a';//定义一个char类型
        int i0 = c1;//char自动类型转换为int
        int i1 = 123;
        byte b = (byte)i1;//强制类型转换为byte
        System.out.println("int强制类型转换为byte后的值等于"+b);
    }   
}

对象和类

类: 是一个模板,描述一类对象的行为和状态

  • 一个类可以包含以下类型变量
    • 局部变量: 在方法中定义的变量
    • 成员变量: 方法体之外,类中的变量
    • 类变量: static 修饰的成员变量
  • 构造方法
    • 一个类默认有一个无参构造方法,如果我们自己写了构造方法,默认构造将不再存在

对象: 类的一个实例,有状态和行为

  • 创建对象
    • 声明 : 对象名称和对象类型
    • 实例化 : 使用关键字new创建对象
    • 初始化 : 调用构造方法初始化对象
public class Dog{
  static String breed; // 类变量/静态变量
  int age; // 成员变量
  void barking(){
      int a = 1 ; // 局部变量
  }
  public Dog(int age){
        // 这个构造器仅有一个参数:age
  }
    public static void main(String[] args){
      // 下面的语句将创建一个Dog对象
      Dog dog = new Dog( "d" );
   }

}

修饰符

访问控制修饰符

  • public : 对所有类可见
  • protected : 对同一包内的类和所有子类可见
  • default : 默认,什么都不写,同一包内可见
  • private : 同一类中可见

重写时子类的权限修饰符必须和父类相同或者更高

非访问控制修饰符

  • static : 修饰类方法和类变量。
    • 静态变量: 用来声明独立于对象的变量,无论实例化多少次,静态变量只有一份拷贝
    • 静态方法: 独立于对象的方法,只能使用静态变量
  • final : 用来修饰类、方法和变量,final 修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的。
    • final 类不能被继承,没有类能够继承 final 类的任何特性。
    • final 修饰的实例变量必须显式指定初始值。变量一旦赋值后,不能被重新赋值
    • final 修饰符通常和 static 修饰符一起使用来创建类常量。
  • abstract : 用来创建抽象类和抽象方法。
    • 抽象类不能用来实例化对象,声明抽象类的唯一目的是为了将来对该类进行扩充。
    • 一个类不能同时被 abstract 和 final 修饰。
    • 抽象方法是一种没有任何实现的方法,该方法的的具体实现由子类提供。
  • synchronized : 用于线程的编程。
    • synchronized 关键字声明的方法同一时间只能被一个线程访问。
  • transient : 序列化的对象包含被 transient 修饰的实例变量时,java 虚拟机(JVM)跳过该特定的变量。
  • volatile : volatile 修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。

继承

继承主要解决的问题是 : 共性抽取

  • java语言是单继承的,一个类的直接父类只能有一个
  • java语言可以多级继承,class A extends B extends C {},继承的最顶端是java.lang.Object类
  • 一个子类的直接父类是唯一的,但是一个父类可以拥有很多子类

重写(Override) : 在继承的关系中,方法的名称一样,参数列表也(一样)

重载(Overload) : 方法名称一样,列表参数(不一样)

重名情况

  • 局部变量,直接写
  • 本类变量,this.变量名
  • 父类变量,super.变量名
  • 方法重名: 创建的对象是谁,就优先用谁,如果没有就向上查找
  • 无论是成员方法,还是成员变量,都是向上查找,绝对不会向下查找

继承关系的构造函数执行:

  • 1.子类构造方法中有一个默认隐含的super()调用,所以父类先执行,子类后执行
  • 2.可以通过super关键字来调用父类重载构造
  • 3.super的父类构造调用,必须是子类的第一个语句,不能一个子类构造调用多次super构造(super只能有一个)

this也能用来在构造中调用其他构造,也必须是第一个语句,不能喝super同时调用,也不能执行多个构造。

抽象类

抽象类: 抽象方法所在的类,必须是抽象类,在class之前加上abstract即可

抽象方法: 就是加上abstract关键字,然后去掉大括号,直接分号结束

  • 抽象类不能之间new使用,必须有子类继承父类,new子类来使用
  • 子类必须覆盖重写父类所有的抽象方法
  • 抽象类可以有构造方法,供子类创建对象时,初始化父类成员使用
  • 抽象类中不一定包含抽象方法,但是抽象方法必须在抽象类中
  • 抽象类的子类必须重写父类的所有抽象方法,否则该子类必须是一个抽象类
  • 构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。
    public abstract class Employee{ // 抽象类
      public abstract void mailCheck(); // 抽象方法
    }
    public class Salary extends Employee{} // 子类继承父类

接口

接口就是一个类的公共规范

接口中可以包含以下内容

  • 常量(java7 jdk)
  • 抽象方法(java7 jdk)
  • 默认方法(java8 jdk)
  • 静态方法(java8 jdk)
  • 私有方法(java9 jdk)

注意事项

  • 接口中的抽象方法,修饰符必须是默认的两个关键字,public abstract
  • 如果实现类没有覆盖重写接口中所有的抽象方法,那么这个实现类自己就必须是抽象类
  • 接口内是没有静态代码块的,也没有构造方法的
  • 一个类的直接父类是唯一的,但是一个类是可以同时实现多个接口的
    • 格式: public class MyClass implements MyInterfaceA, MyInterfaceB{}
  • 如果实现类所实现的多个接口中存在重复的抽象方法,那么只需要覆盖重写一次即可
  • 如果实现类所实现的多个方法中存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写
  • 一个类的直接父类方法如果和接口默认方法重复,那么会优先使用父类的方法
  • 接口与接口之间是多继承的
  • 多个父接口中的抽象方法如果重复是可以的,如果多个父接口中的默认方法重复,那么子接口必须进行默认方法的覆盖重写[default关键字必须带]
// 定义接口
interface Animal {
   public void eat();
   public void travel();
}
// 实现接口
public class MammalInt implements Animal{
    // 重写接口抽象方法
   public void eat(){
      System.out.println("Mammal eats");
   }
   public void travel(){
      System.out.println("Mammal travels");
   } 
   // 自己独有的方法
   public int noOfLegs(){
      return 0;
   }
   public static void main(String args[]){
      MammalInt m = new MammalInt();
      m.eat();
      m.travel();
   }
}

接口的继承: 一个接口能继承另一个接口,和类之间的继承方式比较相似。接口的继承使用extends关键字,子接口继承父接口的方法。

  • 接口可以是多继承的
    • 格式: public interface Hockey extends Sports, Event

标记接口: 最常用的继承接口是没有包含任何方法的接口。

  • 简单形象的说就是给某个对象打个标(盖个戳),使对象拥有某个或某些特权。

标记接口作用:

  • 建立一个公共的父接口
  • 向一个类添加数据类型

Date类

构造函数

  • Date() : 获取当前系统的日期和时间
  • Date(long date) : 传递毫秒值,将毫秒值转换成系统的日期时间

成员方法

  • long getTime() : 返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。

SimpleDateFormat类 : 允许自定义时间格式

  • String format(Date date) : 按照指定的模式把date日期,格式化为符合模式的字符串
  • Date parse(String source) : 将符合模式的字符串,解析为date日期
  • DateFormat是一个抽象类,不能直接创建对象使用,可以用DateFormat的子类(java.text.SimpleDateFormat)

parse方法声明了一个异常ParseException解析异常,如果字符串和构造方法的模式不一样,那么程序就会抛出异常.解决: 要么throws继续声明异常,要么try catch自己处理异常

import  java.util.*;
import java.text.*;

public class DateDemo {
   public static void main(String args[]) {

      Date dNow = new Date( );
      SimpleDateFormat ft = new SimpleDateFormat ("yyyy-MM-dd hh:mm:ss");

      System.out.println("当前时间为: " + ft.format(dNow));
   }
}

Calendar类

  • Calendar类是一个抽象类,在实际使用时实现特定的子类的对象,创建对象的过程对程序员来说是透明的,只需要使用getInstance方法创建即可。
Calendar c = Calendar.getInstance();//默认是当前日期

Calendar c1 = Calendar.getInstance();
c1.set(2009, 6 - 1, 12); // 获取指定日期的Calendar对象

Calendar类对象字段类型

//Calendar类对象信息的设置
c1.set(Calendar.YEAR,2008);
c1.set(Calendar.DATE,10);
//Add设置
c1.add(Calendar.DATE, 10);// 把c1对象的日期加上10,也就是c1也就表示为10天后的日期,其它所有的数值会被重新计算
//Calendar类对象信息的获得
Calendar c1 = Calendar.getInstance();
// 获得年份
int year = c1.get(Calendar.YEAR);
// 获得月份
int month = c1.get(Calendar.MONTH) + 1;
// 获得日期
int date = c1.get(Calendar.DATE);
// 获得小时
int hour = c1.get(Calendar.HOUR_OF_DAY);
// 获得分钟
int minute = c1.get(Calendar.MINUTE);
// 获得秒
int second = c1.get(Calendar.SECOND);
// 获得星期几(注意(这个与Date类是不同的):1代表星期日、2代表星期1、3代表星期二,以此类推)
int day = c1.get(Calendar.DAY_OF_WEEK);

Collection集合

java.util.Collection : 所有单列集合的最顶层接口,里面定义了单列集合的共性方法,单列集合都可以使用

  • 数组的长度是固定的,集合的长度是可变的
  • 数组可以存储基本类型,集合不可以

共性方法

  • public boolean add(E e) : 把给定的对象添加到集合中
  • public boolean remove(E e) : 把给定的元素在集合中删除
  • public boolean contains(E e) : 判断集合中是否包含给定的元素
  • public boolean isEmpty() : 集合是否为空
  • public int size() : 返回集合元素的个数
  • public Object[] toArray() : 把集合中的元素,存储到数组中
  • public void clear() : 清空集合内的所有元素

迭代器 : java.util.Iterator接口

两个常用方法

  • boolean hasNext() 如果仍有元素可以迭代,则返回true
  • E next() 取出集合中下一个元素
    因为是一个接口,我们只能使用其实现类,获取实现类的方式特殊: Collection中有一个Iterator(),这个方法返回迭代器实现类

使用步骤

  • 1.使用集合方法获取实现类,使用Iterator接口接受(多态)
  • 2.使用接口方法hasNext方法判断是否有下一个元素
  • 3.使用next方法取出下一个元素

原理:

    1. 获取迭代器对象,并且把指针放在索引-1的位置
    1. next方法会取出下一个元素,并且把指针往后移动一位

增强for循环: 底层使用的也是迭代器,使用for循环的格式,简化了迭代器的书写,Collection extends Iterator ,所有的单列集合都可以使用foreach

// 格式
for ( 集合/数组的数据类型 变量名: 集合名/数组名){
      // 循环操作
}

示例

public class Demo01Collection {
    public static void main(String[] args) {
        Collection<String> arr = new ArrayList<>();
        arr.add("姚明");
        arr.add("詹姆斯");
        arr.add("科比");
        Iterator<String> it = arr.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }
        System.out.println("============");
        for (String item: arr){
            System.out.println(item);
        }
    }
}

java多线程

创建线程的两种方式

  • 创建Thread类的子类

    • 1、创建Thread的子类
    • 2、子类重写Thread类的run方法,设置线程任务
    • 3、创建子类对象
    • 4、调用Thread类的start方法,开启新线程,执行run方法
  • 实现Runnable接口

    • 1、创建一个Runnable接口的实现类
    • 2、在实现类中重写run方法,无参数的,设置线程任务
    • 3、创建一个Runnable接口的实现类对象
    • 4、创建Thread类对象,构造方法传递Runnable接口的实现类对象
    • 5、调用Thread的start方法,开启线程的run方法

两种方式对比,Runnable方式的好处

  • 1、避免了单继承的局限性
  • 2、增强了程序的扩展性,减低了程序的耦合性(解耦)

常用方法

  • String getName(): 获取该线程的名称
  • static Thread currentThread() : 返回当前正在执行的线程对象
  • void setName(String name): 设置线程名称
  • public static void sleep(long mills) : 使当前线程暂时暂停执行,时间结束后继续执行
// 使用MyThread类
public class Demo01Thread {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        // 设置线程名称
        mt.setName("小贾");
        // 调用新线程
        mt.start();
        // 构造方法设置线程名称
        MyThread m = new MyThread("小一");
        m.start();
        for (int i = 0 ; i < 20 ; i++){
            System.out.println(i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
// MyThread类
package cn.jiatengda.day09.demo05;
public class MyThread extends Thread {
    public MyThread() {
    }
    public MyThread(String name) {
        super(name);
    }
    @Override
    public void run() {
       // 获取线程的名称
        System.out.println(getName());
        // System.out.println(Thread.currentThread().getName());
    }
}

// 使用Runnable接口匿名内部类的方式创建线程
package cn.jiatengda.day09.demo05;

public class Demo02Thread {
    public static void main(String[] args) {
        new Thread(){
            @Override
            public void run() {
                System.out.println("thread1");
            }
        }.start();
        //Runnable接口
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("thread2");
            }
        }).start();
    }
}

线程安全问题 : 多线程访问了共享的数据,会产生安全问题

线程安全问题是不能出现的,共享数据时,一个线程执行时不管是否失去执行权,其他共享想成就只能等待,保证使用一个线程运行

解决线程安全问题

  • 1.同步代码块
    • 1).通过代码块中的锁对象,可以使用任意对象
    • 2).但是必须保证多个线程中使用的锁对象是一个
    • 3).锁对象的作用: 把同步代码块锁住,只让一个线程在同步代码块中执行
  • 2.同步方法
    • 1). 也会把代码内部的代码锁住,只让一个线程执行,同步锁就是调用方法的对象,也就是this
    • 2). 静态同步方法的锁对象不能是this,因为this是创建对象之后产生的,静态方法优先于对象,静态方法的锁对象是本来写class文件对象
  • 3.Lock锁(一个接口)
    • 1.在成员位置创建一个ReentrantLock类对象
    • 2.在可能出现安全问题的代码前,调用Lock接口中的方法lock获取锁
    • 3.在可能出现安全问题的代码后,调用Lock接口中的方法unlock释放锁

      Lock接口中的方法

  • void lock() : 获取锁
  • void unlock() : 释放锁
public class Demo01ThreadSafe {
    public static void main(String[] args) {
        // 创建Runnable 实现类对象
        RunnableImpl r = new RunnableImpl();
        // 创建Thread类对象
        Thread t0 = new Thread(r);
        Thread t1 = new Thread(r);
        Thread t2 = new Thread(r);
        t0.start();
        t1.start();
        t2.start();
    }
}
//  使用锁解决卖票问题
package cn.jiatengda.day09.demo05;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class RunnableImpl implements Runnable {
    // 定义共享票源
    private int ticket = 100;
    // 创建锁对象
    Object obj = new Object();
    Lock l = new ReentrantLock();
    @Override
    public void run() {
        // 使用循环,让卖票重复执行
        while (true){
            synchronized (obj){
               //  l.lock();
                if (ticket > 0 ){
                    // 提高出现的几率 ,睡眠一下
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票");
                    ticket -- ;
                }
               // l.unlock();
            }
            // payTicket()
        }
    }
    public synchronized void payTicket(){
        if (ticket > 0 ){
            // 提高出现的几率 ,睡眠一下
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票");
            ticket -- ;
        }
    }
}

等待唤醒机制:就是在一个线程进行了规定操作后,就进入等待状态(wait()), 等待其他线程执行完他们的指定代码过后 再将其唤醒(notify())。

等待唤醒机制是管理多个线程执行顺序方案,上述案例是多个线程抢占资源,而等待唤醒机制可以让我们操作多个线程的执行先后顺序

  • wait() : 进入一直等待状态,知道被唤醒
  • wait(long l) : 计时等待 和sleep 一样,时间结束自然唤醒
  • notify() : 唤醒等待时间长的一个线程
  • notifyAll() : 唤醒所有的此对象上等待的线程

文章作者: Jia
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Jia !
  目录