JAVA基础-2

JAVA基础-2

不会有人跨年还在学java吧?不会吧?

java好无聊,我爱java,java好无聊,我爱java,java好无聊,我爱java,java好无聊,我爱java,java好无聊,我爱java,java好无聊,我爱java。

命令行参数

1.命令行参数:它是指给main方法传递的参数,叫做命令行参数
2.如何给main方法的形参(String[] args)传值
(1)命令行中
java 类名 参数1 参数2 参数3
(2)idea
在run菜单,Edit Configurations
填写mainclass,然后填写program arguments:参数之间使用空格分隔

方法的参数传递机制

方法的实参负责给形参赋值:
(1)形参是基本数据类型,那么实参给形参的是数据指,而且是书举止的“副本”。
(2)形参是引用数据类型,那实参给形参传递的是地址值,而且是地址值的“副本”。
方法的参数传递机制:
1.方法的形参是基本数据类型时,形参的值的改变不会影响实参。
2.方法的形参是数据应用类型时,形参地址值的改变不会影响实参,但是形参地址值里面的数据改变会影响实参。比如,修改数组元素的值,或修改对象的属性值。

一个扩展小栗子:

public class test {
    public static void main(String[] args) {
        System.out.println("hello world");
        int i = 0;
        new test().change(i);//这是一个匿名对象,目的就是为了执行一次change方法。而方法影响的是基本数据类型,所以基本数据类型值不会变,他们都不是一个内存地址。

        i = i++;//先取i的值,放到操作数栈中。i自增为1。把操作数栈中的0赋值给i。所以最后结果还是0
        System.out.println("i="+i);//输出0
    }

    void change(int i ){
        i++;
    }
}

方法的重载(Overload)

1.方法的重载,当一个类中出现了方法名相同,新参列表不同的两个或多个方法,称为方法的重载。
例如:
定义方法求两个整数的最大值。
定义方法求三个整数的最大值。

方法的递归调用

当方法自己调用自己就是递归。

必须有出口,不能无限递归,否则会出现把栈内存耗尽。

额感觉没什么写的,和py差不多,可以参考以前的文章。

对象数组

之前的数组,都是存储的int类的,基本数据类型的数组。
而这对象数组,元素是引用数据类型。
//演示:存储五个圆对象存储到数组当中
//首先创建一个圆对象,代码如下
package lianxi;

public class Circle {
    double R;//半径

    double jisuanS(double R){
        double S = Math.PI*2*R*R;
        return S;
    }//计算面积

    double jisuanL(double R){
        double L = Math.PI*2*R;
        return L;
    }

    String info(){
        return ("半径:"+R+";面积:"+jisuanS(R)+";周长:"+jisuanL(R));
    }
}
//接下来需要创建五个圆的对象,来存到数组当中去。


package lianxi;

public class diaoyong {
    public static void main(String[] args) {
        //先声明和创建数组,长度为5
        //元素的类型:Circle
        //以为数组的声明和动态初始化的公式
        //元素的数据类型[] 数组名 = new 元素的数据类型[长度]
        Circle[] arr = new Circle[5];
        //元素类型是Cilrcle,那么必须赋值为Circle的对象
        for (int i = 0;i< arr.length;i++){
            arr[i] = new Circle();
            arr[i].R = Math.random()*9+1;//[1,10)
            System.out.println(arr[i].info());//调用类中的方法
        }
    }
}

面向对象-中级

面向对象的基本特征:封装性

面向想对象这个编程思想,有三个基本特征:
1.封装
2.继承
3.多态
什么封装性?
1.隐私->安全,当某个类的代码,内部某些成员不希望外部知晓,或者是随意的更改,那么就可以进行适当的封装。
2.外部使用更方便
    生活中,洗衣机,只会留出几个简单的按钮和进水口出水口,放衣服的地方。隐藏了内部复杂的结构。
    代码中同样如此,String类,它内部的结构和实现是很复杂的,但是我们就算不是特别清楚每一个内部实现。也不影响我们简单的使用。

把代码(成员等)控制在它的合理的可见性范围内,就是一个封装。

如何实现封装?
必须依赖于权限修饰符,或者又称为访问控制修饰。
权限修饰符有哪些?
public\protected\private\和什么都没有写

可见性范围分为四种情况:

image-20221220172200991

属性私有化和get、set方法

成员变量选择哪种权限修饰符?
实际应用当中,习惯上,先声明为private,称为属性私有化
但是如果确实这个成员变量需要扩大它的可见性范围,那么可以把private修改为其他合适的修饰符。
例如:扩大到本包,那么就是缺省
例如:扩大到其他包的子类中,那就是protected
例如:扩大到任意位置,那么就是public
因为属性就是代表对象的数据,对象的数据是非常重要的。不应该完全暴露,至少要可控。
如何使用私有化的属性
如果这个属性确实要被外部使用,需要停工get或set方法。
get方法:供调用者获取属性值的方法
set方法:供调用者修改属性值的方法

