Java学习笔记

语法学习

java命名规范
规范示例
1、项目名全部小写.helloworld
2、包名全部小写.com.test.pack
3、类名首字母大写,其余组成词首字母依次大写.(大驼峰)class ComputeSum()
4、变量名,方法名首字母小写,如果名称由多个单词组成,除首字母外的每个单词的首字母都要大写.(小驼峰)int allNumber
5、常量名全部大写.PI, MAX_NUM
java数据类型
名称属性
byte8位,有符号整数
short16位,有符号整数
int32 位,有符号整数
long64位,有符号整数
float单精度,32位浮点数
double双精度,64位浮点数
boolean一位,true/ fasle
char一个单一的 16 位 Unicode 字符
Java修饰符

像其他语言一样,Java可以使用修饰符来修饰类中方法和属性。主要有两类修饰符:

访问控制修饰符 : default, public , protected, private
非访问控制修饰符 : final, abstract, static, synchronized

Java 变量

Java 中主要有如下几种类型的变量

  • 局部变量:类的方法中的变量。
  • 类变量(静态变量):独立于方法之外的变量,用 static 修饰
  • 成员变量(非静态变量)[又叫实例变量]:独立于方法之外的变量,不过没有 static 修饰。
Java Number类–装箱与拆箱
包装类基本数据类型
Booleanboolean
Bytebyte
Shortshort
Integerint
Longlong
Characterchar
Floatfloat
Doubledouble
Java String 类
  • String 创建的字符串存储在公共池中,而 new 创建的字符串对象在堆上
  • String 类是不可改变的,所以你一旦创建了 String 对象,那它的值就无法改变了(详看笔记部分解析)。如果需要对字符串做很多修改,那么应该选择使用 StringBuffer & StringBuilder 类。
  • 获得其长度的方法: str.length()
Java StringBuffer 和 StringBuilder 类

在这里插入图片描述
当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。

StringBuilderStringBuffer
速度快,非线程安全速度慢,线程安全
Java 数据结构

Java工具包提供了强大的数据结构。在Java中的数据结构主要包括以下几种接口和类:

  • 枚举(Enumeration)
  • 位集合(BitSet)
  • 向量(Vector)
  • 栈(Stack)
  • 字典(Dictionary)
  • 哈希表(Hashtable)
  • 属性(Properties)
Java ArrayList

ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。ArrayList 继承了 AbstractList ,并实现了 List 接口。
在这里插入图片描述

import java.util.ArrayList;
List<E> objectname = new ArrayList<E>();//初始化,E表示泛型数据,**只能为引用数据类型**
指令代码
add(i)
remove(i)
get(i)
set(i, j)
大小list.size()
排序Collections.sort(list)
元素索引indexOf()

ArrayList进行排序,如果是升序直接Collection.sort(),降序需要实现Comparator接口。

        Collections.sort(intList,new Comparator<Integer>(){

            @Override
            public int compare(Integer o1,Integer o2){
                // 返回值为int类型,大于0表示正序,小于0表示逆序
                return o2-o1;
            }
        });

ArrayList遍历输出

import java.util.*;

public class Main{
    public static void main(String[] args) {
        List<String> list=new ArrayList<String>();
        list.add("Hello");
        list.add("World");
        list.add("HAHAHAHA");
        //第一种遍历方法使用 For-Each 遍历 List
        for (String str : list) {            //也可以改写 for(int i=0;i<list.size();i++) 这种形式
            System.out.println(str);
        }

        //第二种遍历,把链表变为数组相关的内容进行遍历
        String[] strArray=new String[list.size()];
        list.toArray(strArray);
        for(int i=0;i<strArray.length;i++) //这里也可以改写为  for(String str:strArray) 这种形式
        {
            System.out.println(strArray[i]);
        }

        //第三种遍历 使用迭代器进行相关遍历

        Iterator<String> ite=list.iterator();
        while(ite.hasNext())//判断下一个元素之后有值
        {
            System.out.println(ite.next());
        }
    }
}

ArrayList 中的元素实际上是对象,在以上实例中,数组列表元素都是字符串 String 类型。如果我们要存储其他类型,而 只能为引用数据类型,这时我们就需要使用到基本类型的包装类。

基本类型对应的包装类表如下:

基本类型引用类型
booleanBoolean
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
ArrayList<Integer> li=new Arraylist<>();     // 存放整数元素
ArrayList<Character> li=new Arraylist<>();   // 存放字符元素
实际操作
选择赋值:
num = temp == used ? num1 : num2;//如果temp等于used则赋值num1,否则赋值num2;
创建一个动态列表:
List<int[]> merged = new ArrayList<int[]>(); 
merged.add(new int[]{a, b});//对动态列表添加一个一维的数组的列表
merged.get(merged.size() - 1)[0] // 取列表中最后一个数组的第一维
merged.toArray(new int[merged.size()][]);//将动态列表转换为二维数组

