一:方法
方法是实现某个功能的语句块的集合,Java的方法包含于类与对象中。
1:方法的组成
修饰符:告诉编译器如何调用该方法,定义了该方法的访问类型(所以用访问修饰符),常见的修饰符有,public,protected、缺省默认(default、friendly),private。 返回值类型方法可能会有返回值。类似于:
public static int add(int a ,int b){
return a+b;
}
返回值是int型,所以需要返回int类型。 psvm的返回值类型是void,所以不需要有返回值。 存在返回值时,一定要用return返回出去。 参数类型:参数类型像一个占位符,当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。 形式参数:在方法被调用时用于接收外界输入的数据。 实际参数:调用方法时实际传给方法的数据。
public class Text10{
public static void main(String[] args) {
int sum =add(1 ,2);
}
public static int add(int a ,int b){
return a+b;
}
}
其中1,2是实际参数,而a,b是形式参数。
可变参数 格式:在方法声明中,在制定的参数类型后加一个省略号(…) 一个方法中只能指定一个可变参数,它必须是方法的最后一个参数,任何普通的参数必须在它之前声明.
public void massItem(int...i){
System.out.println(i[0]);
}
打印输入的i的值,i的个数取决于输入的值的个数.
2:方法的调用
对象名.方法名(实参列表) 直接调用 上面的代码完整版如下:
public class Text10 {
public static void main(String[] args) {
int sum =add(1 ,2);
System.out.println(sum);
}
public static int add(int a ,int b){
return a+b;
}
}
因为在同一个类Text10中,所以可以直接调用,即方法名(实参列表)。 注:Java支持两种调用方法的方式,根据方法是否有返回值来选择。 当方法返回一个值的时候,方法调用通常被当作一个值,所以用另一个值进行接收。比如:
int sum =add(1 ,2);
如果方法返回值是void,则方法调用的一定是一条语句: 同样的返回a+b在void中需要写成:
public class Text10{
public static void main(String[] args) {
Cook();
}
public static void Cook(){
int a=1,b=2;
System.out.println(a+b);
}
}
如代码片,void被调用,调用的是语句sop,最后输出3。 不然没有语句也没有返回值,如何执行方法的功能。 实例化类 如果方法在不同的类中,则通过先实例化类再进行调用。 实例化类:对象类型 对象名 = 对象值
public class Test01 {
public static void main(String[] args) {
}
}
public class Test02 {
public void Sing(){
System.out.println("I want my money back.");
}
}
我们在Test01中调用Test02中的方法,先实例化Test02,然后再进行调用:
public class Test01 {
public static void main(String[] args) {
Test02 sing = new Test02();
sing.Sing();
}
}
其中sing为对象名。 如果是静态方法的话,可以直接通过类名.方法名()进行调用 (如何判断是否为静态方法,看是否有static)
public class Test02 {
public static void Sing(){
System.out.println("I want my money back.");
}
}
调用:
public class Test01 {
public static void main(String[] args) {
Test02.Sing();
}
}
递归 递归就是A方法调用A方法,也就是自己调用自己. 递归的结构包括两个部分: 递归头:什么时候不调用自身方法.如果没有头,将陷入死循环. 递归体:什么时候需要调用自身方法.
public static int f(int n){
if(n==1){
return 1;
}else {
return n*f(n-1);
}
}
如图,求n的阶乘,就是用f方法调用f方法来进行相乘.
静态方法与非静态方法 如何区分是静态方法还是非静态方法,就是看它有没有static.
public class Text {
public static void main(String[] args) {
}
public static int add(int a,int b){
return a+b;
}
public void Input(){
System.out.println("哈哈");
}
}
值传递和引用传递 Java是值传递.
3:方法的重载
简单来说就是方法的名字不变,改变方法的参数类型。比如将上文中的int类型改为浮点型:
public static double add(double a ,double b){
return a+b;
}
方法重载的规则 ~方法名称必须相同 ~参数列表必须不同(个数不同,或类型不同,参数排列顺序不同等) ~方法的返回类型可以相同也可以不相同 ~仅仅返回类型不同不足以成为方法的重载。
二:数组
1:什么是数组
数组是相同类型数据的有序集合,数组描述的是相同类型的若干数据,按照一定的先后次序排列组合而成.其中,每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问它们. 数组的四个基本特点 1:其长度是确定的,数组一旦被创建,它的大小就是不可以改变的. 2:其元素必须是相同类型,不允许出现混合类型. 3:数组中的元素可以是任何数据类型,包括基本类型和引用类型.
4:数组变量属于引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量.数组本身就是对象,Java中对象是在堆中的,因此数组无论保持原始数据还是其他对象类型,数组本身是在堆中的.
声明数组 其中dataType为数组类型,arrayRefVar为数组名称. 代码:
int nums[];
int[] nums;
创建数组 使用new操作符来创建,语法如下: arraySize指数组空间的大小
int nums = new int[10];
int[] a={1,21,17,81,34,25};
这个为静态初始化,空间的大小与数组的值都将不发生改变. 如何将数组中的值取出. 数组名[想要取出的是几号数] 比如我们想取出21,则打印:
System.out.println(a[1]);
数组是从0开始的,21虽然是第二个数字也需标记为1. 动态初始化如下: 遍历数组 array.length为数组的长度.
int add=0;
nums[0]=1;
nums[1]=2;
nums[2]=3;
nums[3]=7;
nums[4]=11;
for (int i = 0; i < nums.length; i++) {
add+= nums[i];
}
System.out.println(add);
动态初始化包含默认初始化 查找数组中值最大的数:
int max = nums[0];
for (int j = 0; j < nums.length; j++) {
if (nums[j] > nums[0]) {
max = nums[j];
}
}
System.out.println(max);
2:三种初始化及内存分析
声明数组,其实就是在栈中开辟一个空间,而给数值进行初始化,则是在堆里分内存.
三种初始化:1:静态初始化,2:动态初始化,3:引用初始化. 其中静态与动态我们上文中已经说过了,现在我们来看一下引用初始化: 首先我们要创建一个名叫Man的类,然后初始化数组: 数组的边界 数组下标越界异常;
int add=0;
nums[0]=1;
nums[1]=2;
nums[2]=3;
nums[3]=7;
nums[4]=11;
for (int i = 0; i <= nums.length; i++) {
add+= nums[i];
}
System.out.println(add);
还是拿这个来举例,此数组的长度为5,当i等于nums.length时i为5,但是因为数组是从零开始的,只能指向4,i等于5是产生数组下标越界. 数组的使用 a:加强for循环来对数组进行遍历:
int[] array ={1,3,6,7,9,8};
for(int x :array){
System.out.print(x+" ");
}
b:输出反转数组
public static int[] reverse(int array[]){
int rArray[];
rArray = new int[array.length];
for (int i = 0,j=array.length-1; i < array.length; i++,j--) {
rArray[j]=array[i];
}
return rArray;
}
3:多维数组
多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一堆数组,其每一个元素都是一个一维数组. 创建一个多维数组并对数组的值进行输出:
public static void main(String[] args) {
int[][] arrays = {{1,2},{2,3},{4,7},{9,1},{5,6},{3,2}};
for (int i = 0; i <arrays.length ; i++) {
for (int j = 0; j < arrays[i].length; j++) {
System.out.print(arrays[i][j]+" ");
}
}
}
Arrays类 查看JDK帮助文档:待补
a:对数组进行排序:Arrays.sort(); b:打印数组:使用toString c:对数组进行填充:fill d:判断两数组是否完全相等.(数组的每一个元素都相同)equals
public static void main(String[] args) {
int a[]={23,24,31,9,7,26,4,31};
int d[]={3,4,31,9,72,2,41,3};
Arrays.sort(a);
System.out.println(Arrays.toString(a));
String b[]=new String[7];
Arrays.fill(b,2,4,"Hello");
System.out.println(Arrays.toString(b));
if(Arrays.equals(a,d)){
System.out.println("他们相等耶");
}else {
System.out.println("他们不相等耶");
}
}
4:冒泡排序:
比较相邻两个元素的大小,如果更大,则交换两元素的位置. 因为是嵌套循环,直接可以算出时间复杂度为O(n2).
public class Text20 {
public static void main(String[] args) {
int a[]={6,3,4,23,9,78,65,6,4,34,23,21,59};
int b[]=sort(a);
System.out.println(Arrays.toString(a));
}
public static int[] sort(int arrays[]){
int tom = 0;
for (int i = 0; i <arrays.length-1 ; i++) {
boolean flag = false;
for (int j = 0; j < arrays.length-1-i; j++) {
while(arrays[j]>arrays[j+1]){
tom=arrays[j];
arrays[j]=arrays[j+1];
arrays[j+1]=tom;
flag = true;
}
}
if(flag==true){
break;
}
}
return arrays;
}
}
输出结果:
5:稀疏数组
当一个数组中大部分元素为0,或者为同一值的数组时,可以使用稀疏数组来保存该数组. 稀疏数组的处理方式是: 记录数组一共有几行几列,有多少个不同值 把具有不同值的元素和行列及值记录在一个小规模的数组中,从而缩小程序的规模. 打印如下的稀疏数组:
public static void main(String[] args) {
int arrays[][]=new int[6][7];
arrays[0][3]=22;
arrays[0][6]=15;
arrays[1][1]=11;
arrays[1][5]=17;
arrays[2][3]=-6;
arrays[3][5]=39;
arrays[4][0]=91;
arrays[5][2]=28;
for (int m = 0; m <arrays.length ; m++) {
for (int n = 0; n <arrays[m].length ; n++) {
System.out.print(arrays[m][n]+" ");
}
System.out.println();
}
int arrays2[][]=new int[9][3];
arrays2[0][0]=6;
arrays2[0][1]=7;
arrays2[0][2]=8;
int count=0;
for (int i = 0; i <arrays.length ; i++) {
for (int j = 0; j <arrays[i].length ; j++) {
if(arrays[i][j]!=0){
count++;
arrays2[count][0]=i;
arrays2[count][1]=j;
arrays2[count][2]=arrays[i][j];
}
}
}
for (int q = 0; q <arrays2.length ; q++) {
for (int k = 0; k <arrays2[q].length ; k++) {
System.out.print(arrays2[q][k]+" ");
}
System.out.println();
}
int arrays3[][]=new int[arrays2[0][0]][arrays2[0][1]];
for (int o = 1; o < arrays2.length; o++) {
arrays3[arrays2[o][0]][arrays2[o][1]]=arrays2[o][2];
}
for (int f = 0; f <arrays3.length ; f++) {
for (int g = 0; g <arrays3[f].length ; g++) {
System.out.print(arrays[f][g]+" ");
}
System.out.println();
}
}
三:什么是面向对象
1:面向对象的本质:以类的方式组织代码,以对象的方式组织(封装)数据。
2:创建对象
使用new关键字创造对象,用new关键字创造时,除了分配内存空间之外,还会给创造好的对象进行默认的初始化以及对类中构造器的调用.
new ImageIcon(resource).getImage();
3:构造器
类中的构造器也称为构造方法,是在进行创建对象的时候就必须要调用的.并且构造器有以下两个特点: 1:必须和类的名字相同 2:必须没有返回类型,也不能写void new一个对象的本质其实是在创建一个构造器
无参构造器与有参构造器
public class Text1 {
String name;
public Text1(){
this.name="王三鸭";
}
public Text1(String name){
this.name = name;
}
}
this代表当前这个类,也就是class Text1,this.name也就是这个类下的name,也就是String name中的name.
构造器可以实例化初始值: 初始值在后续可以进行重新赋值与改变. 如果我们同时有传参与初始化参数,最后输出的会是什么呢: 结果仍然是我们传进去的参数. 一个类即使什么都不写,它也会存在一个方法,显示定义构造器. 注意哦定义有参构造之后,如果想使用无参构造,需要显示的定义一个无参构造.
四:封装
程序设计要追求"高内聚,低耦合".高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用.
封装,即隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别;将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成“类”,其中数据和函数都是类的成员。在电子方面,封装是指把硅片上的电路管脚,用导线接引到外部接头处,以便与其它器件连接。
有了private之后我们想通过对象来调用属性就不行啦.但是我们可以通过getset方法来传参,封装简单来说就是getset方法的使用.
上图还可以改成我们只想要对方拿到我们想给的范围: 输出的年龄为3.
五:继承
继承是面向对象软件技术当中的一个概念,与多态、封装共为面向对象的三个基本特征。继承可以使得子类具有父类的属性和方法或者重新定义、追加属性和方法等。
继承用extends来表示. 1:继承后子类拥有父类的所有方法 2:如果父类是有参构造,则无法调用无参构造,不能直接写super. 3:如何调用父类中的属性? super.属性名 4:方法可以直接调用. 虽然方法可以直接调用,但是方法太多时,我们无法分清哪个是子类的方法哪个是父类的方法,这时可以用super.方法名().
当一个类继承了父类,也意味着继承了父类的父类. 也可以说,当实例化子类时,可以通过子类调用子类的父类的方法: 直接写一个super是在调用父类的构造器.(如果是有参构造器需要在super里传参) 注:1:super构造器必须要放在子类的第一行. 2:super必须只能出现在子类的方法或者构造方法中! 3:super和this不能同时调用构造方法.
补:方法的重写 首先讲一下方法 如果我们有两个类,Soo1继承了Faa1,写成这样的形式: Faa1 b = new Soo1(); 此时b可以调用Soo1和Faa1中的方法,还是只能调用Faa1中的方法呢. 答案是方法的调用看左边,所以b实际上是实例化父类的方法.
再看这个: Faa1 c = new Faa1(); 如果我们想通过c调用Soo1中的方法该怎么办呢. 使用方法的强转: ((Soo1)c).C(); 强转还可以写成:Soo1 d=(Soo1)c; 那什么叫做方法的重写呢? 当子类继承父类,子类可以重写父类的方法: 重写需要满足 1:方法名必须相同 2:参数列表必须相同 3:修饰符:范围可以扩大 public>protected>default>private 4:抛出的异常:范围,可以被缩小,但不能被扩大 不能重写的方法: 1:static方法,属于类,不属于任何实例. 2:被final修饰的,在常量池 3:private修饰
六:多态
在编程语言和类型论中,多态(英语:polymorphism)指为不同数据类型的实体提供统一的接口。 多态类型(英语:polymorphic type)可以将自身所支持的操作套用到其它类型的值上。
instanceof,它的作用是判断两个类之间是否存在父子关系,如果存在则返回true.
System.out.println(object instanceof Object);
System.out.println(object instanceof Soo1);
System.out.println(object instanceof Faa1);
System.out.println(object instanceof String);
Faa1 f = new Soo1();
System.out.println(f instanceof Soo1);
注:object是所有类的父类 所以关系:object>Faa1>Soo1 最后输出的结果:true true false true
但是instanceof只能识别一条支线也就是object>Faa1>Soo1,如果object>Faa1>Father,则object与Father不是父子关系. 同样的因为object与String是另外一条线,所以也返回false. 如果是同级之间,没有任何关系会报错.
类之间的强制类型转换(发生在高转低之间)
Person obj = new Persion();
Student obj1=(Student)obj;
如果是低转高,直接转就好了
Student obj = new Student();
Person person = obj;
匿名代码块
{
}
静态代码块
static{
}
我们来对每个代码块输出进行测试:
public class Exx {
{
System.out.println("这,是一个匿名代码块");
}
static{
System.out.println("这,是一个静态块");
}
public Exx(){
System.out.println("这是一个构造块");
}
public static void main(String[] args) {
Exx e = new Exx();
}
}
输出: 注意,static只执行一次. 二次调用时: 拓展:当一个类被final修饰后就无法被继承. static之间的调用:
抽象类 有abstract在类前叫抽象类,在方法前叫做抽象方法 抽象类里可以有普通方法,普通类里不可以有抽象方法 抽象类不允许调用,只能继承 一旦继承,就要调用抽象类里的所有方法 抽象类无法自我实现,只能通过子类的继承来实现.
来看抽象类与抽象方法:
public abstract class AAA {
public abstract void A();
}
普通类来实现抽象方法:
public class BBB extends AAA{
@Override
public void A(){
}
}
接口 只有规范!自己无法写方法~专业的约束! 约束和实现分离:面向接口编程. 接口不能实例化 接口没有构造方法 imple可以实现多个接口 必须要重写接口中的所有方法 接口的写法:
public interface JieKoo {
void Cook(String name);
void Run(String name);
void Swim(String name);
}
全称为public abstract void run(); 为什么可以省略? 因为接口中的方法默认全部是抽象的.
实现接口:
public class Jii implements JieKoo,JieKou{
@Override
public void Cook(String name){
}
@Override
public void Run(String name){
}
@Override
public void Swim(String name){
}
@Override
public void ToSleep(String name){
}
}
接口中也可以定义属性,接口中定义的属性都是常量: public static final int AGE =99;
七:内部类
外部类就是我们创建的java大类,我们在Java大类中再创建一个类这个类就是内部类.
public class Outer {
int age=10;
public void Ouu(){
System.out.println("这是外部类的方法");
}
public class Inner {
public void Inn() {
System.out.println("这是内部类的方法");
}
}
}
内部类可以调用外部类的私有属性
那如果我们想调用这个类的内部类该怎么调用呢?
//调用外部类
Outer outer = new Outer();
outer.Ouu();
//调用内部类
//1:有static时直接调用
Outer.Inner.Inn();
//2:没有static修饰时,通过外部类来调用内部
outer.new Inner().Inn();
//也可以写成Outer.Inner a = outer.new Inner();
outer.new Inner().getAge();
注:一个Java类中可以有多个class类,但是只能有一个public class类. 局部内部类:在方法中写一个类. 匿名内部类 和内部类的区别:内部类在大类的里面,匿名内部类在大类的外面,其实相当于重新建了一个class.
public class Inner {
public static void main(String[] args) {
new Apple().eat();
}
}
class Apple{
public void eat(){
System.out.println("来个苹果吧");
}
}
匿名内部类还可以是接口,而且我们可以调用接口重写方法:
public class Inner {
public static void main(String[] args) {
new Free(){
@Override
public void Eat(String name) {
}
};
}
}
interface Free{
void Eat(String name);
}
八:异常
首先我们来看一个很常见的错误
public class Text {
public static void main(String[] args) {
new Text().a();
}
public void a(){
b();
}
public void b(){
a();
}
}
什么是异常? 异常是指程序运行中出现的不期而至的各种情况,如:文件找不到、网络连接失败、非法参数等. 异常发生在程序运行期间,它影响了正常的程序执行流程. 但是可喜可贺的是Java把异常当作对象来处理,并定义了一个基类java.lang.Throwable作为所有异常的超类. 在Java API 中已经定义了许多异常类,这些异常类分为两大类,错误Error和异常
抛出异常与捕获异常
public class Text02 {
public static void main(String[] args) {
int a=1;
int b=0;
try{
System.out.println(a/b);
}catch (ArithmeticException e){
System.out.println("程序出现异常,变量b不能为0");
}finally {
System.out.println("finally");
}
}
}
我们看看这时的输出结果: 可以不要finally. catch中填写的是想要捕获的异常类型. 如果不知道填什么就填throwable,因为Throwable的级别最高,能捕获throwable的其他都能捕获. 而且java可以同时不只捕获一个异常写多个catch,但是注意需要把最大的异常写在最下面. 和ifelse差不多,满足一个就不会执行其他else 的内容,满足一个catch,就不会执行其他catch中的内容:
public static void main(String[] args) {
int a=1;
int b=0;
try{
System.out.println(a/b);
}catch (Error e){
System.out.println("程序出现异常,变量b不能为0");
}catch(Exception e){
System.out.printf("Exception");
}catch(Throwable t){
System.out.println("Throwable");
} finally {
System.out.println("finally");
}
}
输出结果: 包裹异常的快捷键: Ctrl+Alt+t
我们还可以主动抛出异常:
public class Text03 {
public static void main(String[] args) {
new Text03().test(3,0);
}
public void test(int a,int b){
if(b==0){
throw new ArithmeticException();
}
System.out.println(a/b);
}
}
也可以主动在方法中抛出异常: (当这个方法中处理不了这个异常的时候)
public class Text03 {
public static void main(String[] args) {
try {
new Text03().test(3,0);
} catch (ArithmeticException e) {
e.printStackTrace();
}
}
public void test(int a,int b)throws ArithmeticException{
if(b==0){
throw new ArithmeticException();
}
}
}
自定义异常
使用Java内置的异常类可以描述在编程时出现的大部分异常情况.除此之外,用户还可以自定义异常.用户自定义异常类,只需要继承Exception类即可.
在程序中使用自定义异常类,大体可分为以下几个步骤: 1:创建自定义异常类. 2:在方法中通过throw关键字抛出异常对象. 3:如果在当前抛出异常的方法处理异常,可以使用try-catch语句捕捉并处理;否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作. 4:在出现异常方法的调用者中捕获并处理异常.
后记
写完了,在写的过程中越来越知道自己想要了解什么.在写自定义异常的时候读懂了异常的源码.在instanceof中开始好奇判断类的关系当父子类绑定后会出现什么样的变化.写完了冒泡排序,另外七个排序是不是也可以开始啦.内部类外部类匿名内部类的堆栈存储方式感觉也可以研究好久. 想观察接口还可以实现什么.为什么内部类的调用和实例化类的调用并不相同. 每一个章节都可以专研好久, 在接下来的学习过程中,决定有更多深入理解与整理的文章.而不是仅仅会用就🆗啦.
|