get/set方法要注意的地方:
1.如果方法中有局部变量(通常是形参)与对象的实例变量“重名”,那么需要在实例变量前面加上"this."
2.get/set的方法名不能随便写,通常都是get+属性名,并且属性名首字母大写。
3.如果属性(实例变量)是boolean类型,那么它对应的get方法换成is。符合英语用词的习惯。


下面给两个栗子(*^▽^*)//java还是好无聊啊,但是还是坚持一下捏
//pri类
package lianxi;

public class pri {
    private String name = "leo";

    public void setName(String name){
        this.name = name;
    }

    public String getName(){
        return this.name;
    }
}
//调用pri类
//接下来需要创建五个圆的对象,来存到数组当中去。


package lianxi;

public class diaoyong {
    public static void main(String[] args) {
        pri test = new pri();
        System.out.println(test.getName());
        test.setName("wanheiqiyihu");
        System.out.println(test.getName());
    }

}

/*输出结果
leo
wanheiqiyihu
*/

IDEA自定义代码模板

image-20221221161107768

在这个功能点,就可以自定义一系列的代码模板。

面向对象的基本特征:继承

1.什么是继承?
重复使用已有的类的代码(代码的复用)
扩展已有类的代码
如何继承
关键字:extends
语法格式
【修饰符】 class 子类名 extends 父类名{

}
子类:SubClass,又称派生类
父类:SuperClass,又称基类,超类

事物范围来说,子类小于父类

继承的特点:
1.子类会继承父类所有的实例变量和实例方法。
A:虽然子类继承了父类所有的实例变量和实例方法,但是因为权限原因,某些成员在子类中不能直接使用。
例如private修饰的成员,在子类中是不能直接使用的。
权限修饰符缺省的成员,在跨包的子类中是不能直接使用的,本包子类可以。
B:这里说的继承所有,是说,子类代表的这个事物,它具备父类中所描述的所有特征。
C:既然是子类继承了所有的实例变量,那么在子类对象创建时,那么需要在子类对象的堆空间中开辟对应的内存来存它的值。
D:所谓的继承,不是说,父类的代码在子类中完全重复一遍,而是在使用子类时,除了可以使用自己本类声明的成员,还可以去父类中寻找可以使用的成员。

2.java只支持单继承,不支持多继承
一个子类只能有一个“直接”父类。
3.java支持多层继承
也就是可以有爷爷等。

查看继承关系快捷键

例如:选择A类名,按Ctrl + H就会显示A类的继承树。

image-20211230090701383:A类的父类和子类

image-20211230090719430:A类的父类

image-20211230090732532:A类的所有子类

例如:在类继承目录树中选中某个类,比如C类,按Ctrl+ Alt+U就会用图形化方式显示C类的继承祖宗

image-20211229180113255

面向对象基本特征:多态

有的时候,我们在设计一个数组、或一个成员、或一个方法的形参、返回值类型时,无法确定它的具体的类型,只能确定他是某个系列的类型。

什么是多态?
父类类型 变量 = 子类的对象;
下面有个栗子来弄一些

image-20221225121933795

多态的现象:子类重写了父类的方法,通过父类的变量调用重写的方法时,执行的是子类重写后的方法。

编译时类型和运行时类型不一样,编译时时父类的类型,子类时是子类的类型,所以它会执行子类重写的代码。

多态引用的好处与弊端
弊端:编译时,只能调用父类声明的方法,不能调用子类扩展的方法。
好处:运行时,看子类,如果子类重写了方法,一定是执行子类重写的方法体;变量引用的子类对象不同,执行的方法就不同,实现动态绑定。代码编写更灵活,功能更强大,可维护性和扩展性更好了。

所以,一般用多态的时候,需要子类重写方法才可以用,父类记得声明。

向上转型与向下转型

Person p1 = new Man();
p1.eat();
p1.walk();

//p1.smoke();编译时看父类Person,Person类中没有这个方法,编译不通过。
Man m = (Man) p1;//强制类型转换
m.smoke();
向上转型:
person p1 = new Man();
    自动类型转换。
    当把子类对象赋值给父类的变量时,在编译时会自动提升为父类的类型。
向下转型:
Person p1 = new ChineseMan();
Man m = (Man)p1;
    如果需要调用子类扩展的方法时,必须向下转型,通过强制类型转换完成。这样才能通过编译。
向上转型:只要满足,对象是这个变量的子类类型就可以。
向下转型:必须满足,对象的运行时类型<=()中向下转的类型。

但是值得注意的是,成员变量并没有多态,比如说father里面有个a变量,son里面有个a变量,son中的a不会覆盖father中的a。而是son中有2个a变量,但是调用的时候,如果直接调用a,那么就是调用的son中的变量。如果想要调用father中的a,需要像下面这样调用。

Son s = new Son();
sout(((Father)s).a);//把s向上转型为Father类型,让编译器从Father中取a。

虚方法