Object[] array = list.toArray(); //动态列表->数组
int[] array = {3,8,5,65,34,27};
System.out.println(array.toString()); //打印出来是地址
System.out.println(Arrays.toString(array));//打印出来是数组
System.out.println(array[1]);//打印出来是数字
System.out.println(Arrays.toString(array)); 

Collections.reverse(wordList);//将一个字符串列表进行翻转

//二维动态列表
List<List<Integer>> rooms = new List<List<Integer>>(); //二维动态列表使用get 去访问;
rooms.get(i).get(j);
创建一个二维数组
boolean[][] dp = new boolean[N][N];
int[][] out = new int[N][N];//创建一个NxN的二维数组
for (int i = 0; i < n; i++) {
	System.out.println(Arrays.toString(out[i]));
}
复制一个数组
要使用这个方法,首先要import java.util.*;
Arrays.copyOfRange(T[ ] original,int from,int to)
将一个原始的数组original,从下标from开始复制,复制到上标to,生成一个新的数组。
注意这里包括下标from,不包括上标to。
对二维数组进行排序:
Arrays.sort(nums);//对一维数组进行排序
//按照二维数组的第一维进行升序排序
//方法一:
int[][] intervals;
Arrays.sort(intervals, new Comparator<int[]>() {
	public int compare(int[] interval1, int[] interval2) {
		return interval1[0] - interval2[0];
	}
});

//方法二:
Arrays.sort(intervals, Comparator.comparingInt(o -> o[0]));//按行排序
Arrays.sort(interval, Compaarator.comparingInt(o -> o[1]));//按列排序
创建一个链表 LinkedListLinked

List sites = new LinkedList();

指令代码
add(i)
remove(i)
get(i)
set(i, j)
大小sites.size()
包含contains(Object o)
元素索引indexOf()
创建一个Collection
1.添加
add(Object obj)
addAll(Collection coll)

2.获取有效元素的个数
int size()

3.清空集合
void clear()

4.是否是空集合
boolean isEmpty()

5.是否包含某个元素
boolean contains(Object obj):通过equals方法来创建是否是同一对象
boolean containsAll(Collection c):调用元素的equals方法进行比较,两个集合之间的对比

6.删除
boolean remove(Object obj):只会删除找到的第一个元素
boolean removeAll(Collection coll):取当前集合的差集

7.取两个集合的交集
boolean retainAll(Collection c):把交集的结果存在当前的集合中,不影响c

8.集合是否相等
boolean equals(Object obj)

9.转换成数组
Object[] toArray()

10.获取集合的Hashcode()
11.遍历:iterator()
创建一个集合:
Set<Integer> row = new HashSet<Integer>();//创建一个集合
row.add(x)//加入集合
row.contains(x);//查找集合
row.remove(x);//删除元素
row.clear();//清除集合
row.size();//计算大小
for(Class each: row) {	//打印集合
	System.out.print(each);
}
指令代码
add(i)
remove(i)
大小set.size()
包含contains(Object o)
字符串

String定义:

  • 通过字面量的方式定义:此时字符串值声明在字符串常量池中
  • 通过new去赋值,堆中有new结构,常量池中有字符串。字符串常量池中不会存储相同的内容
String[] str;//二维字符串
str[0].substring(0,i)//取字符串的某一段
str[0].charAt(i);//取某一个字符
str.length;//取当前字符串列表中字符串
str[0].length();//取当前字符串的个数
str = str.trim();//去处字符串前后的空格
List<String> wordList = Arrays.asList(str.split("\\s+"));//将字符串去掉中间空格,并变成列表,
//将数组与List列表链接起来:当更新其一个时,另一个自动更新
String.join(" ",wordList);//将字符列表转换为字符串
str1.substring(start, end).equals(str2);//比较两个字符串是否匹配;子串是左闭右开的;
//将字符串变为字符数组
String  node = new String("0000");
char[] str = node.toCharArray(); //String 变为字符数组
node = new String(arr); //将改变后的字符数组变为字符串
//比较两个字符串大小,返回前面减去后面的差值
int bigOrSmall = s1.compareTo(s2);
//测试字符串是否以指定前后缀结尾
boolean endsWith(String suffix)boolean startsWith(String prefix)boolean startsWith(String prefix, int toffset)

String- >StringBuffer(),StringBuilder(): 调用StringBuffer(),StringBuilder()构造器
StringBuffer(),StringBuilder()—>String构造器:调用String构造器或者toString()方法

StringBuffer 类

扩容问题:如果要添加的数据底层数组盛不下了,那就需要扩容底层的数组。默认情况下,扩容为原来容量的2倍 + 2,同时将原有数组中的元素复制到新的数组中。指导意义:开发中建议大家使用:StringBuffer(int capacity) 或 StringBuilder(int capacity), 创建的时候就指定容量;

        总结:
        增:append(xxx)
        删:delete(int start,int end)
        改:setCharAt(int n ,char ch) / replace(int start, int end, String str)
        查:charAt(int n )
        插:insert(int offset, xxx)
        长度:length();
        遍历:for() + charAt() / toString()
