1.区别
面向过程
优点:容易理解
缺点:代码量越大越难维护
面向对象:
优点:可扩展性强,易维护
缺点:复杂,难理解
2.this关键字
(以下例子和部分截图均来自B站“老男孩IT教育”的视频)
- main方法中默认把类的实例化对象传给了实例方法(run),在实例方法(run)中用this表示,打印对象c的地址和实例方法中打印this的地址是相同的。如下:
- 另外注意点:先赋值实例变量再调用方法 和 先调用方法再给实例变量赋值 结果是不一样的(效果如下图)。
-
this可以帮我们区分实例变量和局部变量:
-
this省略不写时,先找方法里面的局部变量中同名的变量,方法中没有局部变量则默认是找实例变量
- 所以,想明确调用实例变量时,应该得加上this更好,代码可读性也更好(易于区分局部变量和实例变量)
- 另外,如果方法中不定义局部变量,而是在参数中传过来一个跟实例变量同名的变量,没有this时,则这个变量调用的是参数中的变量。(但是加上this的话他就是指向实例变量)
3.构造方法
3.1新建构造方法
(用alt+insert 可以选择有参,无参构造方法)
实例化对象时(new 的时候)自动调用的方法
3.2语法特点:
public 同类名(传参(实例变量)){
this.实例变量 = 参数;
...
}
3.3小提示:
(只有构造方法的public之后是不用指定返回类型的)
java会自动为每个类建一个看不到的无参构造方法(只有编译之后打开class文件才能看到),如果自己建了一个有参构造方法,在new一个对象时,java则不会帮我们新建无参构造方法,可以自己手动创建。
3.4 重载,this()
-
有参构造方法也是方法,它也可以重载
-
当两个构造方法有很多相同参数时,可以在一个构造方法中调用另一个构造方法,用 this(参数) 就可以调用该类中其他构造方法。(如下图)
-
而 this() 则是调用无参构造方法。
4.static
4.1使用
- 用static修饰的变量是类变量,修饰的方法就是类方法,修饰之后虽然可以用该类的对象来调用,修改其值,但是一般用 类名. 变量名/方法 来调用。(类名.变量名/方法 不能调用非static,会报错)
- tip:它在类加载的时候最先加载,所以它的加载会先于new类对象的加载
4.2总结static的特点
-
数据共享,每个类对象都共用同一个static修饰的类变量/方法,
-
属于类,不属于对象
-
优先于非static变量/方法的加载,所以也优先于对象的产生。
-
static(static方法/static代码块/main方法)中都不能用"this." 来调用变量/方法
-
在实例化一个类对象时以下三者的加载顺序:
①先加载static静态代码块(静态构造器) 、静态方法、静态变量
②再加载匿名代码块(通用构造器),如下格式:
{
...(代码)...
}
③后加载构造方法。
tip:①在类加载全过程只执行一次,②和③会在每次new一个对象时执行。(效果如下图控制台输出)
- 因此,在static(静态)的代码块/方法(包括main方法)中不能调用this.非static的部分。(因为"this"关键字是在加载非静态资源的时候才有的,所以在还没加载出来的时候就去调用它肯定会报错)
(在method2中则可以通过 类名.method1()或者 this.method1()或者method1()调用method1)
- 若想在本类中调用static方法,可以直接写方法名,省略"this." 或者 "类名." ,但是想在其他类中的方法调用该类的static方法,则需要在其他类的方法中调用时加上该类的 "类名."(前提是方法有public修饰) 。所以,为了统一,一般在自己类调用自己的static方法时也用"类名." 或者"this."。
因为疏忽,上图的
public static void method1(){
method1();
}
会导致无限递归,不能这样写。
5 访问权限
5.1 public
无论哪个包哪个类都可以访问到。
5.2 private
除了自己类里面, 无论哪个包哪个类都不可以访问。
5.3 default
只有完全相同的包路径(同文件夹)下才能访问到,就算子继承父,他们不在同一包路径下,子也不能访问父的默认变量。
6 Getter,Setter
是封装的核心
一个类的所有属性都用私有private修饰,对外只提供Get和Set方法,所以其他类只能通过这些方法来修改此类中的属性,不能直接访问,保护了这些属性。同时,这个类的每个对象都有对应自己的空间,一个对象通过Setter定义了值不会影响其他对象下这个属性的值(如下图)。而这个类一般就是我们熟悉的实体类。
因为在前面static部分可以知道,static修饰的变量都指向同一个地址,所以如果对同一个static变量进行set方法赋值,最终其值都是最后一次调用Setter时所设置的值。(如下图,两个对象输出的sta变量的值相同)
7 继承
所有类都继承Object类。
7.1特点
子类对象能访问常量final、公共public变量/方法、保护protected变量/方法、默认default变量/方法、static静态变量/方法,不能访问私有private变量/方法。
即非私有的变量/方法都能访问(继承)。
子类中通过 "super."能访问非私有变量/方法 和 非静态变量/方法。(static部分直接用类名.就可以访问)
7.2 回顾static:
虽然可以用 "对象." 来调用static 的东西,但不建议。因为static属于类,1个类中的static的东西只有一个地址,所以无论多少个对象去调用它,也都只对应一个地址。就跟类一样,只有一个,所以一般都用"类名."来调用static的东西。
而且编程工具中也不会推荐用"对象."来调用静态的东西,因为当你输入"对象."时是不会提示static的部分给你选择的。
7.3 重写的东西的访问权限
子类重写时修饰符范围(public,protected,default)(访问权限)可以在父类以上,也可以相同,但不能在父类以下。
重写范围:public>protected>default
子类重写的范围可以放大父类的,不能缩小。
- 抛出异常的范围则相反,可以被缩小,不能被放大。
8 super关键字
- 跟this的联系:
在子类中的方法中使用"this."调用变量,如果子类中没有这个同名变量,则调用父类中的变量,否则调用子类里的。
所以为了更好区分,一般调用父类的东西就用"super."来调用。
子类在加载时先加载隐含的父类对象(super()调用父类的无参构造方法),后加载子类对象(运行子类的构造方法),先后顺序是
public 子类名(){
super() //调用父类构造方法,加载父类对象
...然后才是自己的构造方法... //加载子类对象
}
(所以父类的构造方法一定在子类构造方法的最前面执行),此时this可以调用子类的东西,super可以调用父类的东西。
- 补充:如果父类中写了有参构造方法,则java不会隐含提供无参构造方法,需要在子类的构造方法中最前面显式写出父类的有参构造方法。另外super.还可以调用父类被子类重写了的方法。
9 多态
9.1 概念
同一对象拥有多种形态
9.2 作用
把不同数据类型进行统一
9.3 特点
父类的引用指向子类对象 (父类 xxx = new 子类() )
上面 xxx 可以调用子类重写父类的非静态方法,可以调用父类所有属性和静态方法,但不能直接调用子类独有方法,必须先强转为子类 ((子类)xxx).子类独有方法(); 才能这样调用到。
↑解释
在进行 父类 xxx = new 子类()时
是低转高(子转父),由于子类已经继承了父类的所有,所以此时子类把自己独有的方法删除后,自然而然就可以转化为父类对象。
而当 xxx调用子类独有的方法时,则需要重新开辟一个只属于子类的空间为((子类)xxx),所以需要强制转,此时((子类)xxx)相当于开辟了一个子类对象的空间,用它就能调用子类独有的方法。
因此,父类引用指向子类对象的变量xxx 不能直接调用子类独有方法
9.4 代码:
父类 Father.java
public class Father {
public String name = "fathername";
public static String name2 = "fathername2";
public void run(){
System.out.println("f run");
}
public static void run2(){
System.out.println("f static run2");
}
public void fathermethod(){
System.out.println("f method");
}
}
子类 Son.java
public class Son extends Father{
public String name = "sonname";
public static String name2 = "sonname2";
public void run(){
System.out.println("s run");
}
public static void run2(){
System.out.println("s static run2");
}
public void sonmethod(){
System.out.println("子类独有方法");
}
}
主程序类 Application.java
public class Application {
public static void main(String[] args) {
Father f = new Son();
f.run(); //调用的是子类重写的run方法(非static方法看右边)
f.run2(); //父类的run2(static方法看左边)
System.out.println(f.name2); //父类的静态属性name2(属性看左边)
System.out.println(f.name); //父类非静态属性name (属性看左边)
((Son) f).sonmethod(); //调用子类独有的方法(非static方法看右边)
//等价于Son xxx = ((Son)f); xxx.xonmethod();
f.fathermethod(); //调用子类中继承的,只是没重写,所有结果为f method(非static方法看右边)
System.out.println("------------------------------");
/*
Father father = new Father();
这块注解的代码运行都报错
((Son)father).run();
((Son)father).fathermethod();
((Son)father).sonmethod();
System.out.println(((Son) father).name);
所以父类引用指向父类对象的变量强转后也不能调用子类的东西。
*/
Son s = new Son();
((Father)s).run(); //还是子类重写的(非static方法看右边)
Father b = ((Father) s);
System.out.println(b.name); //父类的(属性看左边)
s.run(); //非static方法看右边, 右边是new Son 所以打印结果也是Son里重写的
s.run2(); //子类的run2(static方法看左边)
System.out.println(s.name2);//子类的name2(属性看左边)
System.out.println(s.name);//子类重写的 (属性看左边)
}
}
9.5 运行结果
9.6 总结
父类引用指向子类对象的变量调用 静态方法和所有属性时看左边(父类),只有调用 非静态方法时看右边(某子类)。
所以 “父 xxx = new 子() ”中的xxx,简单说就是一个去掉子类独有方法的子类对象。
10 关于子类能不能重写父类静态方法
不能
10.1 解释
(先看代码)
父类 Father.java:
public class Father {
public static String test = "父类static变量";
public static void test(){
System.out.println("这是父类 public static void方法");
}
}
复制代码
子类 Son.java 继承父类
public class Son extends Father {
public static String test = "子类static变量";
public static void test(){
System.out.println("子类static方法");
}
}
复制代码
二儿子 Son2.java 也继承父类
public class Son2 extends Father {
public static String test = "二儿子static变量";
public static void test() {
System.out.println("二儿子static方法");
}
}
复制代码
主程序类 Application.java:
public class Application {
public static void main(String[] args) {
Father father = new Father();
Son son = new Son();
Son2 son2 = new Son2();
System.out.println(father.test);
System.out.println(son.test);
System.out.println(son2.test);
System.out.println("------------------------------");
father.test();
son.test();
son2.test();
Father.test();
}
}
复制代码
输出结果:
10.2 分析
上面子类看似重写了父类的static方法,但其实是各自都开辟了一个空间,各自有各自的方法体(前面博客有说到,静态的东西都是属于类的,一个类只有一个空间专门放静态的东西),所以输出的结果不同。
但是子类不"重写"父类static方法时会继承,验证过了,在主程序类中可以用子类对象直接调用父类的静态变量和方法,打印的就是父类写的内容。
10.3 总结
所以子类可以继承父类的静态方法,但不能重写,之所以显示不同结果,是因为子类也开辟了一个空间去放自己的静态变量和方法,此时相当于写了一个子类独有的方法,只是它是用static修饰的。
关于这一点的证明:在子类中,在"重写"的static方法前面加注解 @Override,此时会直接编译报错:
翻译为“方法不重写其超类中的方法”,大意就是该方法没有从父类方法中重写。 所以说明这个方法不是在重写,而是子类自己的,独有的,只是跟父类那个static方法(test)同名了而已。
11 final关键字
-
final可以修饰变量
被final修饰的变量不可用改变,称为常量
-
final也可以修饰方法
父类中final修饰的方法不能被子类重写
-
final还可以修饰类
被final修饰的类不可以被继承
12 抽象abstract
① 不能直接new一个抽象类的对象,只能new一个继承了抽象类的子类对象。
格式: (继承了抽象类的)子类 xxx = new 子类();(继承了抽象类的子类)
或者像多态那样 父类(父类是抽象类) xxx = new 子类();(继承了抽象类的子类)
②抽象类中的抽象方法只能写 (访问权限) abstract (返回类型) (方法名)(); ——>没有方法体!
(方法体的具体实现,交给继承它的子类来写。)
③ 抽象类中可以写普通方法,但是普通类不能写抽象方法,写了抽象方法的必须是抽象类。
④ 抽象类也有无参构造方法。
抽象类初学用的少,一般用接口,到了架构师阶段对抽象类才有进一步了解
13 接口
能继承接口的只能是接口,接口和类只能是实现关系
接口也具有多态性
接口所有变量都默认是用public static final修饰的,所以都是全局静态常量。
接口的方法默认都是public abstract 修饰,只需要写 返回类型 方法名(); 即可定义一个方法
用抽象类去实现接口后,抽象类可以不用重写接口中的方法,交给继承抽象类的子类去重写(子类不重写会报错)
14 成员变量初始值
| 变量 | 默认值 |
|---|---|
| boolean | false |
| char | '\u0000' (null),将这个char强转int后初始值是0 |
| byte | (byte)0 |
| short | (short)0 |
| int | 0 |
| long | 0L |
| float | 0.0f |
| double | 0.0d |
| String | null |
类引用的变量(只有"类名 xxx",不给""= new 类名()"时的xxx)跟String一样,初始值为null
如果指向了具体的实例化对象( = new 类名() )后,它的初始值是一个地址。
15 equals和==
15.1 变量
基本类型(boolean,byte,char,short,int,long,float,double)只有==能比较两变量是否相等,没有equals方法。
15.2 对象
放在 x.equals(y) 前面的x没实例化——报错,前面x有实例化,y无论有没有实例化——false
15.2 字符串string
==比较地址,.equals比较值
-
用String strx = “xxx(值)”赋值
str1 == str2 比较:
两个都有初始值且初始值相同——true**(地址相同,说明2个字符串对象指向同一个地址,因为两者的值相同,所以就用一个地址来放这个值(java节约资源的机制),然后让两个string对象都指向这个地址)**;其中一个没有赋初值(静态字符串)——false;初始值不同——false。
str1.equals(str2) 比较:
两个都有初值且相同——true;不相同——false(比较字符串的值相不相等);str1没有给初值——报错。
-
用String strx = new String("xxx(值)")赋值
str3 == str4 比较:
两个无论有没有初始值,初始值相不相等都是false**(因为String对象地址不同,虽然两者的值所在地址是相同的,但是他们都有new 一个地址来存入 str,所以两个str指向的地址是不同的,先指向这个地址,这个地址再指向值所在地址,而比较的时候是比较这个两个str所指向的地址而不是值所在地址,所以地址不同,结果为false)**
str3.equals(str4)比较:
两个都有初始值且相同——true;不同——false(equals方法比较字符串的值);str3不给初值——报错。
对象的equals方法跟==作用一样,只有字符串String的equals方法是比较值,因为String类继承了Object之后重写了里面的equals方法,将其重写为判断两个字符串的内容(值)是否一致,所以两个字符串一般用equals来判断内容。
所以,字符串String的== 比较地址,equals方法比较值