Java学习记录

Java基础学习笔记01

Java语言简介

Java应用层序运行机制

Java是解释型和编译型语言

Java程序组成:源文件、字节码、机器码

*.java【源文件】 > 编译【JVM】 > .class【字节码文件】 > 解释【JVM】 > 机器码

  • 会涉及到JVM调优问题

搭建Java开发环境

【2014年 】Java提供了JDK1.8版本,并且支持Lambda表达式,可以使用函数式编程

JRE指的是Java运行时环境,也就是说只提供程序的解析功能,不提供程序的开发功能,如果有JDK环境,会自动进行JRE的更新处理

初始Java开发

CLASSPATH环境属性

SET CLASSPATH=盘符:\目录

JVM解释程序的时候需要得到CLASSPATH的支持

对于CLASSPATH 还是应该采用默认设置方式,设置如下:

【全局属性【用户变量】】SET CLASSPSTH=. 或者在环境变量中 ,设置CLASSPATH为"."

PATH和CLASSPATH区别?

  • PATH:是操作系统提供的路径配置,定义所有【Java】可执行程序的路径

  • CLASSPATH:是由JRE提供,用于定义Java程序解释时类加载路径

    ​ JVM → CLASSPATH定义的路径 →加载字节码文件

Java数据类型简介

基本分为两大类:

  • 基本数据类型:具体的数字单元 → 1,1.1,。。。
    • 数值型:
      • 整型:byte、short、int、long; → 默认值:0
      • 浮点型:float,double; → 默认值:0.0
    • 布尔型:boolean; → 默认值:false
    • char; → 默认值:'\u0000'
  • 引用数据类型:牵扯到内存关系的使用
    • 数组、类、接口。 → 默认值:null

int范围 -2147483648~2147483647

最大值加上1 等于最小值

最小值加上1 等于最大值

超过最大保存范围会出现一个问题叫 数据溢出

public class Test{
	public static void main(String args[]){
		int num = 2147483647;
		//num += 2L;
		long x = 2L;
		System.out.println(num);
		System.out.println(num + x);
		System.out.println(num + 2);
		System.out.println(num + 20);
	}
}
/*
2147483647
2147483649
-2147483647
-2147483629
*/

解决数据溢出

在操作的时候预估数据范围,范围不够就使用更大范围

数据类型转换

数据类型 变量名称 = 常量

Java运算符

【++i】、【--i】:先进行变量的自增或者自减,然后再进行数字的计算;

  • ​ 先加1或者减1,然后再与其他变量或者数字进行运算

【i++】、【i--】:先使用变量进行计算,然后再进行自增或者自减

  • 先与其他变量或者数字进行运算,然后再加一或者减一
"=="	与	equals()的区别
==:是引用比较,比较的是内存地址,
equal():是值比较,比较两个值是否相同

三目(赋值)运算符

关系运算 ? 关系满足时的内容 : 关系不满足时的内容

面试题 :请解释&和&&、|和||的区别?

  • &和|两个运算符可以进行位运算和逻辑运算;

&&:再若干个条件下,如果前面返回false,后面就不用判断

||:再若干个条件下,如果前面返回false,后面接着判断,直到满足条件

循环控制

break; 结束循环,break; 之后的代码不执行

continue: 严格来将只是结束当前的一次调用(结束当前循环)

循环嵌套:乘法口诀

public class Test{
	public static void main(String [] args){
		for (int x = 1; x <= 9; x++) {
			for (int y = 1; y <= x; y++) {
				System.out.print(y + "*" + x + "=" + (x*y) + " ");
			}
			System.out.println();
		}
	}
}

循环嵌套:三角形

public class Test{
	public static void main(String [] args){
		int line = 5;
		for (int x = 0;x <line ;x++) {
			for (int y = 0; y < line - x; y++) {
				System.out.print(" ");
			}
			for (int y = 0; y <= x; y++) {
				System.out.print("* ");
			}
			System.out.println();
		}
	}
}

while循环

要修改循环条件

while循环 与 do...while循环的最大差别:

​ while循环是先判断后执行,而 do ... while先执行一次后判断;

方法的定义

方法的本质就是方便使用者重复调用

方法重载

方法名相同,参数类型不同、参数个数不同,

方法的递归调用

方法的递归调用指的是一个方法自己调用自己的情况

但是处理不当,会造成栈溢出

1604480318546

补充

System.out.println(sum); 本身就是方法的重载,

可以传入不同的数据类型,

System.out.println(1);
System.out.println(1.1);
System.out.println(true);
...

while 和 for 选择条件?

  • 在明确循环次数的情况下,优先选择for
  • 不知道循环次数,但明确结束循环条件,选择while

i++ 和 ++i命令有什么区别?

++i 是先加后赋值;

i++ 是先赋值后加;

2、

i++ 是先使用i,在赋值计算;在计算程序时,先把 i 的值拿来用,然后后再自增1;

++i 是先赋值计算,再使用;再计算程序时,先自增2,然后拿来用;

3、

i++ 返回原来的值,++i返回加1后的值

int i = 1;
int s = ++i;
int x = i++;
/*
	解析:
		i++改变的只有i。
		++i既改变i,也改变赋值变量
*/

测试题集

第一次75分

错题

1604483458913

C

【num *2 ++】 先让num参与运算,之后再自增

num = num ++ *2 ;

num ++ 只会改变 num的值,并不会改变等号前面的num值,

1604483481181

D

1604483496701

死循环

没有修改循环条件

如果加了【修改循环条件】

那么 因为 x>0 进入了一个死循环

1604483525046

B

image-20201109090809031

D

第二次80分

image-20201109090829971

执行满足条件的 case 'A' num++ 此时 num=11

但没有break; 接着往下执行,又有 num++ ,此时num =12;直到有break; 跳出分支

D