StringBuilder 类
String:不可变的字符序列。低层用char[] 存储。final修饰
StringBuffer:可变的字符序列:线程安全的,效率低;低层用char[] 存储
StringBuilder:可变的字符序列:线程不安全的,效率高;低层用char[] 存储
//当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。
StringBuilder res = new StringBuilder();
res.append("abc");//尾接字符串
res.insert(0,"de");//插入字符
res.delete(5, 8);//删除位置5-8

StringBuffer()String()创建的对象不能进行对比,因为是不同的类。只有相同的类中不同对象才能进行==对比。
创建一个正无穷的数
int ans = Integer.MAX_VALUE;
创建一个二维列表
List<List<Integer>> ans = new ArrayList<List<Integer>>();//创建二维整数列表
ans.get(i).get(j) = 1; // 对二维列表内的数进行访问修改
/*列表的访问使用get()函数, 数组[ ]的访问使用[i]*/

列表之间不能相互直接赋值,直接赋值=获得的是一个指针
List<Integer> ans = new ArrayList<Integer>();
List<Integer> last = new ArrayList<Integer>();
ans = last; //错误
last = new ArrayList<Integer>(ans);//正确

创建一个StringBuffer

StringBuffer ret = new StringBuffer();
ret.append(1);//增加元素
ret.toString();//StringBuffer转变String;
创建一个队列
Queue<Integer> queue = new LinkedList();//创建
queue.offer(1);//加入元素
queue.offer(2);
queue.poll()//出栈
queue.getFirst()//返回第一个元素
queue.getLast();//返回最后一个元素

队列常用作BFS中记录之前走过的位置,可以使用一维队列记录二维位置;
Queue<Integer> neighbers = new LinkedList<>();
neighbers.add(i);
row = i / n;
col = i % n;
创建一个栈
//1.创建栈
Stack<Integer> s = new Stack<>();
//2. Push new element.
s.push(5);
// 3. Check if stack is empty.
if (s.empty() == true) 
// 4. Pop an element.
s.pop();
//5.返回栈顶元素
s.peek()
//6.获取栈的大小
s.size()
创建哈希表:
Map<Integer, Integer> visited = new HashMap<>();
visited.get(i);//哈希表中键值i 对应的值
visited.put(i, j);//增加键为i, 键值为j的键值对
visited.containsKey(i); //查找键为i是否存在;
visited.containsValue(i); //查找键值为i是否存在;
visited.remove(i);//移除键为i的键值对;
visited.size()
visited.keySet()、visited.values()、 visited.entrySet()//遍历元素
指令代码
visited.put(i, j)
visited.remove(i)
visited.get(i)
visited.put(i, j)
大小visited.size()
遍历元素visited.keySet()、visited.values()、 visited.entrySet()
查找键visited.containsKey(i)
查找键值visited.containsValue(i)
entrymap.entrySet(); entry.getKey(); entry.getValue()

Map的遍历

import java.util.*;

public class Main{
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<String, String>();
        map.put("1", "value1");
        map.put("2", "value2");
        map.put("3", "value3");

        //第一种:普遍使用,二次取值
        System.out.println("通过Map.keySet遍历key和value:");
        for (String key : map.keySet()) {
            System.out.println("key= "+ key + " and value= " + map.get(key));
        }

        //第二种
        System.out.println("通过Map.entrySet使用iterator遍历key和value:");
        Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, String> entry = it.next();
            System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
        }

        //第三种:推荐,尤其是容量大时
        System.out.println("通过Map.entrySet遍历key和value");
        for (Map.Entry<String, String> entry : map.entrySet()) {
            System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
        }

        //第四种
        System.out.println("通过Map.values()遍历所有的value,但不能遍历key");
        for (String v : map.values()) {
            System.out.println("value= " + v);
        }
    }
}
容易混淆的使用
  • 字符串用length()方法, 数组长度使用length; 列表、队列使用size();
  • 字符串查找用charAt(i), 列表、哈希表用get(i);
类型互转

string 和 int 互相转化:

String  => Int
int i = Integer.parseInt([String]);//将一个字符串
i = Integer.parseInt([String],[int radix]);//将字符串(多少进制)转换为数字
Integer.valueOf([String]);//将一个字符转换为数字

Int => String
String s = String.valueOf(i);
String s = Integer.toString(i);
String s = "" + i;

//String -> char数组
 char[] ch=sc.nextLine().toCharArray();
 // char数组->String 
 String str = String.valueOf(ch)


