JAVA
IDE
对于IntelliJ IDEA,首先需要创建项目,然后添加包,最后创建类,然后开始写函数
IDEA快捷键
ALT+D删除所在行
ALT+Enter自动添加包
ctrl+alt+L快速格式化代码
ctrl+alt+向下箭头快速复制粘贴当前行
shift+F10快速運行程序
alt+insert 快速生成构造器
ctrl+H查看类的继承关系
将光标放在一个方法后,Ctrl+B可以定位到定义方法处
在最后添加.var可以快速分配变量名new Scanner(System.in ).var然后回车可以自动添加变量名
IDEA自定义模板
在file->settings->editor->Live template可以查看和增加模板
在java模板中,输入main之后回车就会自动补全,sout是输出的模板,fori是for循环的模板
自己也可以添加模板
记得添加应用场景,我们这里设置为JAVA
JAVA概述
JAVA"白皮书"的关键术语
简单性
面向对象,也就是重点在于数据即对象和对象的接口上
分布式:JAVA有一个丰富的例程库,用于处理像HTTP和FTP之类的TCP/IP协议
健壮性:JAVA采用的指针模型可以消除重写内存和损坏数据可能性
安全性
体系结构中立:编译过的代码可以很容易在任何机器上运行
可移植性:JAVA的数据类型具有固定的大小,二进制数据以固定的格式进行存储和传输,字符串是用标准的Unicode格式存储的
解释型:JAVA解释器可以在任何移植了解释器的机器上执行JAVA字节码
高性能:字节码可以(在运行时刻)动态地翻译成对应运行这个应用的特定CPU的机器码
多线程:多线程可以带来更好的交互响应和实时行为
动态性:JAVA可以适应不断发展的环境
Java运行机制及运行过程
JRE和JDK
JAVA的基本程序设计结构
一个简单的JAVA应用程序
1 2 3 4 5 6 7 package com.Helloworld;public class Helloworld { public static void main (String[] args) { System.out.println("Helloworld!" ); } }
其实class关键字表示类,class后跟类名(必须以字母开头,后面可以是字母和数字的任意组合,但不能是关键字),表明JAVA程序中的全部内容都包含在类中,public表示是公共的,用于控制程序的其他部分对这段代码的访问级别,main方法必须声明为public
源代码的文件名必须与公共类的名字相同,并用.java作为扩展名
与C中不同的是,Java中的所有函数都属于某个类的方法 ,而在C 中被叫做成员函数
System.out.println(“Helloworld!”)这一句使用了System.out对象并调用了它的println方法,相当于函数调用
使用命令行编译和运行java文件
当我们编写好.java文件(源文件)之后,使用javac test.java命令编译为test.class文件(字节码文件),然后通过java test运行。有了java源文件,通过编译器将其编译成JVM可以识别的字节码文件,在该源文件目录下,通过javac编译工具对java文件进行编译。而运行就是将字节码文件装载到jvm执行
这里使用java test运行是因为java表示运行一个java的类,也就是类似自动补充为test.class
还需要注意一点:由于命令行默认为GBK编码,所以我们需要将java文件改为GBK编码格式
java开发注意事项
Java应用程序的执行入口是main()方法,其格式如下:public static main(String[] args){……}
一个源文件最多只能有一个public类,其他类的个数不限,编译后每一个类都会生成一个class文件
如果源文件包含一个public类,则其文件名必须按该类名命名
一个源文件最多只能有一个public类,其他类的个数不限,也可以将main方法写入非public类中,然后指定运行非public类,这样入口方法就是非public类的main方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class test { public static void main (String[] args) { System.out.println("helloworld!" ); } } class Dog { public static void main (String[] args) { System.out.println("hello,Dog!" ); } } class Tiger { public static void main (String[] args) { System.out.println("hello,Tiger!" ); } }
文档注释
对于不同的需求,文档注释也可以选择不同的符号
Java文档注释
数据类型
在Java中,一共有8种基本类型,其中4种整型、2种浮点类型、1种用于表示Unicode编码的字符单元的字符类型char和1种用于表示真值的boolean类型
整型
加上前缀0b或者0B就可以写二进制数 ,如0b1001就是9
还可以为数字字面量加下划线 ,如1_000_000表示一百万,Java编译器会去除这些下划线
对于long类型的数据,需要在结尾加’L’或者’l’ ,如果将long类型的值赋值给int型变量,可能会有损失,需要使用强制转换
浮点类型
float类型的数值有一个后缀F或者f(例如3.14F),没有后缀F的浮点数值默认为Double类型,也可以在浮点数值后添加后缀D或d
浮点数在机器中存放形式:浮点数=符号位+指数位+尾数位,尾数可能会有损失,造成精度丢失
对于小数点前,如果都是0,可以省略,也就是说0.123可以省略为.123
科学计数法:5.12e2就是5.12×10^2
打印的结果如下,这里会默认为double类型数据,所以会在小数点后添加0
通常情况下我们使用double,因为其精度更高
由于是近似计算,所以对于运算结果是小数进行相等判断时,需要小心,应该是以两个数的差值的绝对值,在某个精度范围内判断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class test { public static void main (String[] args) { double num1 = 2.7 ; double num2 = 8.1 / 3 ; System.out.println(num1); System.out.println(num2); if (num1 == num2){ System.out.println("相等" ); } if (Math.abs(num1 - num2) < 0.01 ){ System.out.println("差值接近,近似相等" ); } } }
输出
Java API文档
Java8 API 中文版
char类型
char类型的值可以表示为十六进制值,其范围从\u0000到\uffff
Unicode和char类型
由于不同地区的编码机制不同,所以需要做出统一,这也促进了Unicode的出现,Unicode使用后两个字节来表示字符
boolean类型
boolean类型有两个值:false和true
变量
Java中每个变量都需要一个类型
变量的初始化和C++类似,就不赘述了
常量
在Java中,利用关键字final指示常量(表示这个变量只能被赋值一次,被赋值之后就不能再改了)
在Java中,经常希望某个常量可以在一个类中的多个方法中被使用,通常将这些常量称为类常量
可以使用关键字static final设置一个类常量
此处使用public static final关键字来声明,那么在其他类的方法也可以使用
运算符
加减乘除和C++一样
但是对于浮点数的精度问题是我们需要解决的,因为不同位数机器计算得到的精度是不同的,所以对于使用strictfp关键字标记的方法必须使用严格的浮点计算来生成可再生的结果
数学函数与常量
在Math类中,包含了各种各样的数学寒湖是
导入Math包就不要在数学方法名和常量名前添加前缀"Math"
数值类型之间的转换
精度小的类型自动转换为精度大的数据类型 ,这个就是自动类型转换
byte、char、short之间可以进行运算,在计算时首先转换为int类型 。不同类型数据进行运算时会转为精度最高的类型
强制类型转换
和C++一样,可能造成精度降低或溢出 ,但是如果想对浮点数进行舍入操作以便得到最接近的整数,可以使用Math.round方法
char类型可以保存int的常量值,但不能保存int的变量值
基本数据类型和String类型的转换
基本数据类型转String类型
将基本数据类型的值+""即可
1 2 3 4 5 6 7 8 9 10 11 12 13 public class stringtobasic { public static void main (String[] args) { int n1=100 ; float f1=1.1F ; double d1=4.5 ; boolean b1=true ; String s1=n1+"" ; String s2=f1+"" ; String s3=d1+"" ; String s4=b1+"" ; System.out.println(s1+" " +s2+" " +s3+" " +s4+" " ); } }
String类型转基本数据类型
使用基本数据类型对应的包装类的相应方法,得到基本数据类型
1 2 3 4 5 6 7 8 9 public class stringtobasic { public static void main (String[] args) { String s5 = "1213" ; int num1=Integer.parseInt(s5); double num2=Double.parseDouble(s5); System.out.println(num1); System.out.println(num2); } }
但是对于char类型,无法转换,只能取出字符串对应位置的字符,下面代码就是取出字符串中下标为0的字符
1 System.out.println(s5.charAt(0 ));
需要注意的事项
但是当字符串由字符组成时,对其进行转换就会报错
结合赋值和运算符
自增与自减运算符
关系和boolean运算符
关系运算符和C++相同,同时Java也支持三元运算符
位运算符
对于短路&&,第一个条件为假则不往后判断,而逻辑与&,即使第一个条件为假,第二个条件也会判断
除了按位与、或、非,左移右移,Java还有
括号与运算符级别
枚举类型
字符串
Java字符串就是Unicode字符序列,在标准Java类库中提供了一个预定义类,很自然地叫做String,每个用双引号括起来地字符串都是String类的一个实例
子串
String类中的substring方法可以从一个较大的字符串中提取处一个子串
1 2 String greeting="hello" ; String s=greeting.substring(0 ,3 );
表示截取greeting对象的前三个字符存储到s中
拼接
Java语言运行使用+号拼接两个字符串
1 2 3 String a="abcdefg" ; String b="hijklmn" ; string c=a+b;
上述代码将"abcdefghijklmn"赋值给c。
当将一个字符串与一个非字符串的值进行拼接时,后者被转换成字符串
这种特性通常用在输出语句中,比如
1 System.out.println("The answer is " +answer);
如果需要把多个字符串放在一起,用一个定界符分割,可以使用静态join方法
字符串比较
使用equals方法进行字符串比较
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import java.util.Scanner;public class whiletest { public static void main (String[] args) { Scanner myScanner = new Scanner(System.in); String name = "" ; String passwd = "" ; int chance = 3 ; for (int i=0 ; i<3 ;++i){ System.out.println("请输入您的用户名:" ); name = myScanner.next(); System.out.println("请输入您的密码:" ); passwd = myScanner.next(); if ("丁真" .equals(name) && "666" .equals(passwd)){ System.out.println("登陆成功" ); break ; } else { chance--; System.out.println("你还有" +chance+"次登陆机会" ); } } } }
键盘输入语句
首先导入包中的类,然后创建一个对象,接着接收用户输入——不同的数据类型对应了不同的方法
1 2 3 4 5 6 7 8 9 10 11 12 import java.util.Scanner;public class inputtest { public static void main (String[] args) { Scanner myScanner = new Scanner(System.in); System.out.println("请输入你的名字:" ); String name = myScanner.next(); System.out.println("请输入你的年龄:" ); int age=myScanner.nextInt(); } }
程序控制结构
分支控制——单分支、双分支、多分支
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import java.util.Scanner;public class controltest { public static void main (String[] args) { System.out.println("请输入年龄:" ); Scanner myScanner = new Scanner(System.in); int age = myScanner.nextInt(); if (age > 18 ){ System.out.println("您的年龄大于18" ); } else if (age == 18 ) { System.out.println("您的年龄等于18" ); } else { System.out.println("您的年龄小于18" ); } } }
在分支中还可以嵌套分支结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import java.util.Scanner;public class controltest { public static void main (String[] args) { Scanner myScanner = new Scanner(System.in); System.out.println("请输入成绩:" ); double score = myScanner.nextDouble(); if ( score > 8.0 ){ System.out.println("请输入性别:" ); char gender = myScanner.next().charAt(0 ); if ( gender == '男' ){ System.out.println("你是男的" ); } else { System.out.println("你是女的" ); } } else { System.out.println("您出局了" ); } } }
switch分支结构
每个case中的类型应该一致,也需要和switch中的类型(只能是byte,short,int,char,String,enum)一致
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import java.util.Scanner;public class switchtest { public static void main (String[] args) { Scanner myScanner = new Scanner(System.in); System.out.println("请输入a-g中的字符" ); char input = myScanner.next().charAt(0 ); switch (input){ case 'a' : System.out.println("星期一" ); break ; case 'b' : System.out.println("星期二" ); break ; case 'c' : System.out.println("星期三" ); break ; case 'd' : System.out.println("星期四" ); break ; case 'e' : System.out.println("星期五" ); break ; case 'f' : System.out.println("星期六" ); break ; case 'g' : System.out.println("星期天" ); break ; default : System.out.println("输入错误" ); break ; } } }
循环分支结构
for循环
1 2 3 4 5 6 7 public class fortest { public static void main (String[] args) { for (int i=0 ;i<10 ;++i){ System.out.println("helloworld" ); } } }
while循环
1 2 3 4 5 6 7 8 9 public class whiletest { public static void main (String[] args) { int n=0 ; while (n<10 ){ System.out.println("hello" ); n++; } } }
do……while循环
也是和C一样的
打印空心金字塔
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import java.util.Scanner;public class whiletest { public static void main (String[] args) { for (int i=0 ;i<5 ;++i){ for (int k=0 ;k<4 -i;++k){ System.out.print(' ' ); } for (int j=0 ;j<2 *i+1 ;++j){ if (j==0 ||j==2 *i||i==4 ){ System.out.print("*" ); } else { System.out.print(" " ); } } System.out.println(); } } }
数组、排序和查找
数组
数组创建后,如果没有赋值,有默认值
1 2 3 4 5 6 7 8 9 class arraytest { public static void main (String[] args) { int a[] = new int [3 ]; int b[]={1 ,2 ,3 }; for (int i=0 ;i<a.length;++i){ System.out.println(a[i]); } } }
数组扩容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class arraytest { public static void main (String[] args) { int arr[] = {1 ,2 ,3 }; Scanner myScanner = new Scanner(System.in); do { System.out.println("您是否需要继续添加元素(Y/N)" ); char a = myScanner.next().charAt(0 ); if ( a == 'Y' || a == 'y' ){ System.out.println("请输入你想添加的元素" ); int add = myScanner.nextInt(); int arrNew[]= new int [arr.length+1 ]; for (int i=0 ;i<arr.length;i++){ arrNew[i] = arr[i]; } arrNew[arrNew.length-1 ] = add; arr = arrNew; System.out.println("添加元素后的数组为" ); for (int i=0 ;i<arr.length;++i){ System.out.println(arr[i]); } } else { System.out.println("退出成功" ); break ; } }while (true ); } }
二维数组的静态初始化,和C不同的是需要加{}来限制为一维数组,否则会报错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class arraytest { public static void main (String[] args) { int arr[][] = { {1 ,2 ,3 ,4 ,5 }, {1 ,2 ,3 ,4 ,5 }, {2 ,3 ,4 ,5 ,6 }, {4 ,5 ,6 ,7 ,8 } }; for (int i=0 ;i<arr.length;i++){ for (int j=0 ;j<arr[i].length;++j){ System.out.print(arr[i][j]+" " ); } System.out.println("" ); } } }
二维数组的动态初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class arraytest { public static void main (String[] args) { int arr[][]=new int [3 ][]; for (int i=0 ;i<arr.length;++i){ arr[i]=new int [i+1 ]; for (int j=0 ;j<arr[i].length;++j){ arr[i][j]=i+1 ; } } for (int i=0 ;i<arr.length;++i){ for (int j=0 ;j<arr[i].length;++j){ System.out.print(arr[i][j]+" " ); } System.out.println(); } } }
杨辉三角
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class arraytest { public static void main (String[] args) { int arr[][]=new int [10 ][]; for (int i=0 ;i<arr.length;++i){ arr[i]=new int [i+1 ]; } for (int i=0 ;i<arr.length;++i){ for (int j=0 ;j<arr[i].length;++j){ if (j==0 ||j==i){ arr[i][j]=1 ; } else { arr[i][j] = arr[i-1 ][j]+arr[i-1 ][j-1 ]; } System.out.print(arr[i][j]+" " ); } System.out.println(); } } }
排序
冒泡排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Sorttest { public static void main (String[] args) { int arr[] = {4 ,3 ,6 ,7 ,9 ,5 }; for (int i=0 ; i < arr.length-1 ; i++){ for (int j = 0 ; j < arr.length-i-1 ; j++){ if (arr[j]>arr[j+1 ]){ int tmp = arr[j]; arr[j] = arr[j+1 ]; arr[j+1 ] = tmp; } } } for (int i=0 ;i<arr.length;i++){ System.out.println(arr[i]); } } }
查找
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Insert { public static void main (String[] args) { int arr[] = {10 ,12 ,30 ,45 ,90 }; int index=-1 ; for (int i=0 ;i<arr.length;++i){ if (arr[i]==30 ){ index=i; break ; } } if (index==-1 ){ System.out.println("未找到" ); } else { System.out.println("其所在下标为" +index); } } }
面向对象编程
类与对象
引入类与对象的原因是同一个对象可能有多种属性和行为,类就是数据类型(我们自己定义的),而对象就是一个具体的实例
类是抽象的,概念的。代表一类事物
对象是具体的,实际的,代表一个具体事物,即实例
类是对象的模板,对象是类的一个个体,对应一个实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Object01 { public static void main (String[] args) { Cat cat1 = new Cat(); cat1.name ="小白" ; cat1.age = 3 ; cat1.color="白色" ; Cat cat2 = new Cat(); cat2.name ="小花" ; cat2.age = 10 ; cat2.color="花色" ; System.out.println("第一只猫的信息" +" " +cat1.name+" " +cat1.age+" " +cat1.color); System.out.println("第二只猫的信息" +" " +cat2.name+" " +cat2.age+" " +cat2.color); } } class Cat { String name; int age; String color; }
对象在内存中的存储
在执行创建类过程中,会加载类的属性信息和方法信息。对于属性信息,根据类型不一样分配不同的空间,字符串存储指向常量池的地址,基本数据类型则直接存储
属性和细节
属性也可以称为成员变量或者filed(字段) ,属性可以是对象也可以是基本数据类型
属性的定义语法同变量,示例:访问修饰符(public/private/protected/默认)-用于控制属性的访问范围,属性类型,属性名
类与对象的内存分配机制
new是在堆中开辟空间,而new的返回值是开辟空间的地址,也就是对象,而cat2是对象名,所以我们可以直接将cat2赋值给新的对象名(相当于地址拷贝),如
Java内存的结构分析
栈:一般存放基本数据类型
堆:存放对象(Cat cat,数组等)
方法区:常量池(常量,比如字符串),类加载信息
Java创建对象的简单分析
先加载类信息(属性和方法信息,只会加载一次)
在堆中分配空间new(),进行默认初始化
把地址赋值给对象名,p就指向对象
进行指定初始化(属性赋值)
成员方法
相当于定义在类中的函数
基本定义和使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Method01 { public static void main (String[] args) { Person person1 = new Person(); person1.speak(); } } class Person { String name; int age; public void speak () { System.out.println("我是一个好人" ); } }
方法的调用机制
1 2 3 4 5 6 7 8 Person p1 = new Person(); int returnRes = p1.GetSum(10 ,20 );public int Getsum (int num1,int num2) { int res = num1 + num2; return res; }
new一个对象在堆中开辟空间
调用Getsum方法后开辟新栈,将参数通过栈传递,进行操作后返回到调用方法的位置
成员方法的好处
对于需要多次进行的操作,方法能够减少代码的冗杂度
方法使用细节
如果想让方法返回多个结果,可以使用数组存储返回值,也就是说返回值可以是基本数据类型,也可以是引用数据类型
实参和形参的类型必须相同或兼容、个数、顺序需要一致
方法体(方法内部)中不能再定义其他方法,也就是说方法不能嵌套定义
对于同一类中的方法可以直接调用,而不同类的方法调用需要通过对象名调用 。跨类的方法调用和访问修饰符有关
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Method01 { public static void main (String[] args) { Person2 person2 = new Person2(); int n=0 ; person2.m(); } } class Person { String name; int age; public void speak () { System.out.println("speak" ); } public void cal01 (int n) { speak(); System.out.println("cal01" ); } } class Person2 { public void m () { Person person1 = new Person(); person1.cal01(0 ); System.out.println("m类调用" ); } }
方法传参机制
当传递的是基本数据类型时,传递的只是值,传递参数的地址的值不变,也就是值拷贝、值传递 ,此时形参的变化不会影响实参
而引用类型传递的是地址的值,可以在方法中对实参的值进行修改,这就是引用传递
方法递归调用
即在方法体中调用自己,注意使用递归时要有递归出口 ,并且需要向推出递归的条件逼近
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import java.util.Scanner;public class Javatest { public static void main (String[] args) { } } class Method01 { public static void main (String[] args) { test test1 = new test(); test1.Recursion01(10 ); } } class test { public void Recursion01 (int n) { if (n>2 ){ Recursion01(n-1 ); } System.out.println(n); } }
使用递归解决迷宫问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 class Map { public static void main (String[] args) { int map[][] = new int [8 ][7 ]; for (int i=0 ;i<7 ;++i){ map[0 ][i]=1 ; map[7 ][i]=1 ; } for (int i=0 ;i<8 ;++i){ map[i][0 ]=1 ; map[i][6 ]=1 ; } map[3 ][2 ]=1 ; map[3 ][1 ]=1 ; for (int i=0 ;i<8 ;++i){ for (int j=0 ;j<7 ;++j){ System.out.print(map[i][j]+" " ); } System.out.println(); } test test1 = new test(); boolean final1= test1.Findway(map,1 ,1 ); System.out.println("\n=======找路的情况如下=======" ); for (int i=0 ;i<8 ;++i){ for (int j=0 ;j<7 ;++j){ System.out.print(map[i][j]+" " ); } System.out.println(); } } } class test { public boolean Findway (int map[][],int i,int j) { if (map[6 ][5 ]==2 ){ return true ; } else { if (map[i][j]==0 ){ map[i][j]=2 ; if (Findway(map,i+1 ,j)){ return true ; } else if (Findway(map,i,j+1 )){ return true ; } else if (Findway(map,i-1 ,j)){ return true ; } else if (Findway(map,i,j-1 )){ return true ; } else { map[i][j]=3 ; return false ; } } else { return false ; } } } }
回溯
使用上面的迷宫寻路问题来理解回溯,假设在该位置添加一个障碍物
在(1,1)位置往下走到(2,1),此时(2,1)左右下都为1,走不了,而上为2表示已经走过了,不需要再走,所以将(2,1)标为3,return false表示判断完毕,鉴定为不能走,然后回溯到(1,1)位置,因为在(1,1)位置只尝试了向下走,还未尝试其他方向
方法重载
java中允许同一个类中,多个同名方法的存在,但要求形参列表不一致(形参类型或者个数) ,返回类型不同不构成方法的重载 ,比如System.out.println(),out是PirntStream类型
重载的好处:
可变参数
Java允许将同一个类中多个同名同功能但参数个数不同 的方法,封装成一个方法
基本语法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class Overload1 { public static void main (String[] args) { HspMethod m = new HspMethod(); System.out.println(m.sum(1 ,5 ,100 )); System.out.println(m.sum(1 ,3 )); } } class HspMethod { public int sum (int ... nums) { int res = 0 ; for (int i=0 ;i<nums.length;++i){ res+=nums[i]; } return res; } }
使用细节
可变参数的实参可以为数组,可变参数的本质就是数组
可变参数可以和普通类型的参数一起放在形参列表,但必须宝恒可变参数在最后
一个形参列表中只能出现一个可变参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Overload1 { public static void main (String[] args) { HspMethod m = new HspMethod(); int arr[]={1 ,2 ,3 }; int arr1[]={2 ,3 ,4 }; m.sum(arr); } } class HspMethod { public void sum (int ... nums) { System.out.println("长度为:" +nums.length); } }
作用域
可以参考C语言的作用域:全局变量无需初始化,都可以使用,而局部变量只能在函数内部使用,需要初始化
在java中,主要的变量就是属性(成员变量)和局部变量
局部变量一般是指在成员方法中定义的变量
Java中作用域的分类:全局变量:也就是属性,作用域为整个类体;局部变量:也就是除了属性之外的其他变量,作用域为定义它的代码块中
全局变量可以不赋值,直接使用,因为有默认值,局部变量必须赋值后,才能使用,因为没有默认值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class Overload1 { public static void main (String[] args) { Cat cat1 = new Cat(); cat1.cry(); } } class Cat { int age = 10 ; public void cry () { int n=10 ; String name = "jack" ; System.out.println(age); } }
作用域使用细节
属性和局部变量可以重名,访问时遵循就近原则
在同一个作用域中,两个局部变量或两个属性不能同名
属性生命周期较长,伴随对象的创建而创建,而局部变量生命周期较短,伴随着他的代码块的执行而创建,即在一次方法调用过程中
修饰符不同:全局变量/属性可以添加修饰符,局部变量不能添加修饰符
全局变量/属性:可以被本类使用,或其他类使用(通过对象调用),局部变量只能在本类中对应的方法被调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import java.util.Scanner;public class Overload1 { public static void main (String[] args) { Person person1 = new Person(); person1.cry(); Person2 person2 = new Person2(); person2.test(person1); } } class Person { int age = 10 ; String name = "jack" ; public void cry () { int n=10 ; System.out.println(name); } } class Person2 { public void test (Person p) { System.out.println(p.name); } }
构造器
构造方法又叫构造器,主要用于完成对新对象属性的初始化
构造器的修饰符可以默认,也可以是public、protected、private
构造器没有返回值
方法名和类名字必须相同
参数列表和成员方法一样的规则
在创建对象的时候,系统自动调用该类的构造器完成对对象的初始化
基本语法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Overload1 { public static void main (String[] args) { constructor c1 = new constructor("jack" ,30 ); } } class constructor { String name; int age; public constructor (String pName,int pAge) { System.out.println("构造器被调用" ); name = pName; age = pAge; } }
构造器使用细节
一个类可以定义多个不同的构造器,即构造器重载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class Overload1 { public static void main (String[] args) { constructor c1 = new constructor("jack" ,30 ); constructor c2 = new constructor("kim" ); } } class constructor { String name; int age; public constructor (String pName,int pAge) { System.out.println("构造器被调用" ); name = pName; age = pAge; } public constructor (String pName) { name =pName; } }
如果没有定义构造器,系统会自动给类生成一个默认无参构造器(也叫默认构造器),即Person(){}。一旦定义了构造器,默认的构造器就会被覆盖
定义一个空类,使用javap对空类进行反编译,可以看到在类中有一个默认构造器(反编译出来的是构造器的声明)
this关键字
java虚拟机会给每个对象分配this,代表当前对象
简化理解:每个人都可以说"我的",但是每个人说出来的"我的"是不同的,所以每个对象的this都不一样,但是可以使用this代表当前对象
1 2 3 4 5 6 7 8 9 10 11 class constructor { String name; int age; public constructor (String name,int age) { this .name = name; this .age = age; } public constructor (String pName) { name =pName; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import java.util.Scanner;public class Overload1 { public static void main (String[] args) { constructor c1 = new constructor("jack" ,30 ); System.out.println(c1); } } class constructor { String name; int age; public constructor (String name,int age) { this .name = name; this .age = age; System.out.println(this ); } public constructor (String pName) { name =pName; } }
通过上述结果打印的结果可知,this指向自己
下图是this在内存中的表示
this使用细节
this关键字可以用来访问本类的属性、方法、构造器
this用于区分当前类的属性和局部变量
访问成员方法的语句:this.方法名(参数列表)
访问构造器语法:this(参数列表);注意只能在构造器中使用(即只能在构造器中访问另外一个构造器),并且必须放在构造器的第一行 ,注意不能在普通方法使用上述语句。this()可以在构造器中调用本类中的其他构造器
this不能在类定义的外部使用,只能在类定义的方法中使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Overload1 { public static void main (String[] args) { constructor c1 = new constructor(); } } class constructor { String name; int age; public constructor (String name,int age) { System.out.println("另一个构造器被调用" ); } public constructor () { this ("jack" ,100 ); System.out.println("第一个构造器被调用" ); } }
包
包的三大作用
区分相同的类
当类很多时,可以很好地管理类(将功能相同或者近似的类放入同一个包中进行管理)
控制访问的范围-访问修饰符
包的基本语法
package com.hsperdu;
package关键字,表示打包
com.hsperdu包名
包的原理
包的本质实际上就是创建不同的文件夹/目录来保存类文件
由于两个Dog类属于两个不同的包,所以可以同时存在
包的案例
不同包下的Dog类
我们先在资源文件目录下创建两个包,名称为com.xiaoming和com.xiaoqiang,可以看到它自动生成了com文件夹,文件夹中存储两个我们需要的包
我们可以同时在两个包中创建Dog类,其放在了两个不同的包
我们可以导入不同的包进而使用其中的类,也可以使用包名指定使用包中的类 ,对于两个相同的类,只能导入其中一个包
包的命名和命名规范
命名规则
只能包含数字、字母、下划线、小圆点,但不能用数字开头,不能是关键字或保留字
命名规范
一般是小写字母+小圆点
一般是com.公司名.项目名.应用模块名
常用的包
java.lang //基本包,默认引入,不需要再引入
java.util //util包,系统提供的工具包、工具类,使用Scanner
java.net //网络包、网络开发
java.awt //是做java的界面开发,GUI
包的使用细节
1 2 import java.util.Scanner;import java.util.*;
但是建议需要什么类就导入什么类即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.hspedu.pkg;import java.util.Scanner;import java.util.*;public class Import01 { public static void main (String[] args) { int [] arr = {-1 , 20 , 2 , 13 , 3 }; Arrays.sort(arr); for (int i = 0 ; i < arr.length; i++) { System.out.print(arr[i] + " " ); } } }
package的作用是声明当前类所在的包,需要放在类的最上面,一个类中最多只有一句package
import指令位置放在package的下面,在类定义前面,可以有多句且无顺序要求
表明Import01这个类属于com.hspedu.pkg这个包中
访问修饰符
公开级别:用public 修饰、对外公开
受保护级别:用protected 修饰,对子类和同一个包中的类公开
默认级别:没有修饰符号,向同一个包的类公开
私有级别:用private 修饰,只有类本身可以访问,不对外公开
使用的注意事项
修饰符可以用来修饰类中的属性,成员方法以及类
只有默认的和public才能修饰类,并且遵循上述访问权限的特点
成员方法的访问规则和属性完全一样
封装
把抽象出的数据(属性)和对数据的操作(方法)封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作(方法),才能对数据进行操作
封装的理解和好处
隐藏方法的实现细节
可以对数据进行验证,保证安全合理
封装的实现
将属性进行私有化[不能直接修改属性]
提供一个公共的set方法,用于对属性判断并赋值
提供一个公共的get方法,用于获取属性的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 package com.hspedu.Encapsualtion01;public class Encapsulation1 { public static void main (String[] args) { Person person = new Person(); person.setName("jackkkkk" ); person.setAge(200 ); person.setSalary(30000 ); System.out.println(person.info()); } } class Person { public String name; private int age; private double salary; public String getName () { return name; } public void setName (String name) { if (name.length() <= 6 && name.length() >= 2 ) { this .name = name; } else { System.out.println("名字长度出错,默认为Admin" ); this .name = "Admin" ; } } public int getAge () { return age; } public void setAge (int age) { if (age >= 1 && age <= 120 ) { this .age = age; } else { System.out.println("设置的年龄不对,需要在1-120之间,默认设置为18" ); this .age = 18 ; } } public double getSalary () { return salary; } public void setSalary (double salary) { this .salary = salary; } public String info () { return "name=" + this .name + " age=" + this .age + " salary=" + this .salary; } }
将构造器与set方法结合
当直接使用构造器,会发现不会对我们传入的参数进行检查,此时Set方法无效了。所以我们可以将Set方法写在构造器中,对传入的数据进验证
1 2 3 4 5 6 7 Person person = new Person("jack" ,18 ,10000 ); public Person (String name, int age, double salary) { setName(name); setAge(age); setSalary(salary); }
继承
使用继承来解决代码复用性 ,当多个类存在相同的属性和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可
比如在两个类中具有类似方法和三个完全相等的属性
Pupil类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class Pupil { public int age; public String name; private double score; public void setScore (double score) { this .score = score; } public void testing () { System.out.println("小学生" + name + "正在考小学数学" ); } public void showInfo () { System.out.println("小学生名" + name + " 年龄" + age + " 分数" + score); } }
Graduate类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class Graduate { public int age; public String name; private double score; public void setScore (double score) { this .score = score; } public void testing () { System.out.println("大学生" + name + "正在考大学数学" ); } public void showInfo () { System.out.println("大学生名" + name + " 年龄" + age + " 分数" + score); } }
继承关系图
此时A类为父类(基类),B、C类为子类(派生类)
继承的基本语法
两个子类
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Pupil extends Student { public void testing () { System.out.println("小学生" + name + "正在考小学数学" ); } } public class Graduate extends Student { public void testing () { System.out.println("大学生" + name + "正在考大学数学" ); } }
父类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Student { public int age; public String name; private double score; public void setScore (double score) { this .score = score; } public void showInfo () { System.out.println("小学生名" + name + " 年龄" + age + " 分数" + score); } }
Test
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class ExtendTest { public static void main (String[] args) { Pupil pupil = new Pupil(); pupil.name="小明" ; pupil.age=10 ; pupil.testing(); pupil.setScore(100 ); pupil.showInfo(); Graduate graduate = new Graduate(); graduate.name="大明" ; graduate.age=20 ; graduate.testing(); graduate.setScore(120 ); graduate.showInfo(); } }
继承的好处
代码的复用性提高了
代码的扩展性和维护性提高了
继承的细节
子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法不能在子类直接访问,要通过公共的方法去访问 (父类提供公共方法进行访问)
在父类中设置get方法来访问private属性、也可以在父类中定义公共方法调用private方法
子类必须调用父类的构造器,完成父类的初始化
1 Pupil pupil = new Pupil();
当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会先去调用 父类的无参构造器 。如果父类中没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作
当父类使用有参构造器,需要使用super来指定父类中的构造器(通过参数) ,且super()需要放在子类构造器的第一行 ,先对父类进行初始化再进行子类的初始化,所有的构造器如果没有写super()和this(),第一行默认都有super(),不过被省略了
如果希望指定去调用父类的某个构造器,则显式的调用一下:super(参数列表) 。也就是当父类有多个构造器时,通过super中的参数列表来指定使用父类中的构造器
super()和this()都只能放在构造器的第一行,因此不能同时存在
java所有类都是Object 的子类,Object是所有类的基类
父类构造器的调用不限于直接父类,将一直往上追溯直到Object类(顶级父类)。也就是说子类会调用父类的构造器,而父类也会先调用父类的父类的构造器 ,一直往上
子类最多只能继承一个父类(直接继承),即java中是单继承机制
不能滥用继承,子类和父类之间必须满足is a的逻辑关系,如猫 is a 动物
继承的本质
当父类和父类的父类具有相同的属性时,访问时采取就近原则 ,若此时我们需要访问的属性在父类中是private,则会报错,并且不会去爷爷类找
super关键字
super代表父类的引用,用于访问父类的属性、方法、构造器
访问父类的属性、方法,但不能访问父类的private属性和方法
访问父类的构造器(前面用过),super(参数列表),只能放在构造器的第一句