java基础复习

JAVA入门

一、认识java

1.计算机语言

机器语言

机器语言是由“0”和“1”组成的二进制数,是一串串由”0“和”1“组成的指令序列,可将这些指令序列交给计算机执行。

汇编语言

用一些简洁的、有一定含义的英文字符串来替代特定指令的“0”、“1”序列。

高级语言

C语言:既有高级语言的特点,又有汇编语言的特点

C++:具有面向对象特性的C语言

C#:是一种面向对象的、运行于.NET Framework之上的高级程序设计语言

Java:Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征

2、JDK

1.简介
  • java程序的运行依赖一个环境,对开发来说需要安装jdk(java Development kit),此工具包中包含在开发过程用到所朋基础类库、编译、运行命令、打包等; 对开发好的程序,只需要一个运行环境即可(JRE --Java Runtime Environment)

  • 通过查看jdk的安装路径,得出在安装JDK时,会自带一个JRE

  • 安装要求:装在非中文目录下

JDK与JRE的区别

jdk:Java Development Kit 开发环境 ,JRE Java Runtime Environment 运行环境;jdk中是包含jre的;

测试jdk是否安装成功:

C:\Program Files\Java\jdk1.8.0_231\bin
cmd + 回车
java -version (在安装路径可以得到java的版本)

如果希望在任何位置都可以使用 bin目录下的命令,需要配制环境变量

右击(计算机) — 属性— 高级系统设置 — 环境变量

JAVA_HOME:C:\Program Files\Java\jdk1.8.0_231(个人安装目录)
PATH:%JAVA_HOME%\bin;(在PATH中添加,放在最前面)
2.基本工具

javac:编译器,将源程序转成字节码文件

java:执行器,运行编译后的字节码文件

javadoc:文档生成器,从源码注释中自动产生java文档

jar:打包工具,将相关的类文件打包成一个文件

3.常用类库

java.lang:系统基础库类,如字符串类String

java.io:输入/输出库类

java.net:网络相关类库

java.util:系统辅助类库

java.sql:数据库操作类库

引用一个类下的所有类:import java.类名.*;

4、第一个程序

第1步:通过notepad++新建一个文件,在保存时存的类型为(HelloWorld.java)

第2步:编写java代码

public class HelloWorld{
	public static void main(String[] agrs){
		System.out.println("Hello,World");
	}
}

第3步:编译源码,在helloworld.java目录下打开cmd,输入javac helloworld.java,如果文件没有出错,会得到一个同名的.class文件

javac HelloWorld.java

第4步:执行class文件(字节码文件,注:不要加.class)

java HelloWorld

5、java程序运行原理

java程序的运行需要2步

第1步:将源程序编译成字节码程序(*.class) — javac

第2步:对字节码程序进行解释执行(边解释边运行) — java

6、java程序概述

1.类(class)
public class 类名{
    //...
}
2.main方法
  • main方法是程序的入口,一个程序必须有一个main方法才能运行。

  • 一个java文件只能有一个main方法。

public static void main(String[] args){
    //...todo
}
3.执行代码
System.out.println("hello,world!")
4.总代码
public class HelloWorld{
    public static void main(String[] args){
        System.out.println("hello,world");
    }
}

注意:一个java文件只能有一个public修饰的类,public修饰的类的类名与文件名要保持一致(可以有多个非public的类)

7、java注释

java 三种注释:单行注释、多行注释、文档注释

单行注释

//单行注释

多行注释

/*
 * 多行注释
 */

文档注释

/**
 * 多行注释 
 */

8、java语言的特点

特点:简单易学的、面向对象的、分布式的、健壮的、跨平台(指用java编写的应用程序,编译成字节码文件.class后,不用修改就可以在不同平台运行)、安全的、多线程的、分布式的

二、数据类型与运算符

1、标识符

java语言中相关对象名称的命名规则要求。java要求名称只能包含:字母、数字、下划线_及$符号,并且不能以数字开始;同时标识不能与 java语言中内置的关键字重名。

变量名、函数名 :需要尊守camel命名法,要求名称首字母小写,后每个单词首字符大写。

类名、接口名、枚举 :需要尊守Pascal命名法,要求名称首字母大写,后每个单词首字符大写。

java关键字