//String 与 byte[]之间的转换
//String - > byte[]:调用 String的getBytes()
String str1 = "abc123";
byte[] bytes = str1.getBytes();//使用默认的字符集进行转换
str1.getBytes("gbk");//设置编码方式:gbk两个字符一个汉字,UTF-8:三个字符一个汉字
System.out.println(Arrays.toString(str1));//打印字符数组,转换为字符串

//String -> others1)String.valueOf(boolean b) :boolean 变量 b 转换成字符串 
(2)String.valueOf(char c) :char 变量 c 转换成字符串 
(3)String.valueOf(char[] data) :char 数组 data 转换成字符串 
(4)String.valueOf(char[] data, int offset, int count) :char 数组 data 中 由 data[offset] 开始取 count 个元素 转换成字符串 
(5)String.valueOf(double d) :double 变量 d 转换成字符串 
(6)String.valueOf(float f) :float 变量 f 转换成字符串 
(7)String.valueOf(int i) :int 变量 i 转换成字符串 

//others -> String1byte : Byte.parseByte(String s) : 将 s 转换成 byte2)Byte.parseByte(String s, int radix) : 以 radix 为基底 将 s 转换为 byte ,比如说 Byte.parseByte("11", 16) 会得到 173double : Double.parseDouble(String s) : 将 s 转换成 double4float : Double.parseFloat(String s) : 将 s 转换成 float5int : Integer.parseInt(String s) : 将 s 转换成 int6long : Long.parseLong(String s)


装箱与拆箱

Java为每种基本数据类型都提供了对应的包装器类型; 它相当于将基本类型“包装起来”,使得它具有了对象的性质,并且为其添加了属性和方法,丰富了基本类型的操作。 核心:让基本类型具备对象的特征,实现更多的功能.

  • 装箱的时候自动调用的是Integer的valueOf(int)方法。
  • 拆箱的时候自动调用的是Integer的intValue方法
Integer i = 10;  //装箱 Integer.valueOf(10);
int n = i;   //拆箱 Integer.intValue(10);
  • "=="与equals的区别:
    当 "= ="运算符的两个操作数都是 包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。另外,对于包装器类型,equals方法并不会进行类型转换。
  • 在通过valueOf方法创建Integer对象的时候,如果数值在[-128,127]之间,便返回指向IntegerCache.cache中已经存在的对象的引用;否则创建一个新的Integer对象。链接
Java 《就业求职手册》
编程基础

1.区分实例成员和静态成员

  • 实例成员: 对象的 实例变量 + 实例方法:只能通过对象引用访问或调用
  • 实例变量::在类的实例化时才分配字段,为非静态变量;
  • 实例方法:隶属于类实例的方法,相同类的所有对象共享该方法的实现;
  • 静态成员:隶属于类的 静态方法 + 静态变量, 静态成员可以通过类名和对象引用加以访问;
  • 静态变量:载入类的时候就需要进行分配的变量,静态变量隶属于类,但是不属于该类的任何对象。叫做类变量或者静态变量;
  • 静态方法:隶属于类但是不属于该类的任何对象的方法,叫做类方法;可以通过类名和对象引用加以访问;

2.java中对象是如何传递消息的?

  • 对象通过调用彼此的实例方法来传递消息

3.静态变量就只有静态方法可以进行改变,非静态方法不能进行操作。

4.开发原则

  • java代码必须封装到类中
  • java 有两种值:对象的引用和元数据类型
  • 引用代表根据类创建对象
  • 对象只能通过引用加以操作
  • java中对象不能包含其他对象,对象只能拥有指向其他对象的引用
语言基础
  1. Java标识符的第一字母不能为数字,区分大小写。
  2. Java关键字都是小写,then, sizeof,NULL,String都不是Java关键字;
  3. 对象引用类型的缺省值为null,而原始类型的缺省值与具体的类型有关。
  4. Java的8个原始类型:boolean, char, byte, short, int, long, float. double,.String 是对象;
    变量的默认初始化值:
类型默认初始化值
byte0
short0
int0
long0
float0.0
double0.0
booleanfasle
char\u0000
对象null

注意:对象的默认初始值为null, 但是系统没有创建过一个相应的对象,只是创建了 一个可以存储对象引用的变量;
5. Java中可以使用Unicode值来表示字符型值;
6. Java源文件包括下结构: package包, import内容, 类型声明;
7.main()方法是Java程序的入口,方法必须是public static void类型,方法必须接受一个字符串数组的参数。main()不能抛出异常,要么处理,要么不处理。

运算符基础
  1. 使用二进制的补码形式表示正负数, 正数负数相互转换使用的是 取反加一;
  2. 运算符优先级:
优先级运算符
1[],()
2++(后缀), --(后缀)
3++(前缀), --(前缀), !,~, instanceof