什么是虚方法?
凡是可以被子类重写的方法都叫做虚方法。
不能重写的方法有:private等(后面还会学习static等

多态现象发生的前提

1.继承
2.有多态引用
父类类型 变量 = 子类的对象;
变形:
    父类的类型[] 数组名 = new 父类的类型[长度];
    数组名[下标] = 子类的对象;
    
形参的类型是父类的类型,实参是子类的对象。

方法的返回值类型是父类的类型,返回到实际结果是子类的对象。

某个成员变量声明的是父类类型,实际接收到是子类对象。
3.有虚方法

构造器

类的第三个成员:构造器

什么是构造器?
【修饰符】 class 类名{
    【修饰符】 类名(){//构造器
    }
    
    【修饰符】 类名(形参列表){//构造器
    }
}


构造器的作用是什么?
1.new对象,即new关键字后面出现的一定是构造器,也只能是构造器。
2.在new对象的同时,给对象的“实例变量”初始化,即赋值。(初始化的时候就不用使用set方法了,但是在后续修改的时候还是需要使用set方法的)

注意:
1.构造器长得像方法,所以又称为构造方法
2.和方法不同的地方
A:构造器的名称不能随意乱写,只能也一定要和类名一致(大小写也要一样)
B:构造器没有返回值类型
C:构造器的修饰符只能是权限修饰符,可以有四中情况,private、缺省、protected、public
构造器的特点:
1.如果一个类没有手动编写任意构造器,那么编译器会将自动添加一个默认的无参构造。而且此时构造器的权限修饰符默认和class类前面的权限修饰符一致。
2.如果你手动编写了任意一种构造器,那么编译器就不会在自动添加一个默认的无参构造。
3.所以如果某个类中编写了构造器,那么在其他类中调用这个类的时候,就不能使用new后面的无参构造了,只能使用编写的构造器进行new。
4.构造器可以重载(可以手动添加无参构造器)

初级栗子

package lianxi;

public class Student {
    String name;
    public Student(String name){//有参构造器
        this.name = name;
    }
}
package lianxi;

public class Test {
    public static void main(String[] args) {
        Student s1 = new Student("dhk");
        System.out.println(s1.name);
    }
}

上面就是定义了Student类中的一个有参构造器,然后在主类中进行调用。

一些小知识

本类构造器相互调用

this():调用本类的无参构造
this(实参列表):调用本类的有参构造
this()和this(实参列表)只能出现在构造器首行
不能出现递归调用

构造器在继承时的要求

问题:
1.父类的构造器会不会继承到子类中?
不会,不管是无参构造器还是有参构造器,都不会继承到子类中去。
2.父类的构造器和子类有没有关系?
A:子类在继承父类时,默认会在子类的构造器首行,加一句代码super();
表示调用父类的无参构造。

B:那么如果父类没有无参构造,子类构造器中没有做特别处理的话,编译是要报错的。

C:那么如果父类没有无参构造,子类构造器中首行一定要明确调用父类的哪个有参构造。通过super(实参列表)的方式确定。

D:如果父类既有无参构造又有有参构造,那么没写super(实参列表)的,就表示调用父类的无参构造。
为什么子类的构造器一定要调用父类的构造器呢?
因为子类会继承父类所有的成员变量,那么在new子类对象时,必须为这些继承成员变量“初始化”,而为这些成员变量初始化,最合适的就是通过父类的构造器。

代码块

类的第四个成员(结构)—代码块

非静态代码块

1.作用:是用来给“实例变量”初始化的
2.意义:把多个构造器中的代码抽取出来,写到代码中,减少代码的冗杂。
小扩展:
在System类中有一个方法可以获取当前系统时间
long System.currentTimeMillis()//距离1970年1月1号0点0分0秒0毫秒的实践差值(时间戳)
如何使用?
用大括号阔起来,就是一个代码块。new对象的时候就会自动执行这个代码块。每new一个对象执行一次。与构造器相比,是代码块部分先执行。值得注意的是,代码块的位置与执行顺序没有关联。

实例初始化的过程

此部分作为了解

实例:对象

实例初始化:对象初始化,就是给对象的“实例变量”初始化。

实例变量:他就是成员变量,准确的说,他是指非静态的成员变量,又经常成为属性。

但是很多文章和教科书都会把属性和实例变量做区分,区分为有get/set方法的实例变量叫属性。
什么时候进行实例初始化呢?
一定实在new对象的时候

那些代码是和实例初始化有关的?
1.实例变量声明后面的显示赋值
2.非静态代码块
3.构造器
实例初始化的过程
1.实例初始化过程其实是在调用一个<init>的方法
2.<init>方法不是由程序员显式声明的,而是由编译器根据上面三个代码部分(构造器等),按照一定的顺序组装而成的。
3.一个类编写了几个构造器,构造器就会组成几个<init>方法,如果程序员没有手动编写任意构造器,那么编译器会更具自动添加到默认的无参构造自动组装一个无参的<init>方法。
<init>方法如何组装以下三个部分代码:
A:实例变量声明后面的显式赋值
B:非静态代码块
C:构造器

(1)先把构造器首行的super()或super(实参列表)提前出来,放到<init>方法的首行
(2)按照程序员编写的A和B的自然顺序,一次组装她两。
(3)把构造器的代码组装在(2)的下面。
总结
1.new调用构造器,本质上是执行它对应的<init>方法
2.每一个构造器都会有自己对应的<init>方法

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。后续可能会有评论区,不过也可以在github联系我。