关键字含义
abstract表明类或者成员方法具有抽象属性
assert断言,用来进行程序调试
boolean基本数据类型之一,声明布尔类型的关键字
break提前跳出一个块
byte基本数据类型之一,字节类型
case用在switch语句之中,表示其中的一个分支
catch用在异常处理中,用来捕捉异常
char基本数据类型之一,字符类型
class声明一个类
const保留关键字,没有具体含义
continue回到一个块的开始处
default默认,例如,用在switch语句中,表明一个默认的分支。Java8 中也作用于声明接口函数的默认实现
do用在do-while循环结构中
double基本数据类型之一,双精度浮点数类型
else用在条件语句中,表明当条件不成立时的分支
enum枚举
extends表明一个类型是另一个类型的子类型。对于类,可以是另一个类或者抽象类;对于接口,可以是另一个接口
final用来说明最终属性,表明一个类不能派生出子类,或者成员方法不能被覆盖,或者成员域的值不能被改变,用来定义常量
finally用于处理异常情况,用来声明一个基本肯定会被执行到的语句块
float基本数据类型之一,单精度浮点数类型
for一种循环结构的引导词
goto保留关键字,没有具体含义
if条件语句的引导词
implements表明一个类实现了给定的接口
import表明要访问指定的类或包
instanceof用来测试一个对象是否是指定类型的实例对象
int基本数据类型之一,整数类型
interface接口
long基本数据类型之一,长整数类型
native用来声明一个方法是由与计算机相关的语言(如C/C++/FORTRAN语言)实现的
new用来创建新实例对象
package
private一种访问控制方式:私用模式
protected一种访问控制方式:保护模式
public一种访问控制方式:共用模式
return从成员方法中返回数据
short基本数据类型之一,短整数类型
static表明具有静态属性
strictfp用来声明FP_strict(单精度或双精度浮点数)表达式遵循[IEEE 754](https://baike.baidu.com/item/IEEE 754?fromModule=lemma_inlink)算术规范
super表明当前对象的父类型的引用或者父类型的构造方法
switch分支语句结构的引导词
synchronized表明一段代码需要同步执行
this指向当前实例对象的引用
throw抛出一个异常
throws声明在当前定义的成员方法中所有需要抛出的异常
transient声明不用序列化的成员域
try尝试一个可能抛出异常的程序块
void声明当前成员方法没有返回值
volatile表明两个或者多个变量必须同步地发生变化
while用在循环结构中

final、finally、finalize区别

  • final用于声明属性,方法和类,分别表示属性不可交变,方法不可重写,类不可继承;

  • finally是异常处理语句结构的一部分,表示总是执行;

  • finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,供垃圾收集时的其他资源回收,例如关闭文件等。

2、常量与变量

java语言中的变量分为局部变量全局变量( 成员变量)

1.全局变量

方法外声明的变量(类中声明的变量),全局变量可以不初使化(因为系统会赋默认值)。

不能改变的变量,在java中,声明常量,需要添加关键字:final (常量名称全大写)。若是强行赋值,程序会显示错误信息,并拒绝接受这个新值。

2.局部变量

方法或者语句块内声明的变量称为局部变量(包括方法的参数),对局部变量,在使用时需要有数据(初使化 )。

public class Demo{
    public static void main(String[] args){
        int i = 10;
        {
            int j = 20;
            System.out.println(i);// 正确,语句块里面可以访问外面的变量
        }
        System.out.println(j);// 错误,语句块外面不可以访问语句块里面的变量
    }
}

如上所示,可以输出i的值,但不能输出j的值,会提示找不到该变量。

方法中的局部变量可以和方法外的成员变量同名。在使用的时候,如果在局部变量所在的方法体内,局部变量覆盖成员变量,输出的结果是局部变量的值;如果在局部变量所在的方法外,不在局部变量的作用域内,输出的是成员变量的值。

public class Demo{
    
    static int i = 1;
    
    public static void main(String[] args){
        {
            int i = 10;
        	System.out.println(i);// 输出10,读取到的是局部变量的值
        }
        System.out.println(i);// 输出1,读取到的是全局变量的值
    }
}

注:类的外面不能有常量和变量的声明

3、数据类型

java语言中数据类型分为基础数据类型(值类型) 与 引用类型

基本数据类型:8种

整型(4个):byte(1字节,8位) 、short(2字节,16位)、int(4字节,32位)、long(8字节,64位)

浮点型(2个): float (4字节,32位)、double(8字节,64位)

字符型:char(2字节16位),可以存汉字,但只能存一个

布尔型:只有两个值,true或者false

  • int是整形的默认类型,double是浮点型的默认类型;

  • long直接赋值时需要在数字后加上L或l,如long l = 10L;

  • float直接赋值时需要在数字后加上F或f,如float f = 1.0F;

基本数据类型的大小

类型最大值最小值默认值
byte127(27-1)-128(-27)0
short32767(215 -1)-32768(-215)0
int2147483647(231-1)-2147483648(-231)0
long263-1-2630L
float2128-21280.0F
double21024-210240.0D
char\u0000(十进制等效为0)\uffff(十进制等效为65535)
booleanfalse

引用类型

类(class)、接口(interface)、数组等

4、数据类型转换

隐式转换

将一个小的数据类型赋值给大的数据类型会自动发生隐式转换

显示转换

将一个大的数据类型赋值给小的数据类型需要做显示转换(需要手动实现),强转程序运行时有可能会出问题(抛异常或得到一个无效的数据),也有可能不会出问题。例如当int=5强转给byte时,因为byte的取值范围包括5,所以不会有问题,当int=129强转给byte时,会有问题,因为129超出了byte的取值范围

类型转换图

5、运算符

与生活中接触的运算符基本一样

1.算数运算符

单目运算符

+(取正)、-(取负)、++、–

++:int i = 1;int j = i++;此时i=2,j=1;先取出i的值赋给j,然后i再+1;i++先赋值再+1,++i先+1再赋值

–:int i = 1;int j = i–;此时i=0,j=1;先取出i的值赋给j,然后i再-1;i–先赋值再-1,–i先-1再赋值

双目运算符

+、-、*、/、%

三目运算符

(表达式1)?(表达式2):(表达式3)

当表达式1的结果为真时,整个运算的结果为表达式2,否则为表达式3,该运算符是java语言唯一一个三目运算符

2.关系运算符

> >= < <= != ==

==为判断两边是否相等

3.赋值运算符

=、+=、-=、*=、/=

=为将右边的值赋给左边

4.逻辑运算符

&&、||、!

&&:一个运算数为假,其值为假,两个运算数为真,其值为真

||:一个运算数为真,其值为真,两个运算数为假,其值为假

!:取反

5.位运算符

&、|、>>、<<、>>>

int x = 100; int y = x>>2;先将x装换为二进制1100100,再整体向右移两位变成0011001,结果为25;即向右移n位就是x÷2n 向左移则是x×2n

三、流程控制

java语言中,设计者设计了三种结构:顺序结构、选择结构、循环结构

1、顺序结构

代码从上到下依次运行为顺序结构

2、选择结构

选择结构有2种,ifswitch

1.if语句
// 第一种
IF(表达式){
	// todo
}

// 第二种
IF(表达式){
	// todo
}else{
	// todo
}

// 第三种
if(表达式){
	// todo
}else if(表达式){
	// todo
}else if(表达式){
	// todo
}else if(表达式){
	// todo
}.....
else{
	// todo
}

表达式的类型必须为boolean类型,程序中判断表达式的前后顺序务必要有一定的规则,要么从大到小,要么从小到大否则会出现错误。

2.switch语句
switch(表达式){
	case 常量1:
        代码块Abreakcase 常量2;
        代码块Bbreak...
	default:
		代码块X}
  • switch关键字表示“开关”,其针对的是后面表达式的值。这个表达式的值只允许是byte,short,int,char,String和枚举类型
  • case后必须要跟一个与表达式类型对应的常量,case可以有多个,且顺序可以改变,但每个case后面的常量值必须不同
  • default表示当表达式的实际值没有匹配到前面对应的任何case常量时,default后面的默认代码块会被执行,default通常放在末尾
  • break表示跳出当前结构,如果不加,则会执行后面的所有代码块
  • switch效率最高,在实际应用中,能用switch语句 ,不要用IF语句

3、循环结构

1.for语句
for(表达式1;表达式2;表达式3{
	// todo
}
  • 表达式1通常是赋值语句,一般是循环语句的初始部分,为循环参数赋初值。表达式1可以省略,但需要在for语句前给循环参数先赋值。
  • 表达式2通常是条件语句,即循环条件,当该条件满足时,进入循环,不满足则跳出循环。表达式2也可以省略,即不判断循环条件,也就形成了死循环。
  • 表达式3通常也是赋值语句,属于循环结构的迭代部分,当一次循环代码块执行完毕后,程序执行表达式3,然后再去判断表达式2的循环条件是否满足。表达式3通常用来更改循环参数的值。表达式3也可以省略,如果省略,通常需要在循环代码块中添加修改循环参数的语句。
2.while语句
// 形式1
while(表达式){
	// todo
}

// 形式2
do{
	// todo
}while(表达式)

while和do…while的区别:while中先进行表达式的判断,为true则执行代码,do…while会先执行代码,再进行判断是否继续执行,所以do…while至少会执行一次。

3.break和continue
  • break:跳出(中断)循环执行循环之后的语句,在swich语句中是跳出swich语句
  • continue:终止本次循环继续执行下一次循环

四、方法

1、方法基本概念

在面向过程的语言(C),没有方法的称呼(只有函数),在面向对象的语言中,只有方法,没有函数;

函数与方法功能一样:一段被命名的代码块(实现某一功能),方法是可以被调用的(不受次数限制);

方法语句:

【修饰符】返回值 方法名(【参数列表.....){
   // 方法体
}

// main方法
public static void main(String[] args){
    //程序运行的入口
}

// 普通方法
public String function(int i){
    // todo
}

大括号{}前面的内容称为方法头,大括号中的为方法体;

修饰符:用来规定方法的一些特征,例如它的可见范围以及如何被调用。例如public static,就是修饰符,public表示这个方法的可见范围,而static表示main方法是一个静态方法,修饰符共有4种:public、default、protected、private;

返回值:表示该方法返回什么类型的值。方法可以没有返回值,这时需要用void表示返回值类型。不过一旦一个方法需要返回值,那么方法体中就必须使用return语句返回此类型的值;

方法名:必须符合标识符的命名规则,并且能够望文知意

形参列表:参数用来接收外界的信息,可以是一个或者多个,也可以没有参数,但无论有没有参数,都必须有小括号。方法中这些参数称为形参,必须说明数据类型;

方法的调用

只需给出方法名以及方法的实际参数列表(实参列表的数据类型必须与形参列表一致或可以自动转换成形参列表的格式)即可。如果方法有返回值,则可以赋值给相应的类型的变量。

public int add(int a, int b) {
    return a + b;
}

// 使用
int result = add(1, 3);
  • 如果方法的返回类型为void,并且方法体中包含 return语句,则必须且只能为return
  • 如果声明了多个方法,那么多个方法之间不能相互嵌套,即不能在方法中再写一个方法
  • 一个方法只能有一个返回值,也只能有一个返回值类型

2、方法重载

重载:方法名相同,参数的类型、数量、或者顺序不同的一组方法称为重载方法

重载与返回值无关(不能根据返回值的不同来判断是不是重载方法)

public static int fun(int a, int b) {
    return a + b;
}
    
public static int fun(int a) {
    return a;
}

3、方法递归

一般情况下,方法的使用是在一个方法中调用另一个方法。

递归:方法中有一条语句直接或间接调用方法本身,称为方法的递归调用;递归是程序中的一种算法(一些问题使用递归解决非常方便,简单,也比较好理解) ,递归就是从已知推未知

递归需要有出口:没有出口递归本质就是一个死循环(Exception java.lang.StackOverflowError)

如何写: 首先写一个方法(一般是有参的 有返回值的),在方法内部从已知条件开始写;

为了防止递归调用无休止地运行,必须在方法内有终止递归调用的手段。通常的做法就是增加判断条件,满足某条件后就不再进行递归调用,然后逐层返回

斐波拉契数列:1,1,2,3,5,8,… 求第30个数

public static int fun(int n){
    if (n == 1 || n == 2) {
        // 当为第一第二个数时结果都是1
        return 1;
    }
    // 当从第三个数开始,每个数都为前两个数之和
    return fun(n - 2) + fun(n - 1);
}

4、方法参数

难点基本数据类型的参数和引用类型的参数,在给参数赋值时,参数值是不一样的。

基本数据类型

数据是存放在机内存的栈区,基本数据类型的变量在传递时,传递的是值的副本,对传递后的变量(形参)的修改不会影响原变量的值

注意:String类与基本数据类型一致,也不会被改变!

public class Test {

    public static void main(String[] args) {
        int n = 3;
        fun(n);
        // 虽然方法fun改变了n的值,但是由于n是基本数据类型,所以n的值仍为3
        System.out.println(n);
    }

    public static void fun(int n){
        n = n + 1;
    }

}

引用类型

数据是存放在内存的堆区(引用类型的变量中存放的是地址),所以引用类型变量在传递时,传递的是地址,则会导制多个变量同时指向堆区同一空间的数据,任意一个变量都可以访问此空间的数据,也可以改此空间的数据。

public class Test {

    public static void main(String[] args) {
        // 先创建一个名叫张三的对象
        Person person = new Person("张三", 20);
        // 在这里获取张三的年龄,可以发现输出了20
        System.out.println(person.getAge());
        // 在方法中修改张三的年龄
        fun(person);
        // 再次输出年龄,发现张三的年龄被修改了,变成了22
        System.out.println(person.getAge());
    }

    public static void fun(Person person){
        person.setAge(22);
    }
    
}

// 创建一个Person类,该类为引用类型,并创建查询和修改年龄方法
class Person{
    private String name;
    private int age;

    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

值类型与引用类型在参数传递时的不同?

  • 值类型的数据是存在栈区的,引用类型的数据是存在堆区的,引用类型的地址存在栈区。基本数据类型的变量在传递时,传递的是值的副本,对传递后的变量(形参)的修改不会影响原变量的值;
  • 引用类型变量在传递时,传递的是地址,所以会导制多个变量(引用)同时指向堆区同一空间的数据,任意一个变量都可以访问此空间的数据(任意变量可以读此空间的数据,也可以改此空间的数据)

五、数组

1、数组概述

数组:是具有相同类型的批量数据的集合;

特点:元素是连续存放的(通过下标可以访问数组元素),数组大小一旦定义,不能修改(定义数组需要确认容器大小),元素是有下标的,下标从0开始,其中length属性返回的是数组的长度(元素数量);

分类一维数组、二维数组、多维数组 ;

两个核心要素:相同类型的变量和按一定的顺序排列。数组中的元素在内存中是连续存储的。数组中的元素可以是基本类型,也可以是引用类型;

2、一维数组

声明数组

方式一:类型[] 数组名 = new 类型[长度];

String[] users = new String[10];
int[] intArray = new int[10];

方式二:类型[] 数组名 = new 类型[] {};

double[] doubleArray = new double[] {1.0,2.0,3.0,4.0,5.0};

方式三:类型[] 数组名 = {};

char[] charArray = {'a','b','c','d'};

数组在创建时如果没有初使化,程序在运行时会自动给默认值

  • byte short int long —> 0

  • double,float —> 0.0

  • char —> 空格

  • boolean —> false;

  • 其它类型(引用类型) —> null

数组赋值

数组名[数组下标] = 数值;

int[] intArray = new int[10];
intArray[0] = 0;

3、二维数组

数据结构为表结构:x,y

声明二维数组

方式一:类型[第1维][第2维] 数组名 = new 类型[第1维长度][第2维长度]

//总元素数量:2*3
int[][] arr = new int[2][3];
for(int i=0; i<arr.length; i++) {
    for(int j = 0; j < arr[i].length; j++) {
        System.out.print(arr[i][j] + "\t");
    }
	System.out.println();
}

方式二:类型[第1维][第2维] 数组名 = new 类型[][]{}

int[][] arr2 =new int[][] { {1,3}, {4,5,6}, {7,8,9,10} };

方式三:类型[第1维][第2维] 数组名 = {{},{},{}}

int[][] arr3 = { {1, 3}, {4, 5, 6}, {7, 8, 9, 10} };

4、数组工具类

java.util.Arrays

Arrays.sort(数组);

Arrays.binarySearch(数组,待查找元素);

5、四种排序

1.冒泡法

相邻两个元素进行比较,每一轮都能找到一个最大值,最终实现排序

int[] arr = {4, 2, 1, 10, 9};
for(int i = 0; i < arr.length - 1; i++) { //控制比较的轮次
    System.out.println("第" + (i + 1) +"轮次");
    for(int j = 0; j < arr.length - 1 - i; j++) { //控制相邻元素比较
        if(arr[j] > arr[j + 1]) {
            //交换
            int tmp = arr[j];
            arr[j] = arr[j + 1];
            arr[j + 1] = tmp;
        }
    }
}
2.选择法排序

每一次从数组中找到一个最大或者最小元素,再和某一个位置的元素交换

int[] arr = {4,2,1,10,9};
//n个元素,只需n-1伦次就能确定一个有序的数组序列;
for(int i = 0; i < arr.length - 1; i++) { //控制比较的伦次;
    int minIndex = i;// i =0;
    for(int j = i + 1; j < arr.length; j++) { //能找到一个最小的元素下标;
        if(arr[j] < arr[minIndex]) {
            minIndex = j;
        }
    }
    if(minIndex != i) { //说明找到一个比minIndex小的下标;
        //交换元素;
        int tmp = arr[i];
        arr[i] = arr[minIndex];
        arr[minIndex] = tmp;
    }
}
3.插入法排序

将元素放到一个已排好序的数组中(将数组分成二部分,一部分为已排好序的,一部分为未排序)

int[] arr = {4,2,1,3,7,5,6,8,10,9};
for(int i = 1; i < arr.length; i++) { //控制轮次
    int position = i; //position:要插入的位置;默认原位置
    int insertElement = arr[i]; //要插入的元素
    //写一段代码,得到要插入的位置(position)
    while(position > 0 && arr[position-1] > insertElement) {
        arr[position] = arr[position - 1]; //将前一个替换后一个
        position--;
    }
    arr[position] = insertElement; //在当前位置放要插入的元素
}
4.快速排序

每一轮设一个基准数(第1元素),将数组分割成2部分,左侧是比基准数小的数列,右侧是比基准数大的数列

public static void quickSort(int[] arr, int left, int right) {
    int i = left; //i, j是一个临时变量,存放每一次数序的比对范围
    int j = right;
    if(left > right){
        return;  //退出条件
    }
    int temp = arr[left]; //设置基准数
    while (i != j) { // 不汇合;
        while (arr[j] >= temp && i < j) {
            j--;
        } 
        // 从右向左找,找比基准数小的元素(记录元素的下标)
        while (arr[i] <= temp && i < j) {
            i++;
        } 
        // 从左向右找,找比基准数大的元素(记录下标)
        if (i < j) { 
            // 交换(右侧比基准数小的与左侧比基准数大的元素交换)
            int t = arr[i];
            arr[i] = arr[j];
            arr[j] = t;
        }
    }
    //交换基准数
    arr[left] = arr[i];
    arr[i] = temp;
    quickSort(arr, left, i - 1); //对左侧再调用此方法继续排
    quickSort(arr, i + 1, right);
}

6、数组查询

1.遍历查询
public static int findElement(int[] arr, int element) {
    int index = -1;
    for(int i = 0;i < arr.length; i++) {
        if(arr[i] == element) {
            index = i;
            break;
        }
    }
    return index;
}
2.二分查找
public static int binarySearch(int[] arr,int element) {
    int index = -1;
    int startIndex = 0;
    int endIndex = arr.length - 1;
    while(startIndex <= endIndex) {
        int middle = (startIndex + endIndex) / 2;
        if(arr[middle] == element) {
            index = middle;
            break;
        }else if(arr[middle] > element) {
            endIndex = middle - 1;
        } else {
            startIndex = middle + 1 ;
        }
    }
    return index;
}

六、字符串

1、理解字符串

字符串:字符的集合称为字符串

public final class String{
   private final char value[];
}

  • String类表示字符串,java程序中的所有字符串都作为此类字符串的对象
  • String类不是基本数据类型,它是一个类
  • String类对象的初始化默认值为null
  • String字符串是常量,在创建后不能更改
  • String类是最终类,不能被继承

2、字符串的声明

方式一:String 变量名 = newString(字符串内容);

String s1 = new String("hello,world");

方式二:字符数组转换

// 先定义一个字符数组
char[] c = {'刘', '静', '涛'};
String s2 = new String(c); // s2 = "刘静涛"
String s3 = new String(c, 1, 2); // 截取字符数组中下标为1 - 2 的字符。s3 = "静涛"

方式三:String s4 = 字符串内容;

String s4 = "hello,world";
  • 字面量方式创建的字符串,先在方法区的字符串池中查找该字符串,如果没有该字符串,则创建该字符串,并将地址传到栈区;如果有该字符串,则直接将该字符串的地址传到栈区。
  • new方式创建的字符串对象,每次都会在堆区申请空间,即通过new方式创建的字符串,它们的地址总是不同的

3、字符串操作

符串的比对

==:比较的是两个对象的地址是否一致,本质上就是判断两个变量是否指向同一个对象,是则返回true,否则返回false

equals或equalsIgnoreCase:比较两个String字符串的内容是否一致,返回值也是布尔类型

String s1 = "hello,world";
String s2 = "hello,world";
System.out.println(s1 == s2); // true
System.out.println(s1.equals(s2)); // true
String s3 = new String("hello,world");
String s4 = new String("hello,world");
System.out.println(s3 == s4); // false
System.out.println(s3.equals(s4)); // true

字符串连接

String s1 = "hello";
String s2 = " world";
String s3 = s1.concat(s2); // 方式一
String s4 = s1 + s2; // 方式二

其他操作

将任意类型转为字符串:String.valueOf

字符串分割:split

获取指定位置的字符:charAt(index)

判断字符中是否包含某了子字符串:contains

判断字符以某一字符串开始|结束:startsWith、endsWith

将字符串转为字符数组:toCharArray

将字符串转为大写|小写: toUpperCase| toLowerCase

字符串截取:subString

查找某一字符的下标: indexOf、lastIndexOf

去左右空格: trim

字符串替换:replace、replaceAll

判断字符串是否为空:isEmpty

获取字符串长度:length

4、StringBuffer、StringBuilder

String类是不可变的,虽然在声明一个字符串后,可以直接对字符串进行修改,表面上看字符串是可以修改,但是实际上是创建了一份新空间来存储修改之后的字符串,修改前的字符串仍占用空间,所以当频繁的修改某个字符串时,就会占用一定的内存,内存占用过多会导致内存溢出,所以java语言加入了StringBuffer和StringBuilder。

1.StringBuffer

StringBuffer也可以存放字符串,代表的是可变的字符序列,可以对字符串对象的内容进行更改

StringBuffer():构造一个其中不带字符的字符缓冲区,其初始容量为16个字符

StringBuffer(String s):构造一个字符串缓冲区,并将其内容初始化为指定的字符串内容

2.StringBuilder

StringBuilder是一个可变的字符序列。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候

String、StringBuffer和StringBuilder的区别

  • String定义的是一个不可变的字符串,StringBuffer构建是一个可变的字符串对象;所以对字符串追加操作StringBuffer相对直接用String效率要高

  • StringBuffer与StringBuilder都可以构建可变的字符串对象,两者的API一样 ;StringBuffer是线程安全的,StringBuilder是线程非安全的

StringBuffer和StringBuilder的使用场景

当频繁地需要在字符串中加入新字符时建议使用

七、类与对象

1、理解类

  • 类代表一个种族,具有相同的特性及行为的一个群体,对象是基于此种族生成的一个具体的个体
  • 在计算机的世界里,如果要表现出这个星球上的一个具体的个体,那它必须要在计算机中存在表明该个体的类
  • 例如,先有人这个概念,然后才能生成一个个具体的人类
  • class是用来定义类的关键字,class关键字后面是要定义的类的名称,然后后面的大括号写的是类的内容
  • 在java中,将这些对象的属性仍然称为属性,将对象具有的行为称为方法。比如教师具有姓名,性别,年龄等属性,具有讲课,批改作业等行为(方法)

2、类的设计

类的构造如下:

【修饰符】 class 类名{
    // 成员属性
    // 初使化块
    // 构造方法
    // 成员方法
}
1.类的属性

也称类的成员变量,指创建一个类后,可以定义类的属性,例如创建Person这个类,可以定义姓名,年龄,性别等属性。

2.初使化块

用于初始化对象,每一次对象创建时都会执行。

初始化过程

  1. 给对象的实例变量分配空间
  2. 初始化类的成员变量
  3. 初始化块的初始化
  4. 构造方法的初始化
3.构造方法
  • 构造方法一般为public (也可以是其它封装)
  • 构造方法没有返回值(也不能加void)
  • 构造方法名与类名相同
  • 构造方法可以重载
  • 不能通过对象调用构造方法
  • 在设计类时,如果此类没有构造方法,在编译时,编译器会给类自动添加一个无参构造方法;当有构造方法时,编译时,不会添加无参构造方法
  • 其实构造方法是有返回值的,返回的是刚刚被初始化完成的当前对象的引用

作用:创建对象时,给对象的属性初使化(赋一个默认值)

4.成员方法

成员方法就是类的方法,例如定义一个Person的类,该类有姓名,年龄,性别等属性,有吃饭,睡觉等方法

5.对象的创建
  • 类型名 对象名 = new 构造方法(【参数列表】);
  • 通过“.”操作符来引用对象的属性和方法
  • 成员方法名称虽然可以跟构造方法名称(或类名)一样,而且也能通过,但是尽量不要让成员方法名称跟构造方法(或类名)一样
class Person{
    // 类的属性
    String name;
    int age;
    
    // 初始化块
    {
        this.name = "张三";
    }

    // 构造方法
    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }

    // 类的成员方法
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }
}
// 创建一个对象
Person person = new Person("zhangsan", 20);
// 获取该对象的名称
System.out.println(person.name);
// 执行该对象的方法
System.out.println(person.getName);

3、封装

封装就是将抽象的属性和行为结合起来,形成一个有机的整体,也就是类。类里面的一些属性和方法(尤其是属性)需要隐藏起来,不希望直接对外公开,但同时提供外部访问的方法来访问这些需要隐藏的属性和方法

在JAVA语言中,封装有4个级别 :private(私有的)、default(默认)、protected(受保护的)、public(公开的)

private : 只能在本类的内部访问

default(默认):在类的内部和本包中是可以访问的,在包外是不能访问的

propected:受保护的,用于继承

public:公开的(访问不受任何限制,本类内部、本包中、其它包中都可以访问)

修饰符类的内部同一个包中子类任何位置
private×××
default××
protected√(子类的内部)×
public

被private修饰的属性或者方法,需要通过get()与set()方法去执行相应的操作

4、this关键字

this:表示本类的对象

this(【参数】) :类的构造方法,主要用于构造方法太多, 代码重复的问题

**注:**构造方法内如果调用其它构造方法,必须放在构造方法的第一条语句

class Person{
    
     String name;
     int age;

    // 只有一个参数的构造方法
    public Person(String name) {
        this.name = name;
    }

    // 有2个参数的构造方法
    public Person(String name, int age){
        // 这里使用this表示上方的构造方法,当类的属性过多时可以使用该方法减少代码
        this(name);
        this.age = age;
    }

}

5、包(package)

:管理类的一个文件夹(一般在开发中,将同一性质的类放到同一个包中)

包的命名:一般采用网址的倒序,用.分割

作用:提供了类似于操作系统树形文件夹的组织形式,能分门别类的存储、管理类,易于查找并使用类;解决了同名类的命名冲突问题;包允许在更广的范围内保护类、属性和方法

包名全部用小写字母

同一个包中的类是直接使用的,如果不在同一个包中,需要导入,如果不导入,需要使用全类名

导包:import 包.类;

如果要使用一个包中的某些类,可以使用import 包名.*;的形式导入这个包中的所有类

import语句需要放在package语句后,在类定义之前

程序中如果有package语句,该语句一定是源文件中的第一条可执行语句,它的前面只能有注释或空行,另外一个文件中最多只能有一条package语句

了解一个jdk中常用的包:

java.lang :基础类放在此包中(此包中的所有类及接口直接使用 --- 默认已导入)
java.util:java中工具类及集合在此包中(Scanner|Date|Arrays)
java.awt: GUI组件被放到此包中;
java.io :文件操作的相关类与接口放到此包中;
java.net :网络编程相关接口与类放在此包中;
java.sql:数据库操作的相关设计放在此包中;
javax.swing :GUI组件设计所放的包(是awt的升级)
......

6、static关键字

static :静态的、共有的

类的成员静态成员实例成员。通过static关键字修饰的是静态成员,否则就是实例成员

类的静态成员:静态属性,静态初始化块,静态方法

类的实例成员:属性,构造方法,初使化块,实例方法

静态成员的访问可以不创建对象,直接通过类名.静态成员访问

方法的内部不允许声明静态变量

静态的访问:建议静态成员访问静态成员,实例成员访问实例成员。虽然实例成员可以访问静态成员,但不建议这样做(传递的信息不明确),静态成员是一定不能访问实例成员,因为静态成员是在类加载的时候就初始化了,但是此时可能还没有创建对象,访问一个不存在的对象当然是不可以了

静态初使化块 :是在类加载到内存中就运行,只运行一次

在设计类(尤其是工具类),为了使用的方便,将类的成员做成静态的

难点

当类的所有成员都存在时,创建对象的流程:先执行静态块(只执行一次),然后执行初始化块(创建一次对象就执行一次),最后执行构造方法

面向对象的基本特性(三大特点):封装、继承、多态

静态变量和实例变量的区别:类变量是所有对象共有,其中一个对象将它值改变,其他对象得到的就是改变后的结果;而实例变量则属对象私有,某一个对象将其值改变,不影响其他对象

静态方法不能调用实例变量的原因:因为静态方法或者变量在类加载的时候就分配内存了,而非静态的方法或变量在创建对象的时候才分配内存,所以当静态的访问非静态的时候会出错是因为当非静态不存在的时候静态已经存在了,访问一个不存在的东西当然会出错

7、单例模式

模式:解决某一类问题的通用方案称为模式。软件开发中,设计的问题主要有23种,所以前辈给出23种模式

单例模式:一个类只能创建一个对象

// 懒汉模式
public class Singleton {
	private static Singleton singleton  =  null;
	private Singleton() {
	}
	public static Singleton newInstance() {
		if(singleton == null) {
			singleton = new Singleton();
		}
		return singleton;
	}
}

// 饿汉模式
public class Singleton {
	private static Singleton singleton  =  new Singleton();
	private Singleton() {
	}
	public static Singleton newInstance() {
		return singleton;
	}
}

八、继承与多态

1、继承

继承:即拥有,例如继承家产

分析:部分类都有相同的一些属性和方法,如果每个类都写出来,则会有很多重复的代码

解决:单独做一个基类(父类),通过此类定义共同属性或者行为(方法),其它类只需要继承本类即可

如果两个类构建了继承关系后,类的身份发生变化:

被继承的类:父类 ,基类

继承的类:子类,派生类

JAVA的继承是单根继承(一个类的直接父类只能有一个),但可以层层继承

1. 实现继承

继承之后,每个子类中都含有一个父类对象

继承需要关键字extends

public class Son extends Parent{
	// 类的成员
}
2.访问权限
  • 子类可以继承父类中访问权限修饰符为public和protected的属性和方法
  • 子类可以继承父类中用默认权限修饰的属性和方法,但子类和父类必须在同一个包中
  • 子类无法继承父类中访问权限修饰符为private的属性和方法
  • 子类无法继承父类的构造方法
  • 子类会默认调用父类的无参构造方法

注意:子类构造方法中如果没有显式调用父类有参构造方法,没有通过this显式调用自身的其他构造方法,则系统默认调用父类无参构造方法

protected:此封装级别为继承而设计,此封装级别子类可以访问父类的成员,子类的内部(不管在同一个包中或不同的包中)都可以访问,但类的外部是不行的

3.方法重写

概念:子类可以从父类继承相应访问权限的方法,但如果父类的方法不能满足子类的需要,则可以在子类中对父类的同名方法进行重写

所需条件

  • 重写方法与被重写方法同名,参数列表也必须相同
  • 重写方法的返回值类型必须和被重写方法的返回值类型相同或是其子类
  • 重写方法不能缩小被重写方法的访问权限

在子类中重新改写父类的方法,然后把子类赋给父类,再调用父类的方法,即可实现重写

4.属性覆盖

创建父类对象的时候用子类实现,此时这个对象的属性为父类的属性,不被子类覆盖

父类的方法被子类覆盖,调用了子类重写的方法,显示出子类方法

5 super关键字

super:父类的对象

super([参数…]) :父类的构造方法

this与super不能同时出现

在父类中创建构造方法,子类创建构造方法后,可以直接用super调用。也就是说,在父类中用this后,子类不用this了,直接用super

6.理解子类对象的创建

在创建子类对象时,会选调用父类的构造方法,创建一个父类的对象,默认会调用父类的无参构造方法创建父类的对象(如果父类有无参构造方法,可以省去显示调用),如果父类没有设计无参构造方法,必须显示的调用父类的带参构造方法

每一个子类的对象中都含着一个父类的对象(如果父类有父类,继续创建父类的父类对象,直到Object类)

7.final关键字

final作用

  • 修饰变量,定义的是常量
  • 修饰方法,方法不能被重写
  • 修饰类,类不能被继承

jdk中目前知道的不能被继承的类

  • java.util.Scanner
  • java.util.Math
  • java.lang.String
  • java.lang.System
  • java.lang.StringBuffer
  • java.lang.StringBuilder
8.向上转型与向下转型

向上转型

A类继承了B类,构造了一个父子关系 (是一个 is a 的关系) ,对 is a 关系的类,在定义对象时,可以直接将子类的对象赋值给父类对象,不需要手动处理,自动实现

向上转型后,父类的对象只能访问获取类的成员,子类新添加的成员父类的对象是访问不了的,即类A继承了类B,创建对象B b = new A();,不能通过b来获取到类A新创建的属性

向下转型

不能直接将父类对象赋值给子类对象(父类对象能代表它的任意一个子类对象),需要做强制转换,这就是向下转型

向下转型有可能出问题(父类的对象本质不是当前对象的类型),抛出类型转换异常(ClassCastException)也有可能不出问题(当前父类的对象的本质就是当前转型的对象类型)

判断对象的类型:instanceof

作用:测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型

// 父类
class Person{
     String name;
     int age;

}

// 子类,继承父类
class Son extends Person{
    String sex;
}

public static void main(String[] args) {
    Person person = new Son();
    System.out.println(person instanceof Son); // 输出true,证明person对象实际上为Son类
}
9.Object类

当一个类没有显示的指定从某一个类继承,那么此类就从Object类继承,**Object类是JAVA语言中类层次的根;**Object类中定义的成员,每一个类身上都有(前提是要能被继承下去)

2、多态

关键字:polymorphic

问题:父类的行为满足不了子类的要求怎么办

解决:在子类中对继承的下来的方法重新设计

重写(override):子类对父类的方法进行重新实现,重写的方法一般添加@Override注解

多态:父类行为的多面性称为多态 (子类重写造成的)

多态实现条件:

  • 要有继承
  • 要有重写
  • 必须通过父类的对象调用

多态实现形式:

  • 重写普通父类中的方法实现多态
  • 重写抽象类中的抽象方法实现多态
  • 重写接口中的方法实现多态(接口中的方法默认全为抽象方法)

3、难点

this与super关键字的理解

this:指代当前对象,哪个对象调用指的就是哪个对象;只能用在方法中,方法中访问成员变量之前默认都有个this

super:指代当前对象的父类对象

理解子类对象的创建

在创建子类对象时,会先调用父类的构造方法,创建一个父类的对象,默认会调用父类的无参构造方法创建父类的对象(如果父类有无参构造方法,可以省去显示调用),如果父类没有设计无参构造方法,必须显示的调用父类的带参构造方法

每一个子类的对象中都含着一个父类的对象(如果父类有父类,继续创建父类的父类对象,直到Object类),因为必须先创建一个父类对象,父类对象中有属性了子类才能继承

重载(overload)与重写(override)区别

overload表示重载,同一类中多态的一种表现形式,返回值可以相同也可以不同,不能根据返回值类型作为区分重载函数的区分标准

override表示重写,子类继承父类,拥有父类的属性和方法但有时候子类并不想原封不动的继承父类中的方法,而是需要做一定的修改,这时候就要重写

九、抽象类与接口

1、抽象类

1.概述

场景:父类服务于子类,如果父类的方法子类直接调用,不需要重写,则可以直接在父类中定义并实现方法。如果父类的方法在子类中需要重写,则可以在父类中只定义方法,不提供方法的实现,由子类自己实现方法。

抽象方法:只有方法的定义,没有方法的实现,在定义时需要添加一个关键字abstract

抽象类

  • 包含抽象方法的类,称为抽象类

  • 定义抽象类关键字:abstract

  • 抽象类通常以父类出现

  • 抽象类是不能实例化的

  • 抽象类本质是类,所以普通类中有的成员,抽象类中都可以有

  • 抽象类可以没有抽象方法,这种情况下抽象类与普通的类没有区别

定义

abstract class Demo{
    
}

抽象类的特征

  • 抽象类不能被直接实例化
  • 抽象类的子类必须实现抽象方法,除非子类也是抽象类
  • 抽象类里可以有普通方法,也可以有抽象方法,但是有抽象方法的类必须是抽象类

问题:类与抽象类的区别

类与抽象类本质是一样的,都是类;抽象类一般是以基类的身份出现的,服务于子类的,可以包含抽象方法,普通类不可以;抽象类是不可以实例化的,普通类可以实例化

2.模板方法模式

模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤的实现延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中某些步骤的具体实现

public abstract class AbstractLogin {
	public final void login() {
		//第1步:验证
		this.validate();
		//第2步:实现登录
		this.userLogin();
		//第3步:记录日志
		this.writeLog();
	}
	public abstract void validate();
	public abstract void userLogin();
	public abstract void writeLog();
}

2、接口

接口:是对类的行为(方法)定制的一套标准、一套规范、一套约束

方法约束,规范:方法的返回值、方法名、方法的参数(类型,数量,顺序)

  • 定义接口:interface
  • 实现接口:implements

接口的定义:

[修饰符] interface 接口名 [extends] [接口列表] {
	// 接口体
}
  • interface前的修饰符是可选的,当没有修饰符的时候,即default,表示此接口的访问只限于同包的类和接口。如果使用修饰符,只能用public修饰符,表示此接口是公有的,在任何地方都可以引用
  • 接口和类是同一个层次的,所以接口的命名规则跟类一样
  • 一个接口可以继承多个父接口,当extends后面有多个父接口时,它们之间用逗号隔开

接口的实现

[类修饰符] class 类名 implements 接口列表{
	// 类体
}

类实现接口用implements关键字,java中的类只能是单继承的,但一个java类可以实现多个接口

接口的特征

  • 当类实现了某个接口时,它必须实现接口中的所有抽象方法,否则这个类必须声明为抽象类
  • 接口中不允许有实体方法,所以接口和抽象类一样是不能实例化的
  • 接口中可以有成员变量,默认修饰符是public static final,可以直接省略不写,但必须赋初始值,如int COUNT = 0;
  • 接口中的抽象方法必须用public abstract修饰,同样也可以省略不写
  • Java接口中只能包含public static final类型的成员变量和public abstract类型的成员方法
  • 一个类可以实现多个接口,但一个类只能有一个直接父类
  • 接口可以继承其他接口,实现接口合并的功能

java8中对接口做了扩展:允许接口中定义静态方法与默认方法:

public interface UserService {

    // 静态常量
    public static final int COUNT = 0;

    List<User> getAll(Page<User> page);

    // 静态方法
    static void fun1(){
        // todo
    }
    
    // 默认方法
    default void fun2(){
        // todo
    }
}

问题:抽象类与抽口的区别?

  • 抽象类的本质为类,接口的本质是给类的行为定制规范
  • 抽象类与接口都不能实例化
  • 定义抽象类用abstract关键字,定义接口用interface
  • 一个类只能有一个父类,但可以实现多个接口
  • 抽象类中除了有抽象方法外,还可以有普通类中有的所有类成员,但接口中只能有抽象方法与属性(jdk8做了扩展,可以有默认方法与静态方法)

完结撒花❀

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值