4.对于原始数据类型,int,long,char等,按值传递; 如果传递的是指向对象的引用,那么就会传递引用值;
5.final

  • 用来修饰一个引用

    • 如果引用为基本数据类型,则该引用为常量,该值无法修改;
    • 如果引用为引用数据类型,比如对象、数组,则该对象、数组本身可以修改,但指向该对象或数组的地址的引用不能修改。
    • 如果引用时类的成员变量,则必须当场赋值,否则编译会报错。
  • 当使用final修饰方法时,这个方法将成为最终方法,无法被子类重写。但是,该方法仍然可以被继承。

  • 当用final修改类时,该类成为最终类,无法被继承。简称为“断子绝孙类”。
    6.日期类型转换

  • Date 类:

  • 构造器两种:

    • Date now = new Date(); //当前时间的对象
    • Date now = new Date(145631541563L); //创建指定毫秒数的Date对象
Date now = new Date(); //创建一个日期时间对象
System.out.println(now.toString());//输出日期,被重写了toString();
long nowlong = now.gettime();//获得当前时间,以毫秒计算;
DateFormat df = DateFormat.getDateInstance();//获得当前的时间格式
String s = df.formaet(nowlong);//将当前时间以字符串形式输出;
申明和访问控制

1.数组中的每一个元素必须是同一类型的, 如果需要一个结构存储不同类型的元素,可以选择collection类;
2.数组的申明不需要分配任何存储空间,一个数组的大小将在数组使用new关键字真正创建。

int num[][];
int[][] num;//两种写法都对
int[]num[]//前面则呢帽写都对
注意: new[][]的时候,第一个值必须有,第二个可以没有;
在一个数组的引用申明的时候。不需要指定数组的大小,数组的大小总是同数组的实例,而不是数组的引用相关联的;例子如下:
int i[] = new int[2]{1,2}//错误
int i[] = new int[] {1,2};//正确,程序会自行确定个数
- 数组大小不能改变

3.static是修饰符,可以通过“类名.方法名”来引用该方法
4.类的声明只能是public, abstract和final;
5.如果一个类包含一个抽象方法,那么这个类为抽象类
6.成员修饰符–Java类成员的可访问性修饰符

修饰符同一类中同一包中不同包中的子类不同包中的非子类
private √ √ XXX
friendly(缺省) √ √ √ √ XX
protected √ √ √ √ √ √ X
public √ √ √ √ √ √ √ √
  • 构造器可以有任何访问的修饰(private, public, protected),但是没有返回值,也不需要void 修饰;
  • 构造器通常使用和类相同的名字,通常以大写字母开始。
  • 构造器是不能被继承的,子类可以继承超类的任何方法;
  • 父类没有构造方法, 但是子类在继承的时候必须调用父类的某个构造方法,否则会引发异常;
    8.构造方法总是调用super()方法,除非第一行是以下内容:
  • super();
  • super(args);
  • this();
  • this(args)
  • 明确使用上述的调用就不会使用默认的super()方法,this()调用同一类中的另一个构造方法,这个方法称为“显示构造方法调用”
  • this关键字主要有三个应用:
      this调用本类中的属性,也就是类中的成员变量;
      this调用本类中的其他方法;
      this调用本类中的其他构造方法,调用时要放在构造方法的首行。
  • 父类中有public draw(),子类中重新定义了draw(),那么只会调用子类的draw();
  • 父类中有private draw(), 那么只会调用父类的draw();
流程控制、异常处理和断言

1.swicth 表达式必须为byte
2. do{} while 循环体内部至少循环一次
3. 异常处理的基础类:(暂时还看不太懂,看完视频后再来)
在这里插入图片描述
Java把异常作为一种类,当做对象来处理。所有异常类的基类是Throwable类,两大子类分别是Error和Exception。这些异常类可以分为三种类型:系统错误、异常和运行时异常。系统错误由Java虚拟机抛出,用Error类表示。Error类描述的是内部系统错误,例如Java虚拟机崩溃。这种情况仅凭程序自身是无法处理的,在程序中也不会对Error异常进行捕捉和抛出。

异常处理的5个关键字:try、catch、throw、throws和finally

  • try:可能会抛出多个异常
  • catch:参数类似与方法的申明, 包含一个异常类型和异常对象; 可以有多个catch语句,分别处理不同类型的异常, 父类异常可以包含子类异常,所以catch 语句一般都是从特殊到一般依次排序。
  • finally:无论是否有异常,finally块中的代码总是会被执行的。 finally语句在执行关闭资源的语句时非常有用。
  • throws:总是出现在一个函数头,声明一个异常,告知方法调用者。
  • throw: 抛出一个异常,至于该异常被捕获还是继续抛出都与它无关。

4.断言 assert:开发的时候使用, 发布的时候一般不使用;
断言是通过关键字assert来定义的,一般的,它有两种形式。
开启断言功能:java -ea AssertionTest

  • assert ; 比如 boolean isStudent = false; assert isStudent;

  • assert : ; 比如 boolean isSafe = false; assert isSafe : “Not Safe at all”;

