JAVA基础-类的成员
1. 类的成员-属性
属性这里就是成员变量,也叫成员变量,直接定义在类中的。
- 在方法体外声明的变量称之为成员变量
- 实例变量(不以static修饰)
- 类变量(以static修饰)
- 在方法体内部声明的变量称之为局部变量
- 形参(方法、构造器中定义的变量)
- 方法局部变量(在方法体内定义)
- 代码块变量(在代码块内定义)
成员变量与局部变量比较:
就近原则:
当成员变量名与局部变量名一致时,若使用这个名字,使用的是离这个调用位置最近的变量(如果局部变量离的近就用局部变量,如果成员变量离得近,就用成员变量)。谁离我近,我就用谁。成员变量也可以用 this. 或类名(静态成员变量)来区分。
public class Animal { //私有变量,肯定不能被继承 private String name; //共有变量 public String age; //静态变量 public static int num; }
public class Dog extends Animal{}
public class SpringDemoMain { public static void main(String[] args) { Dog dog = new Dog(); //public 变量被继承 String age = dog.age; //静态成员变量被继承 int num = Dog.num; int num1 = dog.num; } }
- 非 private 成员变量可以被继承
- 静态成员变量可以被继承
- private 成员变量不能被继承,但是可以通过父类的 get/set 方法拿到
2. 类的成员-方法
1. 方法修饰符
private、缺省、protected、public
2. 方法重载
一个类中可以存在一个以上同名方法,只要它们的参数个数或者参数类型不同即可。
特点:
1.方法名相同
2.方法的参数类型,参数个不一样
3.方法的返回类型可以不相同
4.方法的修饰符可以不相同
3. 方法重写
在子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的重置、覆盖。
特点:
1.子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表
2.子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型
3.子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限
4.子类不能重写父类中声明为private权限的方法
5.子类方法抛出的异常不能大于父类被重写方法的异常
注意:静态方法可以继承,但不能被重写,体现在 override 不能使用
public class Animal { public Animal(){ System.out.println("我是一个 animal!"); } public Animal(String name){ System.out.println("我是一个 animal,我叫" + name); } public static void type(String type){ System.out.println("我是" + type + "动物"); } }
public class Dog extends Animal{ public Dog(){ System.out.println("我是一个狗子"); } public Dog(String name){ System.out.println("我是一个狗子,我叫" + name); } //这里报红线,说明静态方法没有重写一说 @Override public static void type(String type){ System.out.println("我是" + type + "动物"); } }
public class SpringDemoMain { public static void main(String[] args) { //两个子类都是调用父类的无参构造 new Dog(); new Dog("wpz"); //子类可以调用父类的静态方法,说明静态方法可以被继承 Dog.type("狗子"); } }
4. 形参和实参
形参:方法声明时的参数
实参:方法调用时实际传给形参的参数值
5. 值传递和引用传递
值传递:传递对象的一个副本,即使副本被改变,也不会影响源对象,因为值传递的时候,实际上是将实参的值复制一份给形参。
引用传递:传递的并不是实际的对象,而是对象的引用,外部对引用对象的改变也会反映到源对象上,因为引用传递的时候,实际上是将实参的地址值复制一份给形参。
1. 基本类型
由于原始内容和副本都是存储实际值,并且是在不同栈区,因此形参的操作,不影响原始内容。
2. 引用类型
引用类型分为两种情况:
- 一种是形参和实参保持指向同一个对象地址,则形参的操作,会影响实参指向的对象的内容。
private static void changePerson(Person person){ person.setAge(19); person.setName("李四"); }
- 一种是形参被改动指向新的对象地址(如重新赋值引用),则形参的操作,不会影响实参指向的对象的内容。
private static void changePerson1(Person person){ person = new Person(); person.setName("申公豹"); }
3. String
String 类型比较特殊,虽然是引用类型,但是 String 类型是不可变的,所以不会影响原有的值。
3. 类的成员-构造器
定义:与类同名、不具有返回值,不能有return语句
作用:初识化对象、为成员属性赋值
特点:
1.Java 语言中,每个类都至少有一个 构造器
2.默认构造器的修饰符与所属类的修饰符一致
3.一旦 显式定义了 构造器,则系统不再提供默认构造器
4.一个类可以创建多个重载的构造器
4. 类的成员-代码块
用大括号包括其来的一段代码,可以对类或者对象进行初始化操作。分为静态代码块和动态代码块
class CodeBlockDemo { { System.out.println("普通代码块"); } { System.out.println("普通代码块1"); } static{ System.out.println("静态代码块"); } static{ System.out.println("静态代码块1"); } }
1. 静态代码块
- 可以有输出语句。
- 可以对类的属性、类的声明进行初始化操作。
- 不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法。
- 若有多个静态的代码块,那么按照从上到下的顺序依次执行。
- 静态代码块的执行要先于非静态代码块。
- 静态代码块随着 类的加载 而加载,且只执行一次。
2. 非静态代码块
- 可以有输出语句。
- 可以对类的属性、类的声明进行初始化操作。
- 除了调用非静态的结构外,还可以调用静态的变量或方法。
- 若有多个非静态的代码块,那么按照从上到下的顺序依次执行。
- 每次创建对象的时候,都会执行一次。且先于构造器执行。
3. 成员执行顺序
属性赋值、构造方法赋值、代码块赋值、方法赋值之间执行顺序如何?
//同时在属性上,构造方法,代码块中对属性进行赋值。 public class CodeBlockDemo { private String name = "张三"; { System.out.println("普通代码块修改前:" + name); name = "李四"; System.out.println("普通代码块修改后:" + name); } public CodeBlockDemo(){ System.out.println("构造方法修改前:" + name); name = "王五"; System.out.println("构造方法修改后:" + name); } public String getName() { return name; } public static void main(String[] args) { CodeBlockDemo codeBlockDemo = new CodeBlockDemo(); System.out.println(codeBlockDemo.getName()); } }
当执行后发现:
由此发现执行顺序:属性赋值 > 代码块 > 构造方法,普通方法需要调用,肯定排最后,后面赋值会覆盖前面的。
5. 类的成员-内部类
1. 内部类定义
- 我们把在一个类里面定义的类称为内部类(InnerClass)或嵌套类,把外面定义的类称为外部类(OutClass)或宿主类。它与普通外部类最大的不同,在于其实例对象不能单独存在,必须依附于一个外部类的实例对象
- 内部类可以很好地实现隐藏,一般的非内部类是不允许有private 与 protected权限的,但内部类却可以,而且内部类还拥有外部类中所有元素的访问权限。
- 内部类分为四种,成员内部类,静态内部类,局部内部类,匿名内部类
2. 成员内部类
成员内部类就是指没有被static修饰的内部类,也可以称为非静态内部类
public class OuterClassDemo { /** * 成员内部类 */ class memberInnerClass{ } }
语法:
如果是在外部类中,创建成员内部类对象的基本语法格式如下:
内部类 对象名 = new 内部类();
如果是在外部的其他类中,或者是在外部类的静态方法中,创建成员内部类对象的基本语法格式如下:
内部类 对象名 = new 外部类().new 内部类();
3. 静态内部类
静态内部类和成员内部类的定义类似,但要使用static修饰,所以称为静态内部类(Static Nested Class)。
静态内部类和成员内部类有很大的不同,它不再依附于Outer的实例,而是一个完全独立的类,因此无法引用Outer.this的方式调用。但它可以访问Outer类的private静态字段和静态方法,如果我们把静态内部类移到Outer类之外,就失去了访问private的权限。
public class OuterClassDemo { /** * 静态内部类 * */ static class staticInnerClass{ } }
语法:
创建静态内部类对象的基本语法格式如下:
内部类 对象名 = new 外部类.内部类();
4. 局部内部类
指在方法中定义的类,参考局部变量,无法静态,只能在方法内部使用。
public class OuterClassDemo { /** * 局部内部类 * */ public void method(){ class methodInnerClass{ } } }
5. 匿名内部类
匿名内部类就是指没有类名的内部类,必须在创建时使用 new 语句来声明。匿名内部类不能在Outer Class外部类中定义,而是要在某个方法的内部,通过匿名类(Anonymous Class)的形式来定义。匿名内部类本身就是一个对象。
通常情况下,如果一个方法的参数是接口类型,且该接口只需要实现一次,那么我们就可以通过匿名内部类的形式来进行定义。另外如果该接口的实现每次都不同,也可以使用匿名内部类的形式进行定义。我们也可以把这种定义形式叫做“接口回调”。匿名内部类的代码格式使得代码更加简洁、紧凑,模块化程度也更高。
public class OuterClassDemo { /** * 匿名内部类 * */ public void method1(){ new Supplier<String>() { @Override public String get() { return null; } }.get(); } }
● 匿名内部类本身就是一个对象;
● 一般在匿名内部类中不会定义属性和方法,因为没有意义;
● 匿名内部类的父类一般都是抽象类或者是接口;
实现匿名内部类:
● 继承一个类,重写其方法;
● 实现一个或多个接口,并实现其方法。
new <类或接口>(){ 重写类或接口的方法 }
6. 关于继承
1. 成员是否可以继承
1. 属性
Sample:
public class ExtendsDemo { public static void main(String[] args) { Son son = new Son(); //查看属性值 son.getParentField(); //通过父类引用修改属性值 son.changeParentField(); //查看属性值 son.getParentField(); } } class Father{ private String name; public int age; static int step; public StringBuffer str; public Father(){ this.age = 10; step = 100; str = new StringBuffer("测试"); } } class Son extends Father{ /** * 调用子类构造方法,默认第一行是父类的无参构造,所以这里打印从父类继承的三个子类,是父类的构造方法赋的值,不奇怪。 * 这个构造方法中还通过子类修改了从父类继承的几个属性。 */ public Son(){ System.out.println(this.age); //10 this.age = 11; System.out.println(Son.step); //100 Son.step = 102; System.out.println(this.str); //测试 this.str.append("子类的"); } /** * 不管是使用 super 还是 this 调用属性,都是在子类构造方法中修改过的值,这说明父类的值也被修改了 * */ public void getParentField(){ System.out.println("父:" + super.age); //11 System.out.println("子:" + this.age); //11 System.out.println("父:" + Father.step); //102 System.out.println("子:" + Son.step); //102 System.out.println("父:" + super.str); //测试子类的 System.out.println("子:" + this.str); //测试子类的 } /** * 通过父类引用改变属性值 * */ public void changeParentField(){ super.age = 10; Father.step = 100; super.str.append( "父类的" ); } }
总结:
- private 修饰的属性不能继承,其他权限符都可以。
- static 属性和非 static 属性都可以被继承。
- 在可以继承的属性中,父子类是共用的这个属性,通过子类或父类引用修改属性值,另一方也会修改。
- 如果子类有跟父类一样的属性,那就各论各的,谁也不影响谁。
2. 构造器
Sample:
public class Class_1 { public static void main(String[] args) { new S_1(); System.out.println("-------------"); new S_1(""); System.out.println("-------------"); new S_1("", null); } } class F_1{ private String name; public F_1(){ System.out.println("父类无参构造"); } public F_1(String name){ this.name = name; System.out.println("父类有参构造"); } } class S_1 extends F_1{ //这里默认第一行调用父类无参构造 public S_1(){ System.out.println("子类有参构造"); } //这里也是默认第一行调用父类无参构造 public S_1(String name){ System.out.println("子类有参构造"); } //这里显式调用父类有参构造 public S_1(String name, String name1){ super(name); System.out.println("子类有俩参构造"); } }
总结:
- 构造方法无法被继承
- 子类的构造方法第一行会默认调用父类的无参构造,如果使用
super()
显式调用父类有参构造,则替换为调用的父类构造。同时这个只能第一行调用。用this()
也是同样的效果。 - 父类如果没有无参构造,子类构造方法不处理的话,因为找不到父类的无参构造,会报错。
3. 代码块
Sample:
public class Class_2 { public static void main(String[] args) throws ClassNotFoundException { new S_2(); } } class F_2{ static String test; static { System.out.println("父类静态代码块"); } { System.out.println("父类代码块"); } } class S_2 extends F_2{ }
继承没继承也不知道,反正调用了。。
4. 方法
public class Class_3 { public static void main(String[] args) { S_3.method(); new S_3().method1(); } } class F_3{ public void method1(){ System.out.println("父类方法 method1"); } public static void method(){ System.out.println("父类静态方法"); } } class S_3 extends F_3{ public static void method(){ System.out.println("子类静态方法"); } }
总结:
- 对于 private 方法,肯定是不能继承的,其他权限修饰符是可以的,然后就有了重写一说
- 对于静态方法,比较特殊,可以认定为可以继承,因为可以直接通过子类名称去调用。但是重写的时候,可以方法签名跟父类一摸一样,但是不能用 @override,传说中的可以
继承但不能重写
,感觉跟非静态方法,就差个 @override
5. 内部类
懒得研究,参考属性吧。。
2. 父子类属性重名问题
首先声明,开发过程中不建议这么干。如果这么干了,那么如何区分呢?
public class DogDemo { public static void main(String[] args) { BigDog bigDog = new BigDog(); bigDog.getName(); //李四 bigDog.findName(); //张三 } } class Dog{ String name; public Dog(){ this.name = "张三"; } public void getName() { System.out.println(name); } public void findName(){ System.out.println(name); } } class BigDog extends Dog{ String name; public BigDog(){ this.name = "李四"; } @Override public void getName() { System.out.println(name);; } }
由于 getName()
在子类中重写,所以这里获取的指定是子类的 name 属性。这个地方应该类似于方法重写,属性也被子类覆盖了。而 findName() 由于没有重写,调用的是父类的方法,所以打印的也是父类的属性。
3. 对继承的理解
继承应该是这种结构,将父类整个放到子类中,当子类引用成员时,会先从子类内容中获取,获取不到再从父类中获取。
For Example:
public class Class_1 { public static void main(String[] args) { new S_1().getName(); } } class F_1{ String name = "父类"; public String getName() { return name; } } class S_1 extends F_1{ String name = "子类"; }
此时打印应该是 “子类”,因为子类没有 getName() 这个方法,所以去调用父类的 getName() 方法,而父类的 getName() 拿到的 name 属性自然是父类的。
本文作者:Hi.PrimaryC
本文链接:https://www.cnblogs.com/cnff/p/17669533.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步