面向对象
方法
1 | 修饰符 方法返回类型 方法名(方法参数列表) { |
可变参数类型
1 | class Group { |
也可以这样写
1 | class Group { |
但在调用时就比较麻烦,需要构造一个string[]类型
1 | Group g = new Group(); |
参数绑定
1 | Person n1ng = new Person(); |
可见对于基本类型的参数,传入后再进行修改变量不会改变字段的值
1 | Person n1ng = new Person(); |
所以对于引用类型的参数,如果修改了变量的值,相应的字段也会被改变
看这个例子
1 | Person p = new Person(); |
答案是n2ng,这和上面的结论矛盾吗,其实并不矛盾,这涉及到了之前的知识
之前的例子中name是一个String[]类型,是可变的对象,而这里是String类型,是不可变的对象,
当修改name时,其实就是修改了数组中字符串的地址,但是数组的地址没变,所以p的name字段里所存储的地址也发生了改变,但是修改n1ng时,字符串的地址发生了改变,但是字段并没有改变,仍然指向原来的字符串
构造方法
在创建对象实例时就将内部字段进行初始化
1 | Person p = new Person("n1ng", 21); |
构造方法,当然构造方法可以重载(Overload),即可以定义多个
1 | public Person(String name, int age) {//直接以类名为方法名 |
继承
1 | class Person { |
1 | class Student { |
可以看到,Student类和Person类相比,只是多了一个score字段,为了避免代码的重复编写,就可以利用继承来实现代码的复用,Student继承了Person后,就拥有了Person的所有功能,如果需要对Student进行功能的增加,直接在Student中编写即可
在Java中使用extends关键字来实现继承
1 | class Person { |
需要注意的是,对于父类中已有的字段子类中不能重新定义和他重名的字段
并且子类无法访问父类的private字段或方法,如果想要访问,可以将父类中的private关键字改为protected
protected关键字所修饰的字段或方法可以被子类访问但不能被外部类访问
super
super关键字表示父类,子类引用父类的字段时,就可以用super.Name
1 | class Student extends Person { |
对于这段代码
1 | public class Main { |
在运行的时候会报错,因为所有类的构造方法第一行的语句都是调用父类的构造方法,如果没有写,系统则会自动加一句super(),但是在这段代码中Person的构造方法是有参数的,所以需要自己写一句super(name,age)
即如果父类没有默认的构造方法,子类必须显式的调用super(),并给出参数
阻止继承
1 | public sealed class Shape permits Rect, Circle, Triangle { |
sealed修饰的类可以通过permits给出可以继承的类
多态
子类如果定义了一个和父类相同(方法签名相同,返回值也相同)的方法,被称为覆写,即override,如果要调用父类的方法,可以通过super来调用,如果父类的某个方法不允许子类覆写,则需要加上final修饰,同样的,某一个类如果不希望被继承也可以加上final关键字
抽象类
如果父类的方法不需要实现任何的功能,仅仅只是为了定义方法签名,供子类覆写,那么就可以把父类的方法声明位抽象方法,用abstract修饰,同样的,父类也可以用abstract修饰为抽象类,抽象类无法被实例化,只用于继承,并且,抽象类可以强迫子类实现其定义的抽象方法,相当于定义了规范,比如Person类定义了抽象方法run(),那么他的子类Student就必须覆写run()方法
接口
在抽象类中抽象方法实质上就是在定义规范,保证所有的子类都有相同的接口实现,如果一个抽象类没有字段,所有方法全都是抽象方法,就可以把这类抽象类改写为接口(Interface),使用interface声明。一个具体的class实现一个接口时,需要使用implements关键字
1 | Class Student implements Person{} |
在java中一个子类只能继承一个父类,但是一个类可以实现多个接口
1 | Class Student implements Person,Hello{} |
同时,一个接口也可以继承另一个接口
静态字段和方法
对于实例字段,实例字段在每一个实例中都有自己的独立的空间,与之相对的静态字段只有一个共享的空间,所有的实例共享这一个字段,调用时通过类名直接调用即可,静态字段有static修饰。相同的,静态方法也直接通过类名调用。上面提到,接口类不能定义实例字段,但是它可以有静态字段,且必须由public static final修饰
包
为了解决类名的冲突,可以将类放在包下,一个类总是属于某个包,例如,定义一个Person类放在n1ng包下,完整的类名即n1ng.Person,在定义类时需要在第一行声明,同一个包内的类可以访问包作用域的字段和方法(违背public,protected,private修饰的字段,方法)
在一个class中引用其他的class时,可以直接写出完整的类名(如n1ng.Person),也可以通过import导入完整的类名
内部类
内部类不能直接实例化,必须依附于外部类的一个实例,在编译时,外部类Outer被编译为Outer.class,而内部类Inner则会被编译为Outer$Inner.class
1 | Outer outer = new Outer();//创建outer实例 |
匿名类
另一种定义内部类的方法,在方法内部通过匿名类(Anonymous Class)来定义
1 | public class Outer { |
匿名类和内部类一样可以访问外部类的private方法和字段,但是匿名类比内部类要少写许多代码,匿名类在编译时会被编译为Outer$1.class,如果有多个则以此类推,匿名类也可以继承自普通类
静态内部类
和内部类相似,但用static修饰,他不在需要依附于Outer实例,但是也无法再引用Outer.this,仍然可以访问Outer的private静态字段和静态方法
1 | Outer.StaticNested sn = new Outer.StaticNested(); |
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 3049155267@qq.com