面向对象
  • 封装
    指的就是将描述某种实体的数据和基于这些数的操作集合到一起,形成一个封装体。封装的思想保证了类内部数据结构的完整性,使用户无法轻易直接操作类的内部数据,这样降低了对内部数据的影响,提高了程序的安全性和可维护性。

  • 继承
    继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例变量和方法,或类从父类继承方法,使得子类具有父类相同的行为。并可以修改或者增加新的方法使之适合自己的需要。特点:

    • 继承鼓励类的重用
    • 继承可以多层继承
    • 一个类只能继承一个父类, 使用关键字extends
    • 父类中private修饰的不能被继承
    • 构造方法不能被继承
      多重继承的初始化顺序:
      在这里插入图片描述
  • 多态:多态指的就是在应用程序中出现的“ 重名 ” 现象。多态性允许以统一的风格编写程序,以处理种类繁多的已存在的类及其相关类。这样既降低了维护难度,又节省了时间。使用多态实现思路:
      1.编写父类
      2.编写子类,子类重写父类方法
      3.运行时,使用父类的类型,子类的对象

  • 一个类不能同时被声明为abstract和final; abstract是用来被继承的, final是不能被继承的。

  • 面向对象中继承和接口都是 "is a "的关系。

public interface A{
	public void method();
}

public class B implements A{
	public void method(){
		...
	}
}
  • 方法重载(一个类中所有方法)

    • 方法名相同
    • 方法的参数类型、参数个数不相同
    • 参数列表、返回值、方法修饰符等都可以不相同
  • 方法覆盖(父类与子类之间)

    • 子类方法的名称、参数名和返回值类型必须与父类相同
    • 子类方法不能缩小父类方法的访问权限
    • 子类方法不能抛出比父类更多的异常,(默认有抛出RuntimeException)
    • 父类的静态方法不能被子类覆盖为非静态方法
    • 子类可以定义为与父类的静态方法同名的静态方法,以便在子类中隐藏父类的静态方法
    • 父类的非静态方法不能被子类覆盖为静态方法
    • 父类的抽象方法可以被子类覆盖:
      • 子类实现父类的抽象方法
      • 子类重新申明父类的抽象方法
    • 父类的非抽象方法可以覆盖为抽象方法
  • 方法重载和方法覆盖的相同点:

    • 都要求方法名相同;
    • 都可以用于抽象方法和非抽象方法之间。
  • 方法重载和方法覆盖的不同点:

    • 方法覆盖要求参数签名必须一致,而方法重载则要求参数签名必须不一致;
    • 方法覆盖要求返回类型必须一致,而方法重载对此不做限制;
    • 方法覆盖只能用于子类覆盖父类的方法,方法重载用于同一个类的所有方法(包括从父类中继承来的方法);
    • 方法覆盖对方法的访问权限和抛出的异常有特殊的要求,而方法重载在这方面没有任何限制;
    • 父类的一个方法只能被子类覆盖一次,而一个方法在所在的类中可以被重载多次。
  • super 与 this

    • super(参数),调用基类的某一构造函数(应该为构造函数中的第一条语句,可不显示写出)
    • this(参数),调用本类中另一种形成的构造函数(应该为构造函数中的第一条语句,可不显示写出)
    • super:引用当前对象的直接父类中的成员
    • this:表示当前对象名
    • 同一构造函数中,super和this不能同时出现;
    • 子类构造函数中不总是以super()作为第一句。调用super()可能不成功,因为父类可能没有默认的构造函数。
对象生存周期
  • 垃圾回收:引用计数法
    一个对象,可以有一个或者多个引用变量指向它,当一个对象不再有任何一个引用变量指向它时,这个对象就被抛弃了,或者说这个对象就被垃圾回收机制回收了。

  • 垃圾回收器一般只在有对象要回收且系统需要回收的时候才运行,用户无法知道准确的垃圾回收时间。

  • 干预垃圾回收:

    • 将指向某一对象的所有引用变量全部移除
    • 使用库方法 System.gc()
  • finalize()方法

    • 一个对象在运行时,可能会有一些东西与其相关联,因此当对象即将被销毁时,需要做一些善后工作,可以把这些操作写在finalize()里面。
    • 当JVM拦截收集器调用一个合适对象的finalize()方法时,会忽略任何由finalize()方法抛出的异常。其他情况下finalize()方法抛出的异常同普通方法处理是一样的。
  • 初始器:表达式型变量初始器;代码块型变量初始器;代码块型实例初始器

    • 表达式型变量初始器
    • 代码块型静态初始器:只会在初始化类的时候执行一次,不属于任何方法,关键字 this 和 super不能出现在代码块型静态初始器。
    • 代码块型实例初始器:提供了在对象创建期间初始化变量的能力,作用和对象创建期间的构造函数一样。可以使用关键词this和super, 初始器按照顺序执行,之后再执行构造器。
  • final类型的变量在申明的时候必须初始化

