方法
(1)public和private
private修饰的属性只能在类内部调用,防止外部泄漏,可以采用类似的set方法赋值,因为set和get方法是public类型的,外部是可以访问的,同理private修饰的方法,也只限于类内部调用.
(2)this:只代表本类的成员变量,方法,构造方法.不代表一个方法中的局部变量,比如下面this.name 代表的是类Person中定义的成员变量name,不代表setName方法中传入的参数name
public class Main {
public static void main(String[] args) {
Person ming = new Person();
ming.name = "Xiao Ming"; // 对字段name赋值
ming.age = 12; // 对字段age赋值
}
}
class Person {
private String name;
private int age;
//set方法
public void setName(String name){
this.name = name;
}
//get方法
public String getName(){
return this.name;
}
}
(3)基本类型参数的传递,注意事项基本类型,是调用方值的复制。双方各自的后续修改,互不影响,下图代码中调用p.setAge(n),是将基础类型n的值复制出来一份,分配新的地址出来.所以后续修改n的值不会相互影响.
public class Main {
public static void main(String[] args) {
Person p = new Person();
int n = 15; // n的值为15
p.setAge(n); // 传入n的值
System.out.println(p.getAge()); // 15
n = 20; // n的值改为20
System.out.println(p.getAge()); // 结果还是15
}
}
class Person {
private int age;
public int getAge() {
return this.age;
}
public void setAge(int age) {
this.age = age;
}
}
(4)引用类型参数的传递,调用方的变量,和接收方的参数变量,指向的是同一个对象。双方任意一方对这个对象的修改,都会影响对方(因为指向同一个对象嘛)
public class Main {
public static void main(String[] args) {
Person p = new Person();
String[] fullname = new String[] { "A", "B" };
p.setName(fullname); // 传入fullname数组
System.out.println(p.getName()); // 输出A B
fullname[0] ="D";
System.out.println(p.getName()); // 输出D B
}
class Person {
private String[] name;
public String getName() {
return this.name[0] + " " + this.name[1];
}
public void setName(String[] name) {
this.name = name;
}
}
(5)String也为引用类型,修改String类型的值,却没有相互影响
public class test {
public static void main(String[] args) {
Person p = new Person();
String str ="A";
p.setName(str);
System.out.println(p.getName()); // 输出结果A
str = "B";
System.out.println(p.getName()); // 输出的结果依然是A,
/*
因为 str = "B"; 这行代码只是改变了str变量的指向,
由原来指向“A”改为指向“B”,但是“A”这个对象并没有改变,只有实例化的对象才会分配内存地址.所以Person p的name字段还是指向“A”的
*/
}
}
class Person {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
构造方法
构造方法的参数没有限制,在方法内部,也可以编写任意语句。但是,和普通方法相比,构造方法没有返回值,我们都是用构造方法来New一个对象.
如果我们自定义了一个构造方法,那么,编译器就不再自动创建默认构造方法,默认的构造方法就没了.
既类里面的一个字段进行初始化,又在构造方法中对相同字段进行初始化,构造方法是后进行的.
class Person {
private String name = "Unamed";//先进行初始化字段
private int age = 10;
//自定义构造方法Perison(String name, int age)
public Person(String name, int age) {
this.name = name;//再运行构造方法的赋值,最后name的值取得是构造方法的
this.age = age;
}
}
方法重载
是指多个方法的方法名相同,但各自的参数不同;
重载方法应该完成类似的功能,参考String的indexOf();
重载方法返回值类型应该相同
构造方法其实就是方法名必须是类名,传入参数不同.
方法的重载就是方法名相同,参数不同,不能和类名一样.
class Hello {
public void hello() {
System.out.println("Hello, world!");
}
public void hello(String name) {
System.out.println("Hello, " + name + "!");
}
public void hello(String name, int age) {
if (age < 18) {
System.out.println("Hi, " + name + "!");
} else {
System.out.println("Hello, " + name + "!");
}
}
}
继承
/*如果类没有extends的话,默认继承object类,所以下面的继承顺序是object -> Perison -> Student
* private定义父类,子类和外部都不能访问
* proteced外部不能访问,子类以及子类的子类可以访问
* super表示父类,在子类中利用super可以获取父类的属性和方法.
*/
public class JiCheng {
public static void main(String[] args) {
Student student = new Student();
student.getSuperName("hello");
}
}
//父类Person
class Person{
private int sex;
protected String name;
protected Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
//子类Student
class Student extends Person{
private int score;
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
public void getSuperName(String name) {
//此处如果写成this.name或者super.name,就代表是父类中的name了,就不是传入的参数name了
super.setName(name);
System.out.println(super.getName());
}
}
super深入了解,看下面例子,运行会报错
public class Main {
public static void main(String[] args) {
Student s = new Student("Xiao Ming", 12, 89);
}
}
class Person {
protected String name;
protected int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
class Student extends Person {
protected int score;
public Student(String name, int age, int score) {
this.score = score;
}
}
这是因为在Java中,任何class的构造方法,第一行语句必须是调用父类的构造方法,如果我们没有明确地写调用父类的构造方法,编译器会帮我们自动加一句super();,所以,上例子Student类的构造方法实际上是这样:
class Student extends Person {
protected int score;
public Student(String name, int age, int score) {
super(); //我们自己没有写父类的构造,系统就自动加上了
this.score = score;
}
}
但是上例子Person父类自定义了构造方法,所以我们在子类的构造方法中,要先初始化父类的构造方法,
class Student extends Person {
protected int score;
public Student(String name, int age, int score) {
super(name, age); // 调用父类的构造方法Person(String, int)
this.score = score;
}
}
综上所述:Java只允许一个class继承自一个类,因此,一个类有且仅有一个父类。只有Object特殊,它没有父类,object是最高级别的父类,在我们没有写extends的时候,系统自动extends object类.super代表父类,每次new一个子类的构造方法时,都是先调用父类的构造方法.
把一个子类类型安全地变为父类类型的赋值,被称为向上转型,如下代码
Student s = new Student("Xiao Ming", 12, 89);
也可以写成
Person s = new Student("Xiao Ming", 12, 89);
多态
在继承关系中,子类如果定义了一个与父类方法完全相同的方法,被称为覆写(Override),完全相同的方法指的是传入参数和返回类型都相同. 如下代码演示
class Person {
public void run() {System.out.println("Person.run");}
}
class Student extends Person {
// 不是Override,因为参数不同,这是子类中的一个新方法,成为Overload
public void run(String s) { … }
//参入参数和返回值都相同,继承的子类中@Override可以省略,加上@Override可以帮助我们效验此方法是否在父类中存在.不存在会报错
@Override
public void run() { System.out.println("Student.run"); }
}
多态是指,针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法,如下代码演示
Person p = new Student();
p.run(); // 输出结果:Student.run 调用的是Student里面的run方法
看下面代码它传入的参数类型是Person,我们是无法知道传入的参数实际类型究竟是Person,还是Student,还是Person的其他子类,因此,也无法确定调用的是不是Person类定义的run()方法。
所以,多态的特性就是,运行期才能动态决定调用的子类方法。对某个类型调用某个方法,执行的实际方法可能是某个子类的覆写方法。
public void runTwice(Person p) {
p.run();
}
因为所有的class最终都继承自Object,其中toString是Object的方法,我们也可以覆用
@Override
public String toString() {
return "Person:name=" + name;
}
利用super可以在子类中调用父类的方法
如果一个父类不允许子类对它的某个方法进行覆写,可以把该方法标记为final。用final修饰的方法不能被Override
抽象类
如果一个class定义了方法,但没有具体执行代码,这个方法就是抽象方法,抽象方法用abstract修饰。因为无法执行抽象方法,因此这个类也必须申明为抽象类
使用abstract修饰的类就是抽象类。我们无法实例化一个抽象类
因为抽象类本身被设计成只能用于被继承,因此,抽象类可以强迫子类实现其定义的抽象方法,否则编译会报错。因此,抽象方法实际上相当于定义了“规范”。
面向抽象编程的本质就是:
1-上层代码只定义规范(例如:abstract class Person);
2-不需要子类就可以实现业务逻辑(正常编译);
3-具体的业务逻辑由不同的子类实现,调用者并不关心。
例如,Person类定义了抽象方法run(),那么,在实现子类Student的时候,就必须覆写run()方法:
public class Main {
public static void main(String[] args) {
Person p = new Student();
p.run();
}
}
abstract class Person {
public abstract void run();
}
class Student extends Person {
@Override
public void run() {
System.out.println("Student.run");
}
}
接口
所谓interface,就是比抽象类还要抽象的纯抽象接口,因为它连字段都不能有.
一个类可以实现多个接口,接口也是数据类型,适用于向上转型和向下转型.
接口的所有方法都是抽象方法,接口不能定义实例字段.
接口可以定义default方法,实现类可以不必覆写default方法。default方法的目的是,当我们需要给接口新增一个方法时,会涉及到修改全部子类。如果新增的是default方法,那么子类就不必全部修改,只需要在需要覆写的地方去覆写新增方法.
public class Main {
public static void main(String[] args) {
Person p = new Student();
p.run();
}
}
interface Person {
//非default方法
void getName();
//default方法,实现类Student可以不实现此方法
default void run() {
System.out.println("run");
}
}
class Student implements Person {
//实现类必须实现此方法
public void getName() {
System.out.println("name");
}
}
静态字段和静态方法static
静态字段:无论修改哪个实例的静态字段,效果都是一样的:所有实例的静态字段都被修改了,原因是静态字段并不属于实例.
访问静态字段推荐 (类名.静态字段名)
静态方法:静态方法类似其它编程语言的函数,采用(类名.静态方法名)调用
因为静态方法属于class而不属于实例,因此,静态方法内部,无法访问this变量,也无法访问实例字段,它只能访问静态字段.
接口的静态字段:因为interface是一个纯抽象类,所以它不能定义实例字段。但是,interface是可以有静态字段的,并且静态字段必须为final类型
public interface Person {
//因为interface的字段只能是public static final类型,所以我们可以把这些修饰符都去掉
public static final int MALE = 1;
public static final int FEMALE = 2;
}
包
Java内建的package机制是为了避免class命名冲突;
JDK的核心类使用java.lang包,编译器会自动导入;
JDK的其它常用类定义在java.util.,java.math.,java.text.*,……;
包名推荐使用倒置的域名,例如org.apache。