变量
- 变量表示内存中的一个存储区域
- 该区域有自己的名称[变量名]和类型[数据类型]
- 变量必须先声明,后使用,即有顺序
- 该区域的数据或值可以在同一类型范围内不断变化
- 变量在同一作用域内不能重名
- 变量=变量名+值+数据类型 (变量三要素)
程序中加号的使用
- 当左右两边都是数值时做加法运算
- 当左右两边有一方字符串,则做拼接运算
- 顺序为从左到右
System.out.println(100+98);//198
System.out.println("100"+98);//10098
//println为换行输出,print为不换行输出
System.out.println(100+5+"zgs");//105zgs
System.out.println("zgs"+100+5);//zgs1005 即zgs先于100拼接成一个新的字符串zgs100再与5拼接
数据类型
- 基本数据类型
- 数值型
- 整数类型:byte[1],short[2],int[4],long[8]
- 浮点类型:float[4],double[8]
- 字符型(char[2])存放单个字符
- 布尔型:存放true,false
- 数值型
- 引用数据类型
- 类(class)
- 接口(interface)
- 数组([])
整数类型
- byte:字节型
- 占用一个字节 -128~127
- short:短整型
- 占用两个字节 -(215)~215 -1
- int:整型
- 占用四个字节 -(231)~231 -1
- long:长整型(声明long型常量须后加’l’或’L’)
- 占用八个字节 -(263)~263 -1
浮点类型
- float型常量需在后面加’f’或’F’
- 浮点型常量表示形式
- 十进制形式:5.15、4123.0f、.512(允许把0.512的0省略)
- 科学计数法形式: 5.12e2 5.12E-2
- double类型比float类型精度高,如输出0.1234567899时,float只能输出至0.1234567,而double可以输入完
字符类型
- 字符常量是用单引号’'括起来的单个字符
- 转义字符为一个字符即’/t’为一个字符
- char的本质是一个整数,输出是时unicode码对应的字符
char c1='a';
System.out.println(c1);//a
System.out.println((int)c1);//a对应的数字 97
char c2=97;
System.out.println(c2);//a
- char字符可以进行运算的(加减乘除)
System.out.println('a'+10);//97+10=107
类型转换:
注意点::
- 不能对布尔值(boolean,只能为其变量赋值true和false)进行转换
- 不能把对象类型转换为不相干的类型
- 高容量转换为低容量的时候强制转换可能内存溢出或者精度问题。
强制转换(高->低):(类型)变量名
int i=128;
byte b=(byte)i;
自动转换(低->高):
int i=128;
double b=i;
作用域(scope)
- Java主要的变量是属性(成员变量)和局部变量(以Cat类:eat举例)
- 全局变量:也就是属性,有默认值,可以不赋值直接使用,作用域为整个类体Cat类
- 可以被本类使用,或其它类使用(通过对象调用)
- 可以加修饰符
- 可以重名,访问时准寻就近原则
- 生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁
- 局部变量:也就是除了属性之外的其他变量(一般值再成员方法中定义的变量),必须赋值在使用
- 只能在本类中对应的方法中使用
- 不可以加修饰符
- 在同一个作用域中比如在同一个成员方法中,两个局部变量不能重名
- 生命周期较短,伴随着他的代码块的执行而创建,伴随着代码块的结束而销毁
- 全局变量:也就是属性,有默认值,可以不赋值直接使用,作用域为整个类体Cat类
属性 | 局部变量 |
---|---|
有默认值,可以不赋值直接使用 | 无默认值,必须赋值在使用 |
可以被本类使用,或其它类使用(通过对象调用) | 只能在本类中对应的方法中使用 |
可以加修饰符 | 不可以加修饰符 |
可以重名,访问时准寻就近原则 | 在同一个作用域中比如在同一个成员方法中,两个局部变量不能重名 |
生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁 | 生命周期较短,伴随着他的代码块的执行而创建,伴随着代码块的结束而销毁 |
运算符:优先级()
算术运算符
+、-、*、/;
a+=b;//a=a+b;
a-=b;//a=a-b;
自增:a++(执行完代码后,再自增,即先用后加) a=a+1
++a(执代码前,先自增,即先加后用)
自减同理a--、--a
幂运算:需要用工具函数来执行;
double a=Math.pow(3,2);//即a为3的二次方,输出为9.0;
逻辑运算符:
&&(与)、||(或)、!(非)
- a&&b 与运算 有假为假
- a||b 或运算 有真为真
- !a、!b 非运算 假为真,真为假
- !(a&&b)异或运算 同号为假,异号为真
短路运算:当与运算时,若靠左的逻辑运算若为假,则不继续走右边逻辑运算,直接结束运算。
位运算符:
&与、|或、^异或、~、>>、>>>、<<
/*
A=0011 1100
B=0000 1101
-----------------------------
A&B 0000 1100(都为1则为1,有一个非1为0);
A|B 0011 1101(都为0则为0,存在一个1直接为1);
A^B 0011 0001(相同为0,不同为1);
~B 1111 0010(取反)
2<<3 2左移3位 0000 0010->0001 0000 则结果为16
>>右移除二
<<左移乘二
*/
三元运算符
//? :
//x?y:z (如果x==true,则结果为y,否则为z)
用户交互scanner
- C语言中的scanf:
String str=scanner.nextLine();
String str=scanner.next();
//创建一个扫描对象,用于接收键盘数据
Scanner scanner=new Scanner(System.in);
System.out.println("使用next方式接受:");
//判断用户有没有输入字符串
if(scanner.hasNext()){
//使用next方式接受,
String str = scanner.next();//可将next改成nextLIne
System.out.println("输出的内容为"+str);
}
//记得关闭防止占用资源
scanner.close();
}
- next():有空格断掉
- nextLine():以Enter为结束符,可以获得空白
条件输入
用if语句输出int、float或其它类型的字符串。
Scanner scanner=new Scanner(System.in);
System.out.println("使用整数方式接受:");
int i=0;
float f=0.0f;
//输出int类型数字
if(scanner.hasNextInt()){
i=scanner.nextInt();
System.out.println("输出的整数内容为"+i);
}else{
System.out.println("输入的不是整数");
}
System.out.println("使用小数方式接受:");
//输出float类型数字
if(scanner.hasNextFloat()){
f=scanner.nextFloat();
System.out.println("输出的小数内容为"+f);
}else{
System.out.println("输入的不是小数");
}
//记得关闭防止占用资源
scanner.close();
结构
顺序结构
除非特别指明,否则就按照顺序一句一句执行,他是任何一个算法都离不开的一种基本算法结构。
选择结构
- if单、双、多选择结构、嵌套的if结构(if里面还有if,二分查找比较常见)
//多选择为例
if(布尔表达式1){
}else if{
}else{
}
//else语句在所有else if语句之后,一旦其中一个else if语句检测为true,其他的都跳过执行。
- switch多选择结构
int a;
switch(a)//可以是byte、short、int、char
case()://case标签必须为字符串常量或者字面量
//输入语句
break;
case():
//输入语句
break;
default:
//输入语句
while循环:
只要布尔表达式为true,循环就会一直执行下去
while(布尔表达式){
//循环内容
}
do-while循环:
必定先执行一次,再检验while中的布尔表达式。
do{
//循环内容
}while(布尔表达式);
for循环
是最有效最灵活的循环结构。
for(初始化;布尔表达式;更新){
//代码语句
}
- 在idea中输入 100.fori
系统将自动生成:
for(int 1=0;i<100;i++){
}
- 在idea中输入 100.forr系统将自动生成:
for (int i1 = 100; i1 > 0; i1--) {
}
增强for语句:
for(声明语句:表达式)
int [] numbers={10,20,30,40,50};
for(int x:numbers)//将数组每一项遍历出来,遍历给x。
{
System.out.println(x);
}
跳转控制
- break指令:在任何循环语句的主体部分junkeyongbreak控制循环流程,用于强行退出循环不执行循环中剩余语句
- 如果没有指定break,默认退出最近的循环体
- break语句出现在多层嵌套的语句块中时,可以通过**标签(label)**指明要终止的是哪一层语句块
//但尽量不要使用标签,这样会使可读性变差
label1:{...
label2: {...
label3: {...
break label2;
...}
}
}
-
continue指令:语句用在循环语句体中,用于终止某次循环,即结束本次循环,继续执行下一次循环
- continue也有类似的标签功能
-
return指令:跳出所在的方法
方法(method)
- 类似于C语言中的函数
- 一个方法最多返回一个值,如果想返回多个结果,可以选择返回数组
- 形参列表:(参数类型,参数名)…,可以有0个参数或者多个参数,中间用逗号隔开eg getSum(int n1,int n2);
- 方法是void则方法体中可以没有return或者只写return
- 方法不能嵌套定义
- 实参和形参的类型、个数、顺序必须一致
- 跨类中的方法A类调用B类方法,需要通过对象名调用
- 方法命名使用驼峰命名法 eg:printArr、getSum
- 方法定义是的参数为形式参数(形参);方法调用时的传入参数为实际参数(实参),实参和形参的类型必须一致或兼容,个数、顺序必须一致
方法的优点:
- 提高代码的复用性
- 可以将实现的细节封装起来,然后供其他用户调用起来
break 和return 的区别:
return 代表方法结束,返回值需要与参数类型相同
break 代表跳出switch、结束循环
方法构造
方法定义:
访问修饰符 返回值类型 方法名(形参){
//方法体
return 返回值:
}
举例:构造加法方法
public static void main(String[] args) {
int sum=add(1,2);//调用方法
System.out.println(sum);
}
//加法
//public 表示方法是公开的
public static int add(int a,int b)//括号内为形参
{
return a+b;//int表示返回值为int
}
- 在类中构造方法
public static void main(String[] args){
Person p1=new person();//创建对象
pi.speak();//调用方法
}
class Person{
String name;
int age;
public void speak(){
//void即无返回类型
System.out.println("我是一个好人");
}
}
-
可变参数
在方法声明中,在指定参数类型后加一个省略号(…)
一个方法中只能指定一个可变参数,它必须是方法的最后一个参数,任何普通参数必须在他之前声明。
public void test(int ...i){
}
方法的调用
- 基本数据类型传递的是值(值拷贝),形参的任何改变不会影响实参
- 引用类型传递的是地址(传递也是值,但是值是地址)可以通过形参影响实参
- 方法的跨类调用和方法的访问修饰符也有关
- 同一个类中的方法调用:直接调用即可
class A{
public void print(int n){
System.out.println(n);
}
public void speak(){//直接调用
print(10);
}
}
- 跨类中的方法A类调用B类方法:需要通过对象名调用
class A{//通过对象名调用
public void m1(){
B b= new B();//先创建B的对象
b.hello();
}
}
class B{
public void hello(){
System.out.println("hello");
}
}
方法重载 (OverLoad)
- 方法名一定相同
- 形参列表必须不同(个数、类型、顺序至少有一样不同)
- 参数个数不同,如method(int x)与method(int x,int y)不同
- 参数类型不同,如method(int x)与method(double x)不同
- 参数顺序不同,如method(int x,double y)与method(double x,int y)不同
- 只有形参名有区别时会造成方法的重复定义,构不成重载
- 返回类型无要求
- 减轻了记名的麻烦
- 减轻了起名的麻烦
- 利于接口编程
//out 为PrintStream类型的对象
System.out.println(100);
System.out.println(1.1);
System.out.println(true);
System.out.println('h');
System.out.println('hello');
举例:加法器calculator
class MyCalculator{
//下面四个calculate构成重载
public int calculate(int n1,int n2) {
return n1+n2;
}
public double calculate(int n1,double n2) {
return n1+n2;
}
public double calculate(double n1,int n2) {
return n1+n2;
}
public int calculate(int n1,int n2,int n3) {
return n1+n2+n3;
}
方法重写
重写都是方法的重写,和属性无关,子类方法和父类必须要一致,方法体不同
需要有继承关系,子类重写父类的方法:
- 方法名必须相同
- 参数列表必须相同
- 修饰符:范围只可以扩大:public>protected>default>private
- 抛出的异常:范围,可以被缩小,但不能扩大
eg:ClassNotFoundException–>Exception(大)
为什么需要重写:
- 父类的功能子类不一定需要或者不一定满足
Alt+Insert:override
不能重写的方法:
- static方法:属于类,不属于实例
- final 是常量
- private方法:私有方法不能被子类修改
递归
- 自己调用自己,需要有结束条件
递归结构包括两部分:
递归头:什么时候不调用自身方法。如果没有头,将进入死循环
递归体:什么时候需要调用自己
// 递归算阶乘
public static int f(int n){
if(n==1){
return 1;
}else{
return n*f(n-1);
}
}
数组(array)
- 数组是相同类型数据的有序集合
- 数组长度是固定的
- 数组可以是任何数据类型,包括基本类型和引用类型
- 数组对象本身是在堆中
- 数组的下标是从0开始的
- 数组小标必须要指定范围内使用否则报错(下标越界异常)
- 即 int[]arr=new int[5]; 其有效下标为0-4
- 数组属于引用类型时数组型数据是对象
数组声明和创建
定义:
int[]nums;//首选声明类型
int nums2[];//C语言风格
nums=new int[10];//创建一个数组
int [] num=new int [10];//创建与声明相结合
int [] numbers={10,20,30,40,50};//定义数组:
数组赋值机制(引用传递)
- 基本数据类型赋值(值拷贝)
int n1=10;
int n2=n1;
n2=80;
//输出n1为10;n2为80;
//n2的变化不会影响n1的值
- 数组引用传递(地址拷贝)
int[] arr1={1,2,3};
int[] arr2=arr1;
//赋的是一个地址,arr2的变化会影响到arr1
arr2[0]=10;
//输出的arr1为 10 2 3
数组长度
获取数组的长度:arrays.length
初始化
//静态初始化:
int[]a={1,2,3,4,5,6};
System.out.println(a[0]);
//动态初始化:包含默认初始化(一被分配空间后,没被赋值的都为0或NULL)
int[]b=new int[10];
b[0]=10;
数组边界
下标的合法区间:[0,length-1]
如果越界就会报错:ArrayIndexOutOfBoundException:数组下标越界异常
数组使用
int[]arrays={1,2,3,4,5};
//打印所有数组元素
for(int i=0;i<arrays.length;i++){
System.out.println(arrays[i]);
}
//计算所有元素的和
int sum=0;
for(int i=0;i<arrays.length;i++){
sum+=array[i];
}
//查找最大的元素
int max=array[0];
for(int i=1;i<arrays.length;i++){
if(arrays[i]>max){
max=arrays[i];}
}
//反转数组
int[]result=new int[arrays.length];
for (int i = 0,j=result.length-1; i <arrays.length ; i++,j--) {
result[j]=arrays[i];
}
return result;
输入arrays.for 即可自动生成增强型for循环
for(int array:arrays){
System.out.println(arrays);
//没有下标
}
多维数组
- 多维数组可以看成是数组的数组
- 以二维数组为例子
- 定义形式上看 int[][]
- arr[i]表示二维数组的第i+1个元素 eg: arr[0]为二维数组第一个元素
- arr[i].length 得到对应的每个一维数组的长度
- 访问第(i+1)个一维数组的第j+1个值 arr[i][j]
//二维数组的定义:
int[][]arrays={{1,2}{2,3}{3,4}};
//三行两列的数组,arrays[0][0]为1,arrays[1][1]为3
// 1,2
// 2,3
// 3,4
//此时打印的数组长度为3,即外面空间有三个元素
工具类Arrays
避免重复造轮子,有工具就不用自己再打一遍。
常用功能:
- 给数组赋值:fill
- 输出数组:toString
- 对数组排序:sort
- 比较数组:equals
- 查找数组元素:binarySearch
int []a={1,3,23,5235,2432,4234};
//打印数组元素:Arrays.toString
System.out.println(Arrays.toString(a));
//结果为[1, 3, 23, 5235, 2432, 4234]
//排序算法:
Arrays.sort(a);//升序
//之后再打印的结果为[1, 3, 23, 2432, 4234, 5235]
//数组填充
Arrays.fill(a,0);//全填为0
//之后打印结果为[0, 0, 0, 0, 0, 0]
Arrays.fill(a,1,3,0);//下标1到下标3的值之间填为0
//之后打印结果为[1, 0, 0, 5235, 2432, 4234]、
稀疏数组
一种数据结构,一个二维数组很多元素是默认值为0或者为同一值。
处理方式:
- 记录数组几行几列,有多少个不同值
- 吧具有不同值的元素和行列及值记录在一个小规模数组中从而缩小程序规模
//创建一个二维数组11*11 0:没有棋子,1:黑棋,2:白棋
int[][]array1=new int[11][11];
array1[1][2]=1;
array1[2][3]=2;
System.out.println("输出原始的数组");
for (int[] ints : array1) {
for (int anInt : ints) {
System.out.print(anInt+"\t");
}
System.out.println();
//输出结果为:
// 0 0 0 0 0 0 0 0 0 0 0
// 0 0 1 0 0 0 0 0 0 0 0
// 0 0 0 2 0 0 0 0 0 0 0
// 0 0 0 0 0 0 0 0 0 0 0
// 0 0 0 0 0 0 0 0 0 0 0
// 0 0 0 0 0 0 0 0 0 0 0
// 0 0 0 0 0 0 0 0 0 0 0
// 0 0 0 0 0 0 0 0 0 0 0
// 0 0 0 0 0 0 0 0 0 0 0
// 0 0 0 0 0 0 0 0 0 0 0
// 0 0 0 0 0 0 0 0 0 0 0
}
//转换为稀疏数组保存
//获取有效值的个数
int sum=0;
for(int i=0;i<11;i++){
for(int j=0;j<11;j++){
if(array1[i][j]!=0){
sum++;
}
}
}
System.out.println("有效值的个数:"+sum);
//创建一个稀疏数组的数组
int[][]array2=new int [sum+1][3];
array2[0][0]=11;
array2[0][1]=11;
array2[0][2]=sum;
//遍历二维数组array1,将非零的值存放稀疏数组中
int count=0;
for(int i=0;i< array1.length;i++){
for(int j=0;j<array1[i].length;j++){
if(array1[i][j]!=0){
count++;
array2[count][0]=i;
array2[count][1]=j;
array2[count][2]=array1[i][j];
}
}
}
//输出稀疏数组
System.out.println("稀疏数组:");
for(int i=0;i<array2.length;i++){
System.out.println(array2[i][0]+"\t"+array2[i][1]+"\t"+array2[i][2]+"\t");
}
//稀疏数组:
// 11 11 2
// 1 2 1
// 2 3 2
//给出稀疏数组还原原数组:
//1、读取稀疏数组
int[][]array3=new int[array2[0][0]][array2[0][1]];
//2、给其中的元素还原他的值
for(int i=1;i<array2.length;i++){
array3[array2[i][0]][array2[i][1]]=array2[i][2];
}
//3、打印
for (int[] ints : array3 ){
for (int anInt : ints) {
System.out.print(anInt+"\t");
}
System.out.println();
}
//输出结果为
0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0
0 0 0 2 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
}
查找
顺序查找
- 定义一个数组
- 接受用户输入,遍历数组,逐一比较,如果有则提示信息并退出
二分查找
- 二分查找需要的条件
- 用于查找的内容逻辑上来说是需要有序的
- 查找的数量只能是一个,而不是多个
思路:
- 从数组的中间元素开始搜索,如果该元素正好是目标元素,则搜索过程结束,否则执行下一步。
- 如果目标元素大于/小于中间元素,则在数组大于/小于中间元素的那一半区域查找,然后重复步骤(1)的操作。
- 如果某一步数组为空,则表示找不到目标元素。
- 二分法查找的时间复杂度O(log n)
面向对象
- 本质:以类的方式组织代码,以对象的形式封装数据
- 三大特性:封装、继承、多态
封装(encapsulation)
封装就是把抽象出的数据【属性】和对数据的操作【方法】封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作【方法】,才能对数据进行操作
好处:
- 隐藏实现细节 方法(连接数据库)<-调用
- 可以对数据进行验证,保护安全合理
封装的实现步骤:
- 将属性进行私有化private (外部不能直接修改属性)
- 提供一个公共的(public)set方法,用于对属性判断并赋值
public void setXxx(类型 参数名){//Xxx表示某个属性
//加入数据验证的业务逻辑
属性=参数名;
}
//可用快捷键Alt+Insert直接得出模板
- 提供一个公共的(public)get 方法,用于获取属性的值
public 数据类型 getXxx(){//权限判断,Xxxv某个属性
return xx;
}
//可用快捷键Alt+Insert直接得出模板
高内聚 低耦合的特点
//封装举例:
public class encapsulation {
public static void main(String[] args) {
Student student = new Student();
student.name="tom";
student.setId(12);
System.out.println(student.id+student.name);//输出为12tom
}
//类 private:私有(外部不能使用)
static class Student {
public String name;//名字公开
private int id;
private char sex;
//提供一些public的get.set方法
//get 获得数据
public int getId(){
return this.id;
}
//此后可以在外部使用类的name值
//set 给数据设值
public void setId(int id) {
if(id>0&&id<100)
this.id = id;
else {
System.out.println("ID需要在0到100之间,给你默认id为99");
this.id=99;//如果输入的id不在范围内则默认id设为99,输出则变成99tom
}
}
}
继承(extend)
- extends 拓展:子类是父类的扩展。子类可以继承父类的所有方法
- super:调用父类的构造器,且必须要在子类构造器的第一行
- 提高代码复用性,提高代码的扩展性和维护性
继承的关系图:
继承举例:
//人:父类
public class Person{
pubilc void say(){
System.out.println("说话");
}
private int money=1_000_000;//私有的,不能被继承
pubilc void print(){
System.out.println("Person");
}
}
//学生是人:派生类、子类:学生
public class Student extends Person{
pubilc void print(){
System.out.println("Student");
}
pubilc void test1(){
print();
this.print();//访问该类方法
super.print();//访问父类方法
}
}
//老师是人:派生类、子类:老师
public class Teacher extends Person{
}
//测试函数:
public static void main(String[] args){
Student student =new Student();
student.say();//Student类用了Person类的方法,输出:说话
student.test1();//输出:Student Student Person
}
//ctrl+h 会展现出继承关系
继承的细节
- 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法不能在子类直接访问,要通过父类提供的公共的方法去访问
package extend_;
public class Base {//父类
public int n1=100;
private int n2=200;
public Base(){//无参构造器
System.out.println("base()...");
}
public void test001(){
System.out.println("text001");
}
private void test002(){//因为test002为私有所以子类不能直接调用
System.out.println("test002");
}
//父类提供一个public的方法
public int getN2(){//父类提供一个public访问n2
return n2;
}
}
public class sub extends Base {//子类
public sub(){//子类的无参构造器
//super();默认使用该语句,调用父类的无参构造器
System.out.println("sub()...");
}
public void sayOk(){//子类的方法
//非私有的属性方法可以在子类直接访问;
System.out.println(n1);
//System.out.println(n2);
//test002();
//报错私有的禁止访问
System.out.println("n2="+getN2());//子类访问父类public方法即可再访问到n2
}
}
- 子类必须调用父类的构造器,完成父类的初始化
//以1.的父子类关系举例,创造主函数
public class extends02 {
public static void main(String[] args) {
sub a= new sub();
//输出结果为
//base()...即先调用了父类的base()无参构造器
//sub()...再调用类子类的sub()无参构造器
}
}
- 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作
- super注意点:
- super调用父类的构造方法,必须在构造方法的第一个/第一行
- super必须只能出现在子类的方法或者构造方法中。
- super和this不能同时调用构造方法,因为this()也只能放在构造器的第一行
- super和this的区别:
- 代表的对象不同
- this:本身调用者这个对象
- super:代表父类对象的应用
- 前提
- this:没有继承也可以使用
- super:只能在继承条件才能使用
- 构造方法:
- this():本类的构造
- super():父类的构造
- 代表的对象不同
- 如果希望指定去调用父类的某个构造器,就故意显示地调用一下(平时都是默认调用):super(参数列表)
//举例:
//要调用父类无参构造器,则什么都不写或者写super();
//在创建了子类对象new people("mosun",20)的前提下调用举例
//要调用父类Base(String name)构造器
//super("mosun");
//要调用父类Base(String name,int age)构造器
//super("mosun",20);
- Java所有类都是Object类的子类,Object是所有类的祖宗类(基类)
可用ctrl+H 可得出继承关系。
-
父类构造器的调用不限于直接父类,将一直往上追溯到Object类
-
子类最多只能继承一个父类(直接继承)即java单继承机制
-
不能滥用继承,子类和父类必须满足is-a的逻辑关系
- Person is a Music? X
- Cat is Animal? √
多态(polymorphic)
动态编译:类型(可拓展性)
- 大大提高代码的可用性
- 多态是建立在封装和继承基础之上的
- 多态是方法的多态,属性没有多态
- 父类和子类,有联系才可转换,否则类型转换异常(ClassCastException!)
- 存在条件:继承关系,需要重写,父类引用指向子类对象 Father f1=new Son();
给动物喂食举例:创建食物类(子类为骨头和鱼)、主人、动物类(子类为狗和猫),通过多态减少代码冗余,来实现主人给动物喂食食物的过程
public class Animals {//动物类 父类
private String name;
public Animals(String name){
this.name=name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Cat extends Animals{//🐱猫类继承动物类,子类
public Cat(String name) {
super(name);
}
}
public class Dog extends Animals{//🐕狗类继承动物类,子类
public Dog(String name) {
super(name);
}
}
public class Food {//食物类,父类
private String name;
public Food(String name){
this.name=name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Fish extends Food{//食物类的子类
public Fish(String name) {
super(name);
}
}
public class Bone extends Food{//食物类的子类
public Bone(String name) {
super(name);
}
}
//以上只是简单重复的创建对象
public class Master {//主人
private String name;
public Master(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// //主人给狗喂食骨头
// public void feed(Dog dog,Bone bone){
// System.out.println("主人"+name+"给"+dog.getName()+"吃"+bone.getName());
// }
// //主人给猫喂食🐟
// public void feed(Cat cat,Fish fish){
// System.out.println("主人"+name+"给"+cat.getName()+"吃"+fish.getName());
// }
//使用多态机制,统一管理主人喂食问题,代替上面代码
//animal的编译类型是Animal,所以可以接受Animal子类的对象(Cat/Dog)
//food的编译类型是Food,可以接受Food的子类的对象(Fish/Bone)
public void feed(Animals animal,Food food){//喂食语句
System.out.println("主人"
+name+"给"+animal.getName()+"吃"+food.getName());
}
}
public class Poly01 {
public static void main(String[] args) {
Master mosun = new Master("mosun");//创建主人mosun
Dog doggie = new Dog("doggie");//创建狗类对象doggie
Bone bone=new Bone("pork bone");//创建骨头类对象pork bone
Cat tom=new Cat("tom");//创建猫类对象tom
Fish king = new Fish("king");//创建鱼类对象king
mosun.feed(doggie,bone);
mosun.feed(doggie,king);
mosun.feed(tom,king);
//输出为
// 主人mosun给doggie吃pork bone
// 主人mosun给doggie吃king
// 主人mosun给tom吃king
}
}
对象的多态(多态的核心)
- 一个对象的编译类型和运行类型可以不一致
- 编译类型在定义对象时,就确定了,不能改变
- 运行类型是可以变化的
- 编译类型看定义时=号左边,运行类型看=号的右边
Animal animal=new Animal//animal的编译类型是Animal,运行类型也是Animal
Animal animal=new Dog();//animal的编译类型是Animal,运行类型是Dog
animal=new Cat();//animal的编译类型仍然是Animal,运行类型则变成了Cat
对象的多态举例
public class Animal {
public void cry(){
System.out.println("动物在叫");
}
}
public class Cat extends Animal{
@Override
public void cry(){
System.out.println("miao");
}
}
public class Dog extends Animal{
@Override
public void cry() {
System.out.println("wang");
}
}
public class PolyObject {
public static void main(String[] args) {
Animal animal=new Dog();//编译类型是animal,运行类型是Dog
animal.cry();//因为运行时,执行到该行时,animal运行类型是Dog,所以cry就是Dog的cry;
animal=new Cat();//运行到这行时,运行类型变成了Cat,所以cry变成了Cat的cry;
animal.cry();
//输出为
//wang
//miao
}
}
多态的细节
- 多态的前提:两个对象(类)存在继承关系
- 多态的向上转型
- 本质:父类的引用指向了子类的对象
- 语法:父类类型 引用名=new 子类类型();
- 特点
- 编译类型看左边,运行类型看右边
- 可以调用父类中的所有成员(需遵守访问权限eg:子类私有,父类则无法访问)
- 不能调用子类中特有成员(属性+方法),因为编译阶段能调用哪些成员是由编译类型来决定的
- 多态的向下转型
- 语法:子类类型 引用名=(子类类型)父类引用;
- 只能强转父类的引用,不能强转父类的对象
- 要求父类的引用必须指向的是当前目标类型的对象
- 可以调用子类类型中所有的成员
public class Person{
public void run(){
System.out.println("run");
}
}
//学生是人:派生类、子类:学生
public class Student extends Person{
public void eat(){
System.out.println("eat");
}
}
public static void main(){
//一个对象的实际类型是确定的
new Student();
new Person();
//但是可以指向的引用类型不确定
Student s1=new Student();
Person s2=new Student();//父类型可以指向子类,但是不能调用子类独有的方法
Object s3=new Student();//Object是所有类的顶级父亲,也就是始祖
//s1.run();父类有该方法可以调用
// s2.eat();父类没有该方法,且不能调用子类的eat方法
//((Student) s2).eat();强制类型转换,此时就可以用子类方法
}
类
eg: 类:人类,对象:你
- 相当于C语言中的结构体
- 类是一种抽象的数据类型,它是对某一类事物整体描述
- 类是对象的抽象,对象是类的具体实例
- 静态的属性、动态的行为
- 类是抽象的,需要实例化
//学生类
public class Student{
//属性:字段/成员变量
String name;
int age;
int ID;
}
//实例化
//student对象(下面的小明)就是一个Student类的具体实例
Student xiaoming=new Student();//创建一个学生赋给xiaoming
xiaoming.name="小明";
xiaoming.age=13;
System.out.println(xiaoming.name);
System.out.println(xiaoming.age);
//输出为
//小明
//3
Stduent xiaohong=new student();//直接创建对象xiaohong
xiaohong.name='小红';
xiaohong.ID=10;
System.out.println("小红的信息为"+xiaohong.name+" "+xiaohong.ID);
//输出为
//小红的信息为小红 10
使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用
属性(properties)
- 叫法:成员变量=属性=field(字段)
- 属性是类的一个组成部分,一般是基本数据类型,也可以是引用类型(对象、数组)
- 属性的定义语法同变量: 访问修饰符 属性类型 属性名;
- 属性如果不赋值就有默认值,规则和数组一致
- int 默认值 0
- String 默认值 null
- double默认值 0.0
- boolean 默认值为false
对象创建过程
- 类和对象的内存分配机制
- 栈:一般存放基本数据类型(局部变量)
- 堆:存放对象(Cat、cat,数组等)
- 方法区:长两次(常量,比如字符串),类加载信息
- 示意图[Person(name,age)]
访问修饰符
作用:
- 控制方法使用的范围
- 公开级别 public 对外公开
- 受保护级别 protected 对子类和同一个包中的类公开
- 默认级别 没有修饰符号 向同一个包的类公开
- 私有级别 private 只有类本身可以访问,不对外公开
访问修饰符的访问范围:
注意事项
- 修饰符可以用来修饰类中的属性,成员方法以及类
- 只有默认和public才能修饰类,并且遵循上述访问权限的特点
- 成员方法的访问规则和属性完全一样
构造器/构造方法 (constructor)
是类的一种特殊方法,作用是完成对新对象的初始化,并不是创建对象。
基本语法:
[修饰符]方法名(形参列表){
方法体;
}
-
参数列表和成员方法一样的规则
-
构造器的修饰符可以默认,也可以是public、protected、private
-
构造器必须和类的名字相同
-
必须没有返回类型,也不能写void
-
构造器的调用,由系统完成
-
一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器,除非故意的定义声明一下,即: Person(){}
-
如果没有定义构造器,系统会自动给类生产一个默认无参构造器
对象的创建和使用: -
必须使用new 关键字创造对象,设构造器 person mosun
- 对象的属性 mosun.name
- 对象的方法 mosun.run()
//构造器实例化初始值
//使用new关键字,必须要有构造器,本质是在调用构造器
//用来初始化值
//对象属性:字段field 成员变量
//默认初始化:
// 数字:0 0.0
// char:u0000
// boolean:false
// 引用类型:null
public Student(){
this.name="mosun"//没实例化之前类的对象为null
}
//有参构造(alt+insert默认生成有参):
public Person(String.name){
this.name=name;
}
//================================================
public class pet {
public String name;
public int age;
public void shout(){
System.out.println("汪了一声");
}
}
public class 使用类 {
public static void main(String[] args) {
pet dog = new pet();
dog.name="messi";
dog.age=3;
dog.shout();
System.out.println(dog.name);
System.out.println(dog.age);
}
//输出汪了一声
//messi
//3
}
构造器重载:eg: 两个Person构造器,一个指定人名和年龄,一个只指定人名
class Person{
String name;
int age;
public Person(String pName,int pAge){//指定人名和年龄
name=pName;
age=pAge;
}
public Person(String pName){//只指定人名
name=pName;
}
}
this 关键字
使构造器的形参能够直接写成属性名时,按照变量的作用域原则,参数值会为默认值,因此引用this 关键字
- 哪个对象调用,this就代表哪个对象
- this关键字可以用来访问本类的属性、方法、构造器
- this用于区分当前类的属性和局部变量
- 访问成员方法语法:this.方法名(参数列表);
- 访问构造器语法:this(参数列表); 只能在构造器中调用/访问另外一个构造器
-如果有用这个语法,必须放置在第一条语句。 - this不能在类定义的外部使用,只能在类定义中使用
方法举例:
public class TestPerson {
public static void main(String[] args) {
Person p1=new Person("tom",21);
Person p2=new Person("tim",21);
System.out.println(p1.compareTo(p2));//谁去调用谁就是this
// p1调用的compareTo,所以this指向的是p1
// p2对象作为实参传给了方法compareTo的形参,所以他不是当前对象
}
static class Person{
String name;
int age;
public Person(String name,int age){//构造器
this.name= name;
this.age=age;
}
public boolean compareTo(Person p){
// 创建一个方法比较输入的两人是否为同一人
// if(this.name.equals(p.name)&&this.age==p.age)
// return true;
// else
// return false;
return this.name.equals(p.name)&&this.age==p.age;
}
}
}
包机制
本质就是创建不同的文件夹来保存类文件
- 区分相同名字的类
- 当类很多时,可以很好的管理类
- 控制访问范围
- 基本语法:package com.hspedu;
- 一般利用公司域名倒置作为包名:com.baidu.www
- import package1[.package2…].(classname|*);
- package com.kuang.operator;
- 命名规则:只能包含数字、字母、下划线、小数点。并且数字不能开头,不能是关键字或保留字
- demo.class.exec1 //class是关键字
- demo.12a //12a是数字开头
- demo.ab12.oa //对
常用的包:
- java.lang. 是基本包,默认引入
- java.util. util包,系统提供的工具包,工具类,使用Scanner
- java.net. 网络包,网络开发
- java.awt. java界面开发,GUI
引入包(需要使用到哪个类,就导入哪个类即可,不建议用*导入所有类)
- import类似于C语言中的头文件,一些代码需要包才可运行
- 语法:import 包
- import java.util.Scanner//表示只会引入java.util 包下的Scanner
- import java.util.* // 表示将Java.util包下所有类都引入
注意事项:
- package 的作用是声明当前类所在的包,需要放在类的最上面,一个类中最多只有一句package
- import指令 位置放在package的下面,在类定义前面,可以有多句且无顺序要求
instanceof(类型转换)
引用类型,判断一个对象是什么类型
延申之前举例的Person、Student、Teacher
有以下关系
//System.out.println(X instanceof Y)编译能不能通过取决于X,Y有无父子关系
//Object>String
//Object>Person>Teacher
//Object>Person>Student
Object object=new Student();
System.out.println(object instanceof Student);//true
System.out.println(object instanceof Person);//true
System.out.println(object instanceof Object);//true
System.out.println(object instanceof Teacher);//false
System.out.println(object instanceof String);//false
Person person=new Student();
System.out.println(person instanceof Student);//true
System.out.println(person instanceof Person);//true
System.out.println(person instanceof Object);//true
System.out.println(person instanceof Teacher);//false
//System.out.println(person instanceof String);//编译报错
Student student =new Student();
System.out.println(student instanceof Student);//true
System.out.println(student instanceof Person);//true
System.out.println(student instanceof Object);//true
// System.out.println(student instanceof Teacher);//编译报错
// System.out.println(student instanceof String);//编译报错
Person obj=new Student();
(Student)obj; //高类转换为低类
Person person=student//子类转换为父类
-
低(子)转高(父)时,由于子已经继承了父的所有,所以删去属于自己的后自然而然就可以转化问父类的;而父想要转子,则需要重新开辟只属于子的空间,则需用强制转换
-
父类引用指向子类,可以调用父类公共的方法,还可以调用子类重写的方法,但是无法调用子类独有的方法,只有通过强制转换才能调用子类的独有的方法
static(静态变量)
private static int age;
private double score;
public void run(){//非静态方法可以调用静态方法
go();
}
public static void go(){
}
public static void main(String[] args){//静态方法只可以调用静态方法里面的东西
go();
//run();//不能调用
}
静态代码块的顺序
public class Hello {
//2:赋初值
{
System.out.println("匿名代码块");
}
//1:只执行一次
static{
System.out.println("静态代码块");
}
//3
public Person(){
System.out.println("构造方法");
}
public static void main(String[] args) {
Person person=new Person();
}
//输出结果:静态代码块 匿名代码块 构造方法
}
抽象类
abstract修饰 抽象方法只有方法名字,没有方法的实现
- 不能new这个抽象类,只能靠子类去实现他:约束
- 抽象类中可以写普通的方法
- 抽象方法必须在抽象类中
普通类:只有具体实现
抽象类:具体实现和规范(抽象方法)都有
接口:只有规范,自己无法写方法;约束和实现分离:面向接口编程
接口的本质是契约
接口
interface接口
定义约束
定义一些方法,让不同的人实现
public interface UserService {
//接口中的所有定义其实都是抽象的 public abstr act
void add(String name);
void delete(String name);
void update(String name);
void query(String name);
}
//类可以实现接口 implements 接口(可以实现多个接口)
//实现了接口的类,就需要重写接口中的方法
public class UserServiceImpl implements UserService {
//快捷键ALT+Insert 选 overide
//选所有方法不会报错
@Override
public void add(String name) {
}
@Override
public void delete(String name) {
}
@Override
public void update(String name) {
}
@Override
public void query(String name) {
}
}
断点调试(debug)
- 断点调试指在程序的某一行设置一个断点,调试时程序运行到这一行就会挺住,然后一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码行即显示错误,停下并进行分析从而找到这个BUG
- 一步一步查看源码执行的过程,从而发现错误所在
- 在断点调试过程中,是运行状态,是以对象的运行类型来执行的
断点调试快捷键
F7(step into跳入方法)
F8(跳过,逐行执行代码)
shift+F8(跳出方法)
F9(resume,执行到下一个断点)
异常(Exception)
- 检查性异常:用户错误或问题引起的异常
- 运行时异常:编译时看不到,运行时才可以发现
- 错误(Error):错误不是异常,而是脱离程序员控制的问题,例如栈溢出
异常处理框架
- 在Exception分支中有一个重要的子类RuntimeException(运行时异常)
- 这些异常是不检查异常,程序中可以选择捕获处理也可以不处理
- ArrayIndexOutOfBoundsException(数组下标越界)
- NUllPointerException(空指针异常)
- ArithmeticException(算术异常)
- MissingResourceException(丢失资源)
- ClassNotFoundException(找不到类)等
异常处理机制
try、catch、finally、throw、throws
int a=1;
int b=0;
//假设要捕获多个异常,从小到大捕获
try{//try监控区域
System.out.println(a/b);
}catch(ArithmeticException e){//catch 捕获异常
System.out.println("程序出现异常,变量b不能为0");
}catch(MissingResourceException){
System.out.println("程序出现异常,丢失异常");
}
finally{//处理善后工作
System.out.println("finally");
}
//==================================================================//
//或者选中下行代码,快捷键 ctrl+alt+t,自动把所需要的代码包裹起来,自动包裹处理机制出现上面的捕获代码
System.out.println(a/b);
自定义异常
只需要继续Exception类即可
- 创建自定义异常类
- 在方法中通过throw关键字抛出异常对象
- 如果在当前异常的方法中处理异常,可以使用try-catch语句捕获并处理;否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作
- 再出现异常方法的调用者中捕获并处理异常
可变参数(variable parameters)
- java允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法
- 可变参数的实参可以为0个或任意多个
- 可变参数的实参可以为数组
- 可变参数的本质就是数组
- 可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
- 一个形参列表中只能出现一个可变参数
基本语法
访问修饰符 返回类型 方法名(数据类型...形参名){
}
举例:n个数字累加
public class Hspmethod {
public static void main(String[] args) {
hspMethod m=new hspMethod();
System.out.println(m.sum(10,3,2));//输出10,3,2三个数的累加值
}
public static class hspMethod{
public int sum(int...nums){
int res=0;
for (int i = 0; i < nums.length; i++) {
res+=nums[i];
}
return res;
}
}
}
常用缩写(可以自定义)
psvm:生成main函数
public static void main(String[] args){
}
sout:生成输出函数
System.out.println();
选定语句快捷键 ctrl+alt+t:生成捕获错误函数
try {
System.out.println(a/b);//为选定的语句
} catch (Exception e) {
e.printStackTrace();
} finally {
}
在idea中输入 100.fori
系统将自动生成:
for(int 1=0;i<100;i++){
}
输入arrays.for 即可自动生成增强型for循环
for(int array:arrays){
System.out.println(arrays);
//没有下标
}
在idea中输入 100.forr系统将自动生成:
for (int i1 = 100; i1 > 0; i1--) {
}
快捷键
默认:
- 删除当前行 ctrl+y
- 复制当前行 ctrl+d
- 补全代码 alt+/
- 添加注释和取消注释 ctrl+/
- 导入该行需要的类 alt+enter
- 快速格式化代码 ctrl +alt+L
- 快速运行程序 shift + f10
- 生成构造器等 alt+insert
- 用类实现接口 快捷键ALT+Insert 选 overide
public class UserServiceImpl implements UserService {
//选所有方法不会报错
@Override
public void add(String name) {
}
@Override
public void delete(String name) {
}
@Override
public void update(String name) {
}
@Override
public void query(String name) {
}
}
- ctrl+h 展现出继承关系:
- 快速定位方法 ctrl +B或者ctrl+鼠标左键
- 自动分配变量名,通过在后面加.var