Java的多线程机制

一个具有生命的线程有哪些状态:

  • 创建状态(New):实例化Tread对象,但是没有调用start()方法时的状态。

  • 就绪状态(Runnable):线程有资格运行但是调度程序还没有把它选为线程运行时所处的状态。当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;

  • 运行状态(Running):从就绪状态池中被选择为当前执行的线程所处的状态。注:就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;

  • 等待、阻塞或者睡眠状态(Blocked):线程是活的,但是缺少运行的条件,一旦具备了条件就可以转为就绪状态。
    1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;

    2.同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;

    3.其他阻塞 – 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

  • 死亡状态(Dead):一个线程的run()方法运行结束,它的栈结构将解散。但是该线程仍然是一个Tread对象,仍可被引用,一旦线程死亡就不能再被重新启动了。
    在这里插入图片描述
    线程的创建:

  • 继承java.lang.Tread类,重写该类的run()方法:在Tread类中所有的操作都是从run()方法开始的,并且在run()中编写独立线程的执行代码。没有参数的run()方法总是自动被调用的,而带参数的run()是被重载的,必须显示调用。

class TreadTest extends  Tread{
	public void run(){
		System.out.println();
	}
	public void run(String s){
		System.out.println(s);
	}
	public static void main(String[] args){
		TreadTest tt = new TreadTest();	
		tt.start()//使之进入到就绪状态
		tt.run("hello world!")}
}
  • 实现java.lang.Runnable接口:Java是单继承的,继承了Tread类就不能继承其他类了。实现Runnable接口,并重写该接口的run()方法,该run()方法同样是线程执行体,创建Runnable实现类的实例,并以此实例作为Thread类的target来创建Thread对象,该Thread对象才是真正的线程对象。
class MyRunnable implements Runnable {
    private int i = 0;

    @Override
    public void run() {
        for (i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }
}

public class ThreadTest {
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
            if (i == 30) {
                Runnable myRunnable = new MyRunnable(); // 创建一个Runnable实现类的对象
                Thread thread1 = new Thread(myRunnable); // 将myRunnable作为Thread的target创建新的线程
                Thread thread2 = new Thread(myRunnable);
                thread1.start(); // 调用start()方法使得线程进入就绪状态
                thread2.start();
            }
        }
    }
}
  • 线程的启动学要调用start()方法,只有这样才能创建新的调用栈,直接调用run()方法就不会创建新的调用栈,run()就会和其他普通的方法没有区别。
线程同步

线程同步指多个线程同时访问某资源时, 采用一系列的机制以保证同时最多只有一个线程访问该资源。

  • 共享变量:多个线程在同一程序中有用,必须实现线程间相互通信或共享结果,最简单的方法就是使用共享变量。
  • 线程与同一进程中其他线程共享相同的进程上下文,包括内存空间。但是必须保证线程以受控方法访问共享变量,以免他们之间相互干扰对方的更改。
  • 为确保可以在线程之间以受控方式共享数据,Java提供了两个关键字synchronized和volatile。

synchronized有两个重要的含义:

  • 一次只有一个线程可以执行代码的受保护部分
  • 一个线程更改的数据对于其他线程时可见的

volatile:当一个变量被声明为volatile,任何对该变量的写操作都会绕过高速缓存,直接写入主内存,读取也是一样,直接取自主内存。所有线程在任何时候看到的volatile变量值都相同。

Java锁定:可以保护许多代码块或方法, 每次只有一个线程可以持有锁。 每个Java对象都有一个相关的锁,同一时间只能有一个线程持有Java锁.当线程进入synchronized 代码块时,线程会阻塞并等待,直到锁可用.当线程处于就绪状态时,并且获得锁后,将执行代码块,当控制退出受保护的代码块,即到达了代码块末尾或者抛出了没有在synchronized 块中捕获的异常时,它就会释放该锁.代码块有锁并不表示两个线程不能同时执行该代码块,只表示如果两个线程正在等待相同的锁,则他们不能同时被执行该代码。

同步的方法: 创建synchronized 块的最简单方法是将方法声明成synchronized.这表示在进入方法主体之前,调用者必须获得锁.示例代码如下

public class Point{
    public synchronized void setXY(int x,int y){
        this.x = x;
        this.y = y;
    }
}

对于普通的synchronized方法,这个锁是一个对象,将针对它调用方法,对于静态的synchronized方法,这个锁是与Class对象相关的监控器,在该对象中声明了方法。setXY()方法被声明成了synchronized ,并不表示两个不同的线程不能同时执行setXY()方法,只要它们调用不同Point实例的setXY()方法,就可同时执行.对于一个Point实例,一次只能有一个线程执行setXY()方法,或Point的任何其他synchronized方法.