1604488976574`

第一次 循环的时候,x=0;sum=0;而sum += x=0;[sum + sum +x;] ,0%3 =0

所以选 B

1604489988426

B

int i = 1;

int j = i++;

这个时候, i++ 改变的只有i, 即 j = 1;i=2

++j ,先自增再去运算, 所以这个时候++j = 2

这个时候 (i=(++j)) ==2

i==(++j)是先j自增在比较 所以这个时候j=2

(i++)==j 是先比较再自增 所以这个时候i还是等于2,比较完成之后i变成3

第三次

1604491153397

B

1604491171078

B

代码

public class recursion {
	public static void main(String args[]) {
		
		System.out.println(rs(1));
	}
	
	static int count = 0 ;
	public static int rs(int num) {
		if (num <= 100) {
			count += num;
			rs(num + 1);
		}
		return count;
	}
}

Java基础学习笔记02

类与对象

面向对象简介

多态:在范围内可以处理的特征,【类型的转换处理】

  • OOA:面向对象分析
  • OOD:面向对象设计
  • OOP:面向对象编程

类和对象

类是对象的抽象,对象是类的载体

类是抽象概念,对象是具体的产物

声明对象 :类对象 对象名称 = null

实例化对象:对象名称 = new 类名称()

对象内存分析

Java之中 类属于引用数据类型

范例:

public class JavaDemo {
	public static void main(String args[]) {
		Person p = new Person();
		p.name="111";
		p.age=18;
		p.tell();
	}
}

​ 如果要进行内存分析,那么首先给出两块最为常见的内存空间

  • 堆内存:保存的是对象的具体信息;* 在程序中堆内存空间的开辟是通过 new 完成的;
  • 栈内存:保存的是一块堆内存的地址;即:通过地址找到堆内存,而后找到对象内容;

image-20201108175004759

new 对象,会 再堆内存 开辟一块新的堆空间

1604551574696

image-20201108212349511

per严格来将(它描述的是一个栈,是一个地址)per.name 其实是这个地址中的name ,堆内存中的name 变成“张三”

构造方法与匿名对象

构造方法

  • 不允许有返回值类型
public 类名(){}	无参
public 类名(参数类型1 参数名1 ...){}		有参

通过构造方法 实现 实例化中的属性的初始化处理,关键字new 的时候 使用构造方法

匿名对象

  • 只创建对象,但没有把对象地址赋值给某个变量【没有变量名的对象】
new 类名(参数);

注意一个匿名对象,只能使用一次,【多次调用,会产生内存垃圾,堆垃圾】

匿名对象可以作为方法的参数或者返回值

public static void main(String args[]){
    input(new Persion(参数));
}
public static void input(Persion ps){
    ...
}

作为返回值

public static void main(String args[]){
    getPerson();
}
public static Person getPerson(){
    return new Person(参数);
}

引用类型

同一块堆内存 被不同栈内存 引用

this 关键字

  • 当前类中的属性:this.属性
  • 当前类中的方法:(普通方法、构造方法)this.()、this.方法名称
  • 描述当前对象:

this调用本类方法

public Person(){
    System.out.println("1");
}
public Person(String name){
    this();		//调用无参构造方法
    this.name = name;
}
public Person(String name ,int age){
    this(name);	//调用单参构造方法
    System.out.println("1");
    
}

注意:

  • 构造方法必须在实例化新对象的时候调用。所以 “this()” 必须放在首行

static定义的方法或属性 公共属性

代码块

普通代码块

在方法中,进行结构拆分,防止相同变量名称所带来的相互影响

{
    ......
}

构造块

定义在类中

构造块会优先于构造方法执行,并且每次实例化对象的时候都会执行构造块

class Person {
    public Father(){
    	System.out.println("构造方法");
	}
    {
        System.out.println("构造代码块");
    }
}

public

静态代码块 定义在主类中,和非主类

优先于 构造块 执行,且只执行一次,主要目的是为类中的静态属性初始化

同步代码块【在多线程的时候会涉及到】

数组

二维数组

定义语法

  • 数据类型 数组名称 [] [] = new 数据类型 [行个数] [列个数];
  • 数据类型 数组名称 [] [] = new 数据类型 [] [] {{1,2,3},{4,5,6},{7,8,9}};

二维数组就是数组的嵌套使用

维数组要分两次

		int data [][] = new int [][]{
			{1,2,3},{4,5,6},{7,8,9}
		};
		
		for (int temp [] : data) {
			System.out.println(temp);
			for (int num : temp) {
				System.out.print(num + ",");
			}
			System.out.println();
		}

第一次foreach 遍历出二维数组的内存地址

[I@7852e922
[I@4e25154f
[I@70dea4e

第二次foreach 遍历出每个 内存地址的变量

数组与方法

最大、最小、平均、求和

部分代码

给最大值、最小值、定义一个初始值 0

public ArrayUtil(int[] data) {
        for (int x = 0; x < data.length; x++) {
            if (data[x] > max) {
                //如果数组第x索引的值 大于 max,将当前数组索引的值赋值给max
                this.max = data[x];
            }
            if (data[x] < min) {
                //。。。
                this.min = data[x];
            }
            this.sum += data[x];
        }
        this.avg = this.sum / data.length;
    }

数组排序

public class ArrayDemo {
	public static void main(String args[]) {
		int data [] = new int [] {9,3,6,19,99,56,36,45,97};
		for (int n = 0;n < data.length; n++) {
			for (int m = 0; m < data.length - n - 1; m ++) {
				if (data[m] > data[m + 1]) {
					int temp = data[m];
					data[m] = data[m + 1];
					data[m + 1] = temp;
				}
			}
		}
		printArray(data);
	}
	public static void printArray(int temp []){
		for (int x = 0; x < temp.length; x++) {
			System.out.print(temp[x] + "、");
		}
		System.out.println();
	}
}

注意

主方面里面尽量不要涉及到大量代码,

将业务需求逻辑 写在方法里面,

**数组反转 **

需求:将 1,2,3,4,5,6,7,8,9===>9,8,7,6,5,4,3,2,1

public class ArrayDemo {
	public static void main(String args[]) {
		int data [] = new int [] {1,2,3,4,5,6,7,8,9};
		//数组长度是 9
		int temp [] = new int [data.length];
		//但是 数组脚标,也就是索引是从0开始的,数组长度-1即是数组索引长度,
		int foot = temp.length - 1 ;
		for (int x = 0; x < data.length ;x++) {
			temp[foot-- ] = data[x];
			/*
			foot-- 在赋值完后会自减
				8	1
				7	2
				..	..
			*/
		}
		data = temp;
		System.out.println(data);
		printArray(data);
	}
	public static void printArray(int temp []){
		for (int x = 0; x < temp.length; x++) {
			System.out.print(temp[x] + "、");
		}
		System.out.println();
	}
}

* 会存在 产生垃圾问题,data = temp data将不会指向任何堆内存

第二种

相对来说,第二种更好一点

public class ArrayDemo {
	public static void main(String args[]) {
		int data [] = new int [] {1,2,3,4,5,6,7,8,9};
		int center = data.length / 2;	//交换次数 4
		int head = 0;	//脚标
		int tail = data.length - 1;		//脚标
		for (int x = 0; x < center ;x++) {
			int temp = data [head];
			/*
			第一次	data[0]	8
						9	1
			第二次	data[1]	7
						8	2
				...
				
			*/
			//System.out.println("temp:" + temp);
			data [head] = data[tail];
			//System.out.print("data [head]:" + data [head]);
			//System.out.print("data [tail]" + data [tail]);
			data [tail] = temp;
			head++;
			tail--;
		}
		printArray(data);
	}
	public static void printArray(int temp []){
		for (int x = 0; x < temp.length; x++) {
			System.out.print(temp[x] + "、");
		}
		System.out.println();
	}
}

数组拷贝

System.arraycopy(源数组,源数组开始点,目标数组,目标数组开始点,拷贝长度);

对象数组

1604569526882

String 类 特点分析

String这个类里面定义了一个数组,所以字符串的每一个字符 都是 保存在数组中;

但是,应该清楚,既然包装的是数组,那么字符串的内容是无法改变的

赋值

直接赋值 只会产生一个实例化对象,并且可以自动保存到对象池中,字符串重用

&&

构造方法赋值 ==> 会产生两个实例化对象,不会自动入池

程序中 没有字符串这种常量,只有String类匿名对象

String类对象两种实例化方式比较

String strA = "xxx";
String strB = "xxx";
System.out.println(strA == strB); //true

为什么会返回true,是因为在Java程序底层提供一个字符串池(字符串数组)

1604574853442

构造方法赋值

1604574921001

String类对象两种实例化方法比较

String内存分析

1604575123039

第一种方式:这种方法创建字符串后会有一个字符串池来保存内容,而栈内存存的就是这个字符串的地址,在作为函数参数时是值传递

第二种方式:这种方法在创建字符串对象的同时,还会在字符串池中创建一个对象,在传递时是引用传递

范例:

String a="hello";
a="java";
输出a=java

a变成java,并不是hello改成java,而是又创建了一个对象,内容为Java,只是将a指向hello变为a指向java

String常量池

静态常量池

1604575651293

运行时常量池

String info = "123";
String str = "456" + info + "789";

String不可修改

1604576368179

字符串常量没有发生过任何改变,改变的只是String类对象的引用,但是会带来垃圾空间。

String 类 常用方法

字符串比较

equals() 区分大小写

equalsIgnoreCase() 不区分大小写

public boolean equals(Object anObject) 字符串比较(区分大小写)
public boolean equalsIgnoreCase(String anotherString) 字符串比较 (不区分大小写)
public int compareTo(String anotherString) 字符串大小比较(区分大小写)
public int compareToIgnoreCase(String str) 字符串大小比较(不区分大小写)

compareTo

public class JavaDemo {
	public static void main(String args[]) {
		String strA = "A";	//A=65
		String strB = "a";	//a=97
        //前面的 减去 后面的
		System.out.println(strA.compareTo(strB));	//-32
	}
}

字符串查找

方法名称 作用
1 public boolean contains(CharSequence s) 判断子字符串是否存在 返回true/false
2 public int indexOf(String str) 从头查找指定字符串的位置 返回第一个匹配的索引
3 public int indexOf(String str, int fromIndex) 从指定位置区间查找指定字符串的位置 返回指定位置区间的第一个匹配的索引
4 public int lastIndexOf(int ch, nt fromIndex) 从指定位置区间由后向前查找指定字符串的位置 返回指定位置区间的第一个匹配的索引
5 public int lastIndexOf(String str) 由后向前查找指定字符串的位置 返回第一个匹配的索引
public boolean startsWith(String prefix) 判断是否以指定的字符串开始
public boolean startsWith(String prefix, int toffset) 由指定位置判断是否以指定的字符串开始
public boolean endsWith(String suffix) 判断是否以指定的字符串结束

范例:

public class JavaDemo {
	public static void main(String args[]) {
		String strA = "wanut.you";
		String strB = "hello";
		System.out.println(strA.indexOf("u",5));	//8
	}
}

字符串替换

方法名称 作用
public String replaceAll(String regex, String replacement) 全部替换 匹配的参数,替换的参数
public String replaceFirst(String regex, String replacement) 替换首个匹配字符串

范例:

public class JavaDemo {
	public static void main(String args[]) {
        //替换全部
		String strA = "wanut.you";
		String strB = "hello";
		System.out.println(strA.replaceAll("u","A"));	//wanAt.yoA
        
        //替换首个匹配
        String strA = "wanut.you";
		String strB = "hello";
		System.out.println(strA.replaceFirst("u","A"));	//wanAt.you
	}
}

字符串拆分

方法名称 作用
public String[] split(String regex,int limit) 按照指定的字符串拆分为指定个数
public String[] split(String regex) 按照指定的字符串全部拆分(从哪里开始分割)

范例:

public String[] split(String regex,int limit) 按照指定的字符串拆分为指定个数

public class JavaDemo {
	public static void main(String args[]) {
		String strA = "wanut.you";
		String strB = "hello";
        //以.为分割线,开始拆分,并且数组最大长度是2
        //【\\.】转义,两个斜杠 ,表示一个斜杠,斜杠也要转义,用斜杠转义,
		String result[] = strA.split("\\.",2);
		for (String x : result) {
			System.out.println(x);
		}
	}
}
/*
wanut
you
*/

全部 拆分

public class JavaDemo {
	public static void main(String args[]) {
		String strA = "wuut.you";
		String strB = "hello";
		String result[] = strA.split("u");
		for (String x : result) {
			System.out.println(x);
		}
	}
}
/*
w

t.yo
*/

字符串截取

方法名称 作用 示例
public String substring(int beginIndex) 从指定索引开始(包括)截取到字符串结尾 "unhappy".substring(2) returns "happy"
public String substring(int beginIndex,int endIndex) 从指定索引开始(包括)截取到指定索引结束(不包括) "smiles".substring(1, 5) returns "mile"

范例: public String substring(int beginIndex)

public class JavaDemo {
	public static void main(String args[]) {
		String strA = "wanut.you";
		String strB = "hello";
        System.out.println(strA.substring(5));	//.you
	}
}

范例: public String substring(int beginIndex,int endIndex)

截取的字符串 包括开始索引的字符串,但不包括结束索引的字符串

public class JavaDemo {
	public static void main(String args[]) {
		String strA = "wanut.you";
		String strB = "hello";
        System.out.println(strA.substring(1,5));	//anut
	}
}

通过indexOf()计算 ,截取字符串

public class JavaDemo {
	public static void main(String args[]) {
		String strA = "admin-photo-张三.jsp";
		String strB = "hello";
		int begin = strA.indexOf("-",strA.indexOf("photo")) + 1;
		int end = strA.indexOf(".");
        System.out.println(strA.substring(begin,end));//张三
	}
}

字符串格式化

方法名称 作用
public static String format(String format,各种类型... args) 根据指定结构进行文本格式化xianshi

范例:格式化日期 年-月-日

JavaDoc文档简介

处理中文的时候 往往使用char类型,其可以包含中文数据;

方法的覆写

保留父类的方法,优化父类的功能

权限:

public > default > private

公共的 默认 私有的(访问权限最小)

面试题:解释Override 与 OverLoading 区别?

区别 Overloading Override
重载 覆写
方法名称相同,方法类型和个数不同 方法名称、参数类型、参数个数、返回值相同
父类方法加上private,就不是覆写了,不可见

面试题:super 与 this 区别?

  • this就近选择本类查找,然后再去父类查找,super只去父类查找

  • this 与 super 都可以进行构造方法的调用,但是this() 调用的是本类构造,super() 调用的是父类构造,必须方法构造方法的首行,不能同时出现

  • this 可以表示本类当前对象

final

可以使用 final 关键字实现 定义不能够被继承的类,定义不能够被覆写的方法、常量;

综合案例(继承分析)

案例(字符串统计)

class StrignUtil {
    public static int[] count(String str){
        //定义两个长度的数组
        int countData[] = new int [2];
        //将字符串变为字符数组
        char[] data = str.toCharArray();
        for (int i = 0; i < data.length; i++) {
            if (data[i] == 'n' || data[i] == 'N') {
                countData[0]++ ;
            }
            if (data[i] == 'o' || data[i] == 'O') {
                countData[1]++ ;
            }
        }
        return countData;
    }
}
public class JavaDemo {
    public static void main(String args][]){
        String str = "want you to know on thing";
        int result[] = StringUtil.count(str);
        System.out.println(result[0]);
        System.out.println(result[1]);
    }
}

用继承的方式实现 字符串统计

class StringUtil {
    //保存字符串
    private Strin content;
    private StringUtil(String content) {
        this.content = content;
    }
    public String getContent() {
        return this.content;
    }
    //默认的信息返回	默认返回全部字符串
    public String getInfo() {
        return this.getContent();
    }
}

class StringCount extends StringUtil{
    //统计 n 的个数
    private int nCount;
    //统计 o 的个数
    private int oCount;
    public StringCount(String content) {
        super(content);
        this.countChar();
    }
    public void countChar() {
        char[] data = super.getContent().toCharArray();
        for (int i = 0; i < data.length; i++) {
            if (data[i] == 'n' || data[i] == 'N') {
                this.nCount++ ;
            }
            if (data[i] == 'o' || data[i] == 'O') {
                this.oCount++ ;
            }
        }
    }
    public int getNCount(){
        return this.nCount;
    }
    public int getOCount(){
        return this.oCount;
    }
    public String getInfo() {
        return "n个数:" + this.nCount + "、o个数:" + this.oCount;
    }
}

public class JavaDemo {
    public static void main(String args[]) {
        StringCount sc = new StringCount("want you to know on thing");
        sc.getInfo();
    }
}

数组操作(继承分析)

//数组操作类
class Array {
	//定义一个整型数组
	private int[] data ;
	//进行数组索引控制
	private int foot;
	public Array(int len) {
		if (len > 0) {
			//开辟数组
			this.data = new int[len];
		}else {
			//开辟一个空间
			this.data = new int[1];
		}
	}
	//实现数组的容量扩充,给出的是扩充大小;实际大小=已有大小 + 扩充大小	
	public void increment(int num) {
		int newData[] = new int[this.data.length + num];
		//System.arraycopy(源数组,源数组开始点,目标数组,目标数组开始点,拷贝长度);
		System.arraycopy(this.data,0,newData,0,this.data.length);
		//改变数组引用 
		this.data = newData;
	}
	//数组增加
	public boolean add(int num) {
		//如果 索引大小 小于 数组大小 有位置
		if (this.foot < this.data.length) {
			this.data[this.foot++] = num;
			return true;
		}
		return false;
	}
	public int[] getData() {
		return this.data;
	}
	private int getFoot() {
		return this.foot;
	}
}
//定义排序子类
class SortArray extends Array {
	public SortArray(int len) {
		super(len);
	}
	public int[] getData() {
		//获得排序结果
		java.util.Arrays.sort(super.getData());
		return super.getData();
	}
}
//定义反转子类
class ReverseSortArray extends Array {
	public ReverseSortArray(int len) {
		super(len);
	}
	public int[] getData() {
		//获得反转结果
		int center = super.getData().length / 2;
		int head = 0;
		int tail = super.getData().length - 1;
		for (int  i = 0; i < center; i++) {
			//用temp 接收 data[0] 值
			int temp = super.getData()[head];
			//data[0]的值 = data[7]的值 交换位置
			super.getData()[head] = super.getData()[tail];
			super.getData()[tail] = temp;
			head++;
			tail--;
		}
		return super.getData();
	}
}
public class JavaDemo {
	public static void main(String args[]) {
		ReverseSortArray arr = new ReverseSortArray(5);
		System.out.println(arr.add(5));
		System.out.println(arr.add(52));
		System.out.println(arr.add(54));
		System.out.println(arr.add(533));
		System.out.println(arr.add(53));
		arr.increment(3);
		System.out.println(arr.add(13));
		System.out.println(arr.add(19));
		System.out.println(arr.add(12));
		int result[] = arr.getData();
		for (int temp : result) {
			System.out.print(temp + "、");
		}
	}
}

多态

多态概念:

有两种实现多态的方式

方法的多态

  • 覆写
  • 重载

对象的多态性:

  • 对象向上转型 父类 父类实例 = 子类实例 自动完成转换
  • 对象向下转型 子类 子类实例 = (子类)父类实例 强制转换

对象向上转型

解决参数统一问题

发生对象的向下转型之前一定要首先发生向上转型,会出现“ClassCastExecuption”异常 ,

向下转型的主要意义:为了使用子类的特殊定义

抽象类概念

使用 abstract 关键字定义,并且没有提供方法体的方法,

注意

  • 抽象类使用很大程度上有一个核心的问题:抽象类自己无法直接实例化
  • 抽象类之中主要的目的是进行过渡操作使用,要使用抽象类进行开发的时候,往往都是解决类继承问题时所带来的代码重复处理

抽象类 可以有构造方法

包装类

基本数据类型并不是一个类,如果想要将基本数据类型以类的形式进行处理,那么就要对其进行包装

装箱 是向上转型

拆箱 是向下转型

class Int {
    //包装了一个基本数据类型
    private int data;
    private Int(int data) {
        this.data = data;
    }
    public int intValue() {
        return this.data;
    }
}
public class JavaDemo {
    public static void main(String args[]) {
        //装箱:将基本类型数据保存在包装类对象中
        Object obj = new Int(10);
        //拆箱:从包装类对象中获取基本数据类型
        int x = ((Int)obj).intValue();
    }
}

Number 类

接口

interface 关键字 定义接口

接口无法直接产生实例化对象,所以接口需要被子类实现 implements

接口对象 可以利用子类对象的向上转型进行实例化;

接口定义加强

以前:

1604648113876

但是从JDK1.8之后开始,在接口中允许定义普通方法

接口中的普通方法必须追加 default 声明,该操作属于挽救功能,并不能作为涉及的首选

范例:

interface IMessage{
	public String getInfo();
	public default boolean connect(int num) {
		if (num > 0) {
			System.out.println("消息通道建立");
			return true;
		}
		return false;
	}
}
class IMessageImpl implements IMessage {
	public String getInfo() {
		return "IMessageImpl消息通道已建立连接";
	}
}

public class JavaDemo {
	public static void main(String args[]) {
		IMessage im = new IMessageImpl();
		if (im.connect(3)) {
			System.out.println(im.getInfo());
		}
	}
}

工厂设计模式(Factory)

1604650516385

客户端不会直接关注子类 ,将由工厂类去调用,

客户端只需要关心吃什么,不用去关心怎么做出来的。

子类扩充的时候只需要修改Factory类 即可实现。

interface Phone {
    void make();
}
class MiPhone implements Phone {
    public MiPhone() {
        this.make();
    }
    public void make() {
        System.out.println("制造小米手机");
    }
}
class HWPhone implements Phone {
    public HWPhone() {
        this.make();
    }
    public void make() {
        System.out.println("制造华为手机");
    }
}
class FactoryPhone {
    public static Phone makePhone(String phoneType) {
        if (phoneType.equlas("MiPhone")) {
            return new MiPhone();
        }else if (phoneType.equals("HWPhone")) {
            return new HWPhone();
        }
        return null;
    }
}
public class JavaDemo {
    public static void main(String args[]){
        FactoryPhone fp = FactoryPhone.makePhone("MiPhone");
        
    }
}

代理设计模式(Proxy)

interface IEat {
	void get();
}
class EatReal implements IEat {
	public void get() {
		System.out.println("【真实主题】3、得到一份食物,开始吃");
	}
}

class EatProxy implements IEat {	//代理对象
	private IEat eat;		//为吃而服务
	public EatProxy(IEat eat) {	//代理项
		this.eat = eat;
	}
	public void prepare() {		//准备过程
		System.out.println("【代理主题】1、购买食材");
		System.out.println("【代理主题】2、处理食材");
	}
	public void get() {
		this.prepare();
		this.eat.get();
		//System.out.println("this.eat.get()==" + this.eat.get());
		this.clear();
	}
	public void clear() {
		System.out.println("【代理主题】4、清洁卫生");
	}
}
public class JavaDemo {
	public static void main(String args[]) {
		IEat eat = new EatProxy(new EatReal());
		eat.get();
	}
}

代理设计模式的特点:一个接口提供有两个子类,其中一个子类是真实业务操作类,另一个子类是代理业务操作类,没有代理业务操作,真实业务无法操作。

1604653404580

抽象类与接口的区别

抽象类 接口
abstract class 类名称{} interface 类名称{}
子类通过 extends 可以继承一个抽象类 接口不允许继承抽象类,但是允许继承多个父接口

泛型

把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型;

参数化类型:

  • 把类型当作参数传递
  • <数据类型>只能是引用类型

泛型通配符

<?> 可以接收所有的类型,并且只允许获取;

泛型接口

第一种

interface IMessage<T> {
    public String echo(T t);
}
class MessageImpl<S> implements IMessage<S> {
    public String echo(S t) {
        return "【ECHO】" + t;
    }
}
public class JavaDemo {
    public static void main(String args[]) {
        IMessage<String> msg = new MessageImpl<String>();
        msg.echo("want.you");
    }
}

第二种实现方式

interface IMessage<T> {
    public String echo(T t);
}
class MessageImpl implements IMessage<String> {
    public String echo(String t) {
        return "【ECHO】" + t;
    }
}
public class JavaDemo {
    public static void main(String args[]) {
        IMessage<String> msg = new MessageImpl();
        msg.echo("want.you");
    }
}

泛型方法

格式:

修饰符 <T,S> 返回值类型 方法名(形参列表){
    //方法体
}

需求,将一个Object数组的所有元素添加到Collection集合中,

static void fromArrayToCollection(Object[] a, Collection<Object> c {
    for (Object o : a) {
        c.add(o);
    }
}

Collection不是Collection的子类,上面方法没有问题,

1604731776118

但是会由局限性,只能将Object[] 数组 添加到 Collection集合中;且元素只能是Object Collection<Object>

修改形式

static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
    for (T o : a) {
        c.add(o);
    }
}

包的定义及使用

生成jar文件

1、定义一个程序类

2、对程序进行编译与打包处理

  • 对程序打包编译:javac -d . 程序类.java

  • 此时会形成对应的包,包里面有相应的子包与*.class文件,将其打包为 mldn.jar jar -cvf mldn.jar 包名

    ​ -c :创建一个新的 jar 文件;

    ​ -v :的大一个详细输出;

    ​ -f : 设置也要生成的 jar 文件;本处定义的是 mldn.jar

3、每一个 *.jar文件都是一个独立的程序,如果想要运行,必须通过 CLASSPATH 进行配置

4、SET CLASSPATH =.;盘符:\目录\mldn.ajr

5、建立测试类,导入程序类(jar文件类)并调用方法 cn.mldn.util.类名) x = (new cn.mldn.util.类名)

6、编译程序类:javac -d . 类名

​ 解释程序:java 全类名

系统常用包

java.lang Stirng、Number、Object .....
java.lang.reflect 反射机制处理包
java.util 工具类的定义
java.io 输入与输出操作的程序包
java.net 网络程序开发的程序包
java.sql 数据库编程的开发包
java.awt、java.swing Java图形界面开发包GUI,awt属于重量级,swing属于轻量级

访问控制权限

关键字 同一包中的同一类 同一包中的不同类 不同包的子类 不同包的所有类
private (私有的)
default (默认的)
protected (受保护的)
public (公共的)

单例设计模式

定义枚举类

JDK1.5之后才提出枚举概念

主要作用是用于定义有限个数对象的一种结构

使用枚举可以在程序编译的时候就判断所使用的实例化对象是否存在

枚举本身就属于一种多例设计模式;

enum Color {
    RED,PINK;
}

Enum类

public abstract class Enum<E extends Enum<E>> extends Object 
implements Comparable<E>, Serializable
方法名称 作用
protected Enum(String name,int ordinal) 传入名字和序号 (由系统调用)
public final String name() 获得对象名字
public final int ordinal() 获得对象序号

异常的捕获及处理

throws关键字

声明 有异常的情况下 由调用处处理

throws 异常类	//声明出现异常,由异常类处理

throw关键字

主要作用在于 表示 手工进行异常的抛出

throw new RunntimeExecption();

自定义异常

实现自定义异常需要继承 Exception 或者 继承 RunntimeException

class BombExecption extends Exception {
    public BombExecption(String msg) {
        super(msg);
    }
}
class Food {
    public void eat(int num) throws BombExecption{
        if (num > 10) {
            throw new BombExecption("吃撑了");
        }else {
            System.out.println("继续吃");
        }
    }
}
public class JavaDemo {
    public static void main(String args[]) throws BombExecption{
        Food.eat(11);
    }
}

内部类

内部类的概念

内部类本身最大的缺陷在于破坏了程序的结构,但是破坏需要有目的的破坏

内部类的优点:轻松的访问外部类中的私有属性

范例:不用内部类

class Person {
    private String name = "want.you";
    public String getName(){
        return this.name;
    }
    public void fun() {
        Inner in = new Inner(this);
        in.print();
    }
}
class Inner {
    //Inner这个类对象实例化的时候 需要 Person类的引用
    private Person ps;
    public Inner(Person ps) {
        this.ps = ps;
    }
    public void print() {
        System.out.println(this.ps.getName());
    }
}
public class JavaDemo {
    public static void main(String args[]) {
        Person ps = new Person();
        ps.fun();
    }
}

折腾半天主要目的就是为了让 Inner 这个内部类可以访问Person 这个类中的私有属性

范例:使用内部类

class Person {
    private String name = "want.you";
    public void fun() {
        Inner in = new Inner();
        in.print();
    }
    class Inner {
        public void print() {
            System.out.println(Person.this.name);
        }
	}
}
public class JavaDemo {
    public static void main(String args[]) {
        Person ps = new Person();
        ps.fun();
    }
}

外部类.内部类 内部类对象 = new 外部类().new 内部类();

static定义内部类

如果说现在在内部类上使用了 static 定义,那么这个内部类就变为了 "外部类",static 定义的都是独立于类的结构,所以该结构就相当于是一个独立的程序类。

需要注意的是:static 定义的不管是类还是方法只能够访问 static 成员,所以 static 定义堆内部类只能够访问外部类的 static 属性或方法

外部类.内部类 内部类对象 = new 外部类.内部类();

class Person {
    private static final String name = "张三";
    static class Inner {
        System.out.println(Person.name);
    }
}
public class JavaDemo {
    public static void main(String args[]) {
        Person.Inner in = new Person.Inner();
    }
}

匿名内部类

interface IMessage {
    public void send(String str) ;
}
public class JavaDemo {
    public static void main(String args[]) {
        IMessage msg = new IMessage() {	//匿名内部类
            public void send(String str) {
                System.out.println(str);
            }
        };
        msg.send("want.you");
    }
}

函数式编程

Lambda表达式

interface IMessage {
    public void send(String str);
}
public class JavaDemo {
    public static void main(String args[]) {
        IMessage msg = (str) -> {
          System.out.println(str);  
        };
        msg.send("want.you");
    }
}

想要使用Lambda表达式,必须有一个重要的实现要求:SAM(Singlee Avstract Method) ,只有一个抽象方法,被称为函数式接口

函数式注解 @FunctionalInterface

Lambda表达格式:

  • 方法没有参数:()->{}
  • 方法有参数:(参数,参数)->{}
  • 如果现在只有一行语句返回:(参数,参数)->语句

1

IMessage msg = () -> {
          System.out.println(str);  
        };
        msg.send();

2

IMessage msg = (str) -> {
          System.out.println(str);  
        };
        msg.send("want.you");

3

interface IMessage {
    public void send(t1,t2);
}
IMessage msg = (t1,t2) -> return t1 + t2; 
msg.send(10,20);

方法引用

​ 引用数据类型最大的特点就是可以进行内存的指向处理,

方法引用通过方法的名字来指向一个方法。

方法引用可以使语言的构造更紧凑简介,减少冗余代码

从JDK1.8之后也提供方法的引用;即:不同的方法名称可以描述同一个方法

  • 引用静态方法:类名称 :: static方法名称;

  • 引用某个实例对象的方法:实例化对象 :: 普通方法;

  • 引用特定类型的方法:特定类 :: 普通方法;

  • 引用构造方法:类名称 :: new;

  • 构造器引用:它的语法是Class::new,或者更一般的Class< T >::new实例如下:

    final Car car = Car.create( Car::new ); final List< Car > cars = Arrays.asList( car );

  • 静态方法引用:它的语法是Class::static_method,实例如下:

    cars.forEach( Car::collide );

  • 特定类的任意对象的方法引用:它的语法是Class::method实例如下:

    cars.forEach( Car::repair );

  • 特定对象的方法引用:它的语法是instance::method实例如下:

    final Car police = Car.create( Car::new ); cars.forEach( police::follow );

内建函数式接口

功能型函数式接口

@FunctionalInterface
public interface Function<T,R> {
    R apply(T t);
}
  • 在String类中有一个方法判断是否以指定的字符串开头:public boolean startsWith(String prefix);

范例:

import java.util.function.*;
public class JavaDemo {
    public static void main(String args[]) {
        Function <String,Boolean> con = "**want" :: startsWith;
        con.apply("w");
    }
}

消费型函数式接口:只能进行数据的处理,没有返回值

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t)
}

范例:

import java.util.function.*;
public class JavaDemo {
    public static void main(String args[]) {
        Consumer <String> con = System.out :: println;
        con.accept("want.you");
    }
}

供给型函数式接口

  • 在String类中提供由转小写方法,这个方法没有接收参数,但是有返回值;

    • public String toLowerCase();
      
@FunctionalInterface
public interface Supplier<T> {
    T get();
}

范例:

import java.util.function.*;
public class JavaDemo {
    public static void main(String args[]) {
        Supplier <String> con = "System.out" :: toLowerCase;
        System.out.println(con.get());	//system.out
    }
}

断言型函数式接口:进行判断处理

  • 在String类中有一个equalsIgnoreCase()方法
@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}

范例:

import java.util.function.*;
public class JavaDemo {
    public static void main(String args[]) {
        Predicate <String> con = "System.out" :: equalsIgnoreCase;
        System.out.println(con.test("mldn"));	//false
    }
}

链表的定义与使用

链表的特点

  • 增删快,
  • 查询慢,需要遍历查找

链表实现原理

1、创建一个节点类,包含两个部分,数据源(要往节点里面存储的信息),引用域(相当于指针,指向下一个节点)。

2、创建一个链表类,包含三个属性:根节点、尾节点和大小。。还有增删改等方法

数据增加

interface ILink<E> {	//设置泛型避免安全隐患
	public void add(E e) ;
}
class LinkImpl<E> implements ILink<E> {
	//内部类
	private class Node {	//保存节点的数据关系
		private E data;		//保存的数据
		private Node next;	//保存下一个节点(引用)
		private Node(E data) {	//有数据的情况下才有意义
			this.data = data;
		}
		/*
		第一次调用:this=LinkImpl.root;
		第二次调用:this=LinkImpl.root.next;
		第三次调用:this=LinkImpl.root.next.next;
		*/
		public void addNode(Node newNode) {	//保存新的Node数据
			if (this.next == null) {	//当前节点的下一个节点为null
				this.next = newNode; //保存当前节点
			} else {
				this.next.addNode(newNode);
			}
		}
	}
	
	//定义Link类中的成员
	private Node root;		//保存根元素
	//定义Link类中的方法
	public void add(E e) {
		//保存的数据为空
		if (e == null) {
			return ;	//方法调用直接返回
		}
		Node newNode = new Node(e);	//创建一个新节点
		if (this.root == null) {	//没有根节点
			this.root = newNode;	//当前节点作为根节点
		} else {	//根节点存在
			this.root.addNode(newNode);	//将新节点保存在合适的位置
		}
	}
}
public class JavaDemo {
	public static void main(String args[]) {
		ILink<String> all = new LinkImpl<String>();
		all.add("hello");
		all.add("world");
		all.add("want");
		System.out.println(all);
	}
}

LinkImpl类只是负责数据的操作与根节点的处理,而所有后续节点的处理,全部都是由Node类(内部类)负责完成的

image-20201108150500105

根节点不存放任何数据,只是充当一个指向链表中真正存放数据的第一个节点的作用;每个节点都有一个next(相当于指针)引用,next引用指向下一个节点,直到最后一个节点,next指向null

获取链表数据长度:public int size()

1、在ILink接口 追加一个获取数据长度的方法

public int size();

2、在实现类LinkImpl类 追加一个统计个数的属性

public int count();

3、覆写该方法

public int size() {
		return this.count;
	}

判断空集合

1、在ILink接口 追加一个判断是否为空的方法

public boolean isEmpty();

2、在实现类LinkImpl类 覆写 该方法;

	public boolean isEmpty() {
		return this.root == null;
	}

返回链表数据

1、在ILink接口 追加一个返回数据的方法

public Object [] toArray();

2、在实现类 LinkImpl类 追加两个属性 :脚标 、返回的数据

private int foor;

private Object [] returnData;

3、在Node类中递归获取数据

/*
		第一次调用:this = LinkImpl.root
		第二次调用:this = LinkImpl.root.next
		第三次调用:this = LinkImpl.root.next.next
		*/
		public void toArrayNode() {
			LinkImpl.this.returnData [LinkImpl.this.foot ++] = this.data;
			if (this.next != null) {	//还有下一个数据
				this.next.toArrayNode();
			}
		}

4、进行数据返回的时候判断集合是否为空

if (this.isEmpty()) {	//空集合
			return null;		//返回空,没有数据
		}

根据索引获取数据

1、在ILink 接口中 追加方法

public E get(int index);	//根据索引获取数据

2、在Node类中 追加有根据索引获取数据的处理

public E getNode(int index) {
			if (LinkImpl.this.foot++ == index) {	//索引相同
				return this.data;	//返回当前数据
			} else {
				return this.next.getNode(index);
			}
		}

3、在LinkImpl 实现类 定义数据获取的实现方法

public E get(int index) {
		if (index >= this.count) {	//索引应该在指定的范围内
			return null;
		}	//索引数据的获取应该由Node类完成
		this.foot = 0;//重置索引的下标
		return this.root.getNode(index);
	}
interface ILink<E> {	//设置泛型避免安全隐患
	public void add(E e) ;
	public int size();	//获取链表数据长度
	public boolean isEmpty();
	public Object [] toArray();	//将集合以数组的形式返回 
	public E get(int index);
}
class LinkImpl<E> implements ILink<E> {
	//内部类
	private class Node {	//保存节点的数据关系
		private E data;		//保存的数据
		private Node next;	//保存下一个节点(引用)
		private Node(E data) {	//有数据的情况下才有意义
			this.data = data;
		}
		/*
		第一次调用:this=LinkImpl.root;
		第二次调用:this=LinkImpl.root.next;
		第三次调用:this=LinkImpl.root.next.next;
		*/
		public void addNode(Node newNode) {	//保存新的Node数据
			if (this.next == null) {	//当前节点的下一个节点为null
				this.next = newNode; //保存当前节点
			} else {
				this.next.addNode(newNode);
			}
		}
		/*
		第一次调用:this = LinkImpl.root
		第二次调用:this = LinkImpl.root.next
		第三次调用:this = LinkImpl.root.next.next
		*/
		public void toArrayNode() {
			LinkImpl.this.returnData [LinkImpl.this.foot ++] = this.data;
			if (this.next != null) {	//还有下一个数据
				this.next.toArrayNode();
			}
		}
		public E getNode(int index) {
			if (LinkImpl.this.foot++ == index) {	//索引相同
				return this.data;	//返回当前数据
			} else {
				return this.next.getNode(index);
			}
		}
	}
	
	//定义Link类中的成员
	private Node root;		//保存根元素
	public int count;		//保存数据个数
	private int foot;		//操作数组的脚标
	private Object [] returnData;	//返回的数据保存
	//定义Link类中的方法
	public void add(E e) {
		//保存的数据为空
		if (e == null) {
			return ;	//方法调用直接返回
		}
		Node newNode = new Node(e);	//创建一个新节点
		if (this.root == null) {	//没有根节点
			this.root = newNode;	//当前节点作为根节点
		} else {	//根节点存在
			this.root.addNode(newNode);	//将新节点保存在合适的位置
		}
		count ++;
	}
	public int size() {
		return this.count;
	}
	public boolean isEmpty() {
		return this.root == null;
	}
	public Object[] toArray() {
		if (this.isEmpty()) {	//空集合
			return null;		//返回空,没有数据
		}
		this.foot = 0;	//脚标清零
		this.returnData = new Object[this.count];	//根据已有的数据长度创建数组
		this.root.toArrayNode();//利用Node类进行递归数据获取
		return this.returnData;
	}
	public E get(int index) {
		if (index >= this.count) {	//索引应该在指定的范围内
			return null;
		}	//索引数据的获取应该由Node类完成
		this.foot = 0;//重置索引的下标
		return this.root.getNode(index);
	}
}
public class JavaDemo {
	public static void main(String args[]) {
		ILink<String> all = new LinkImpl<String>();
				System.out.println(all.size() + "、链表是否为空" + all.isEmpty());
		all.add("hello");
		all.add("world");
		all.add("want");
		System.out.println(all.size() + "、链表是否为空" + all.isEmpty());
		Object result [] = all.toArray();
		for (Object obj : result) {
			System.out.print(obj + "、");
		}
		System.out.println();
		System.out.println("根据索引获取数据:" + all.get(0));
		System.out.println(all.get(1));
	}
}

测试题集

第一次 70分

1604649607403

1604649668125

C compilation error in line NO.5 第五行出现编译错误 不兼容类型,Object无法转为float

1604649708652

C

1604649721872

C

1604649758867

A

1604649770843

C 函数式接口 ,内建函数式接口

1604742746381

A

1604743095915

A 不能在方法里面 声明一个 static 的变量

1604743164074

C

image-20201108225812506

A

image-20201108225910956

image-20201108225948914

posted @ 2020-11-07 12:45  san只松鼠  阅读(150)  评论(0)    收藏  举报