同步的块 synchronized块的语法比synchronized方法稍微复杂一点,因为还需要显式地指定锁要保护哪个块.

public class Point{
    public synchronized void setXY(int x,int y){
        synchronized (this) {
            this.x = x;
            this.y = y;
        }
    }
}

使用this引用作为锁很常见,但这不是必须的.使用this引用作为锁表示该代码与这个类中的synchronized方法使用一个锁.由于同步防止了多个线程同时执行一个代码块,因此性能上就有问题,即使是在单处理器的系统上,也最好在尽可能小的需要保护的代码上使用同步.访问基于堆栈的局部变量从来不需要受到保护,因此它们只能受到自己所属的线程访问.

大多数类并没有同步:因为同步会带来小小的性能损失,大多数通用类,如java.util中的Collection类,不在内部使用同步,这表示在没有附加同步的情况下,不能在多个线程中使用诸如HashMap这样的类.

Thread类的常用方法如下所示

start();//启动线程

getId();//获得线程ID

getName();//获得线程名字

getPriority();//获得优先权

isAlive();//判断线程是否活动

isDaemon();//判断是否守护线程

getState();//获得线程状态

sleep(long mill);//休眠线程

join();//等待线程结束

yield();//放弃cpu使用权利,运行状态变为准备状态

interrupt();//中断线程

currentThread();//获得正在执行的线程对象

守护线程:也叫精灵线程,当程序只剩下守护线程的时候就会退出。

守护线程 的作用类似在后台静默执行,比如JVM的垃圾回收机制,这个就是一个守护线程。而非守护线程则不会。

线程优先级:1~10,数字越大,表明线程的等级越高。默认为NORMAL_PRIORITY = 5;

线程调度规则

  • 如果两个或是两个以上的线程都修改一个对象,那么把执行修改的方法定义为同步的(Synchronized),如果对象更新影响到只读方法,那么只读方法也应该定义为同步的.

  • 如果一个线程必须等待一个对象状态发生变化,那么它应该在对象内部等待,而不是在外部等待,它可以调用一个被同步的方法,并让这个方法调用wait()方法.

  • 每当一个方法改变某个对象的状态时,它应该调用notifyAll()方法,这给等待队列的线程提供机会,来看一看执行环境是否已发生改变.

  • 记住wait(),notify(),notifyAll()方法都属于Object类,而不是Thread类,仔细检查看看是否每次都有相应的notify()或notifyAll()方法,且它们作用于相同的对象.

说明:在Java中每一个类都有一个主线程,要执行一个程序,那么这个类当中,一定要有main()方法,main()方法是Java类中的主线程.自己创建线程有两种方法,一种是继承Thread类,另一种是实现Runnable接口.一般情况下,最好避免继承,因为Java中是单继承的,如果继承Thread类,则无法再继承其他的类.

基础类
  • java.lang是java的基础包,在编译时,会被自动导入源文件。包含Object类和包装类、常用接口和异常内容。

  • 1)对于==,一般比较的是值是否相等

    • 如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等;
    • 如果作用于引用类型的变量,则比较的是所指向的对象的地址
  • 2)对于equals方法,一般为比较内容是否相同

    • 如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;
    • 诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。
  • "=="对于基本数据类型的变量,如:Byte(字节型)、short(短整型)、char(字符型) 、int(整型)、float(单精度型/浮点型)、long(长整型)、double(双精度型) 和boolean(布尔类型), ==是直接对其值进行比较。
    对于引用数据类型的变量,则是对其内存地址的比较

  • equals:我们知道任何类都继承Object类,在没有重写equals方法之前,equals方法里是直接调用==,因此实质上与==没有差别。但是要注意:equals方法不能作用于基本数据类型的变量,这是因为基本数据类型非对象的缘故,没有方法。然而,在一些类库当中这个方法被重写了,比如String 、Date、ArrayList等类,这样就不在是去比较在堆内存中的存放地址了

  • toString()返回该对象的字符串表示, toString()方法会打印类名和对象的内存位置。几乎每个类都会覆盖该方法。

  • hashCode()将对象在内存中的地址转换为int类型并返回。如果两个对象是equal的,那么它们的hashCode也是equal的,即hashCode返回的整数值必须相同。如果重写了euqals,那么hashCode一般也要重写。

  • 针对同一个对象,new出来的字符串和直接赋值给变量的字符串存放的位置是不一样的,前者是在堆里面,而后者在常量池里面。

  • java.util:实用工具类库,提供了一些实用的方法和数据结构、日期、日历,随机数类、堆栈、向量、位集合以及哈希表等类来表示相应的数据结构。

  • 集合类:在数组中可以存储原始数据类型变量和对象,但是Java集合中只能呢个存放对象,如果一定要存放变量,必须将变量转换为对应的对象实例。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值