栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

JAVA笔记-面向对象

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JAVA笔记-面向对象

目录

(1)创建类(class)与实例(instance)

(2)方法

(3)构造方法

 (4)方法重载

 (5)继承

 (6)多态

覆写Object方法

final

 (7)抽象类

抽象类

面向抽象编程

(8)接口 

接口继承

default方法

 (9)静态字段和静态方法

静态方法

(10)包

(11)作用域

最佳实践

小结

(12)内部类

(13)class与jar 

(14)模块

笔记内容整理来自:


(1)创建类(class)与实例(instance)

在OOP中,class和instance是“模版”和“实例”的关系;

定义class就是定义了一种数据类型,对应的instance是这种数据类型的实例;

class定义的field,在每个instance都会拥有各自的field,且互不干扰;

通过new操作符创建新的instance,然后用变量指向它,即可通过变量来引用这个instance;

访问实例字段的方法是变量名.字段名;

指向instance的变量都是引用变量。

package test10;

//创建一个类:
class Person
{
	String name;
	String Address;
	int age;
	Person(String name,String Address,int age){
		this.name = name;
		this.Address = Address;
		this.age = age;
	}
	public String toString()
	{
		return "["+"姓名:"+name+" "+"年龄:"+age+" "+"地址:"+Address+"]";
	}
}

public class Main {

	public static void main(String[] args) {
		
		//创建一个类的实例:
		Person per = new Person("Jack","Eglish",18);
		System.out.print(per);
	}

}

一个class可以包含多个字段(field),字段用来描述一个类的特征。上面的Person类,我们定义了两个字段,一个是String类型的字段,命名为name,一个是int类型的字段,命名为age。因此,通过class,把一组数据汇集到一个对象上,实现了数据封装。

public是用来修饰字段的,它表示这个字段可以被外部访问。

(2)方法
  • 方法可以让外部代码安全地访问实例字段;

  • 方法是一组执行语句,并且可以执行任意逻辑;

  • 方法内部遇到return时返回,void表示不返回任何值(注意和返回null不同);

  • 外部代码通过public方法操作实例,内部代码可以调用private方法;

  • 理解方法的参数绑定。

package test10;

//创建一个类:
class Person
{
	public String name;
	public String Address;
	private int age;
	Person(String name,String Address,int age){
		this.name = name;
		this.Address = Address;
		this.age = age;
	}
	
	public String toString()
	{
		MoveHomeToBeijing();//Private类型,只能在类体中执行
		return "["+"姓名:"+name+" "+"年龄:"+age+" "+"地址:"+Address+"]";
	}
	
	private void MoveHomeToBeijing()
	{
		this.Address = "Beijing";
	}
}

public class Main {

	public static void main(String[] args) {
		
		//创建一个类的实例:
		Person per = new Person("Jack","Eglish",18);
		//per.MoveHomeToBeijing();//因为MoveHomeToBejing是private类型只能在类体中调用,所以此处代码无法执行。
		System.out.println(per.name);//Jack,因为name被public修饰,所以类外可访问
		//System.out.println(per.age);//编译报错,因为age被private修饰,所以类外无法访问
		System.out.print(per);//[姓名:Jack 年龄:18 地址:Beijing]
	}

}

(3)构造方法

实例在创建时通过new操作符会调用其对应的构造方法,构造方法用于初始化实例;

没有定义构造方法时,编译器会自动创建一个默认的无参数构造方法;

可以定义多个构造方法,编译器根据参数自动判断;

可以在一个构造方法内部调用另一个构造方法,便于代码复用。

没有在构造方法中初始化字段时,引用类型的字段默认是null,数值类型的字段用默认值,int类型默认值是0,布尔类型默认值是false:

在Java中,创建对象实例的时候,按照如下顺序进行初始化:

  1. 先初始化字段,例如,int age = 10;表示字段初始化为10,double salary;表示字段默认初始化为0,String name;表示引用类型字段默认初始化为null;

  2. 执行构造方法的代码进行初始化。

因此,构造方法的代码由于后运行,所以,new Person("Xiao Ming", 12)的字段值最终由构造方法的代码确定。

 一个构造方法可以调用其他构造方法,这样做的目的是便于代码复用。调用其他构造方法的语法是this(…)。

package test10;

public class Main {
	public static void main(String[] args) {
		Person per = new Person("Kangkang");
		System.out.print(per);//[姓名:Kangkang 年龄:17 地址:Beijing]
	}
}
//创建一个类:
class Person
{
	private String name;
	private String Address;
	private int age;
	Person(String Address,int age){//有参构造方法1
		this.Address = Address;
		this.age = age;
	}
	Person(String name)//有参构造方法2
	{
		this("Beijing",17);
		this.name = name;
	}
	Person()//无参构造方法
	{
		this.age = 18;//永远18岁
	}
	
	
	private void MoveHomeToBeijing()
	{
		this.Address = "Beijing";
	}
	public String toString()
	{
		MoveHomeToBeijing();//Private类型,只能在类体中执行
		return "["+"姓名:"+name+" "+"年龄:"+age+" "+"地址:"+Address+"]";
	}
}

 (4)方法重载

方法重载是指多个方法的方法名相同,但各自的参数不同;

重载方法应该完成类似的功能,参考String的indexOf();

重载方法返回值类型应该相同。

方法名相同,但各自的参数不同,称为方法重载(Overload)。

注意:方法重载的返回值类型通常都是相同的。

方法重载的目的是,功能类似的方法使用同一名字,更容易记住,因此,调用起来更简单。

举个例子,String类提供了多个重载方法indexOf(),可以查找子串:

  • int indexOf(int ch):根据字符的Unicode码查找;

  • int indexOf(String str):根据字符串查找;

  • int indexOf(int ch, int fromIndex):根据字符查找,但指定起始位置;

  • int indexOf(String str, int fromIndex)根据字符串查找,但指定起始位置。

package test10;

public class Main {
	public static void main(String[] args) {
		String str = "Hello, my friend, thanking you for reading my blog!";
		int n1 = str.indexOf('H');
		System.out.println(n1);//0
		int n2 = str.indexOf("Hello");
		System.out.println(n2);//0
		int n3 = str.indexOf('f',3);
		System.out.println(n3);//10
		int n4 = str.indexOf("you",4);
		System.out.println(n4);//27
	}
}

 (5)继承
  • 继承是面向对象编程的一种强大的代码复用方式;

  • Java只允许单继承,所有类最终的根类是Object;

  • protected允许子类访问父类的字段和方法;

  • 子类的构造方法可以通过super()调用父类的构造方法;

  • 可以安全地向上转型为更抽象的类型;

  • 可以强制向下转型,最好借助instanceof判断;

  • 子类和父类的关系是is,has关系不能用继承。

 继承树:

┌───────────┐
│  Object   │
└───────────┘
      ▲
      │
┌───────────┐
│  Person   │
└───────────┘
      ▲
      │
┌───────────┐
│  Student  │
└───────────┘

package test10;

public class Main
{
	public static void main(String[] args)
	{
		Student stu = new Student("007");
		System.out.println(stu);//Jack 007 18
		
		//向上转型:理解继承树,再来看这个就不难。在继承树中越往上的辈分越大,向上转型就是用父类对象创建子类实例。
		Person per = new Student("008");
		System.out.println(per);//Jack 008 18
		//因为Student继承自Person,因此,它拥有Person的全部功能。Person类型的变量,如果指向Student类型的实例,对它进行操作,是没有问题的!
		//这种把一个子类类型安全地变为父类类型的赋值,被称为向上转型(upcasting)。
		
		//向下转型:用子类对象强制创建父类实例。这想想都很难,有一种大逆不道的感觉……
		Student stu1 = (Student)new Person("Kangkang",18);
		System.out.println(stu1);//【空】
		//不能把父类变为子类,因为子类功能比父类多,多的功能无法凭空变出来。
		//因此,向下转型很可能会失败。失败的时候,Java虚拟机会报ClassCastException。
		//利用instanceof,在向下转型前可以先判断:
		Person p = new Student("001");
		if (p instanceof Student) {
		    // 只有判断成功才会向下转型:
		    Student s = (Student) p; // 一定会成功
		}else
		{
			System.out.print("强转失败");
		}
		
	}
}

class Person//正常情况下,只要某个class没有final修饰符,那么任何类都可以从该class继承。
{
	protected String name;//承有个特点,就是子类无法访问父类的private字段或者private方法。
	protected int age;//这使得继承的作用被削弱了。为了让子类可以访问父类的字段,我们需要把private改为protected。用protected修饰的字段可以被子类访问
	
	Person(String name,int age)
	{
		this.name = name;
		this.age = age;
	}
}

class Student extends Person//正常情况下,只要某个class没有final修饰符,那么任何类都可以从该class继承。
{
	String name;
	String Id;
	
	Student(String Id)
	{
		super("Jack",18);//调用父类构造函数。
		this.name = super.name;//super关键字表示父类(超类)。子类引用父类的字段时,可以用super.fieldName
		this.Id = Id;
	}
	public String toString()
	{
		return name+" "+Id+" "+age;
	}
}

 (6)多态
  • 子类可以覆写父类的方法(Override),覆写在子类中改变了父类方法的行为;

  • Java的方法调用总是作用于运行期对象的实际类型,这种行为称为多态;

  • final修饰符有多种作用:

    • final修饰的方法可以阻止被覆写;

    • final修饰的class可以阻止被继承;

    • final修饰的field必须在创建对象时初始化,随后不可修改。

Override(方法重写)和Overload(方法重载)区别:

Override和Overload不同的是,如果方法签名不同,就是Overload,Overload方法是一个新方法;如果方法签名相同,并且返回值也相同,就是Override。

package test10;

public class Main
{
	public static void main(String[] args)
	{
		Car c = new Car();
		c.run();//车在跑。如果在Car类中没有run方法,则此处调用输出人在跑。
		System.out.println(c.run("法拉利"));//法拉利在跑
	}
}

class Person
{
	void run()
	{
		System.out.println("人在跑");
	}
}

class Car extends Person
{
	void run()//run方法重写
	{
		System.out.println("车在跑");
	}
	
	String run(String Carname)//run方法的重载
	{
		return Carname + "在跑";
	}

}

多态是指,针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法。

多态的特性就是,运行期才能动态决定调用的子类方法。对某个类型调用某个方法,执行的实际方法可能是某个子类的覆写方法。

 观察下面代码,在方法 totalApples 中,参数只有AppleTree,所以totalApples只需要和AppleTree打交道就能算出所有苹果总数,而不需要和Monkey和Pig打交道。因为Monkey和Pig都是由AppleTree派生出的,也是AppleTree的另一种形态。

package test10;

public class Main
{
	public static void main(String[] args)
	{
		//由苹果树派生出的类
		AppleTree []at = new AppleTree[] {
				new AppleTree(10),
				new Monkey(5),
				new Pig(2)
		};
		System.out.println(totalApples(at));//一共有17个苹果
	}
	
	public static int totalApples(AppleTree... at)
	{
		int total = 0;
		for(AppleTree a : at)
		{
			total += a.getApple();
		}
		return total;
	}
}
//1、先定义一个苹果树
class AppleTree
{
	protected int apples;
	
	public AppleTree(int apples)
	{
		this.apples = apples;
	}
	
	public int getApple()
	{
		return apples;
	}
}
//2、由苹果树派生出猴子有多少个苹果
class Monkey extends AppleTree
{
	
	public Monkey(int apples)
	{
		super(apples);
	}
	
	//Override
	public int getApple()
	{
		if(apples > 0)
			return apples;
		return 0;
	}
}
//3、由苹果树派生出猪有多少个苹果
class Pig extends AppleTree
{
	public Pig(int apples)
	{
		super(apples);
	}
	//Override
	public int getApple()
	{
		if(apples > 0)
			return apples;
		return 0;
	}
}

 

覆写Object方法

因为所有的class最终都继承自Object,而Object定义了几个重要的方法:

  • toString():把instance输出为String;
  • equals():判断两个instance是否逻辑相等;
  • hashCode():计算一个instance的哈希值。
  • 在必要的情况下,我们可以覆写Object的这几个方法。例如:

//复写Object中的toString方法
	public String toString()
	{
		return "这棵树上有"+apples+"个苹果。";
	}
	//复写Object中的equals方法
	public boolean equals(Object o)
	{
		if(o instanceof AppleTree)
		{
			AppleTree p = (AppleTree) o;
			return true;
		}
		return false;
	}
	//复写Object中的hashCode方法
	public int hashCode()
	{
		return this.hashCode();
	}

 *在子类的覆写方法中,如果要调用父类的被覆写的方法,可以通过super来调用。

class Person {
    protected String name;
    public String hello() {
        return "Hello, " + name;
    }
}

class Student extends Person {
    @Override
    public String hello() {
        // 调用父类的hello()方法:
        return super.hello() + "!";
    }
}

final

继承可以允许子类覆写父类的方法。如果一个父类不允许子类对它的某个方法进行覆写,可以把该方法标记为final。用final修饰的方法不能被Override:

class Person {
    protected String name;
    public final String hello() {
        return "Hello, " + name;
    }
}

class Student extends Person {
    // compile error: 不允许覆写
    @Override
    public String hello() {
    }
}

*如果一个类不希望任何其他类继承自它,那么可以把这个类本身标记为final。用final修饰的类不能被继承

*对于一个类的实例字段,同样可以用final修饰。用final修饰的字段在初始化后不能被修改。

*

可以在构造方法中初始化final字段:

class Person {
    public final String name;
    public Person(String name) {
        this.name = name;
    }
}

这种方法更为常用,因为可以保证实例一旦创建,其final字段就不可修改。

 (7)抽象类

如果父类的方法本身不需要实现任何功能,仅仅是为了定义方法签名,目的是让子类去覆写它,那么,可以把父类的方法声明为抽象方法。

把一个方法声明为abstract,表示它是一个抽象方法,本身没有实现任何方法语句。因为这个抽象方法本身是无法执行的,所以,Person类也无法被实例化。编译器会告诉我们,无法编译Person类,因为它包含抽象方法。

必须把Person类本身也声明为abstract,才能正确编译它:

abstract class Person {
    public abstract void Hello();
}

抽象类

如果一个class定义了方法,但没有具体执行代码,这个方法就是抽象方法,抽象方法用abstract修饰。

因为无法执行抽象方法,因此这个类也必须申明为抽象类(abstract class)。

使用abstract修饰的类就是抽象类。我们无法实例化一个抽象类:

Person p = new Person(); // 编译错误

 

无法实例化的抽象类有什么用?

因为抽象类本身被设计成只能用于被继承,因此,抽象类可以强迫子类实现其定义的抽象方法,否则编译会报错。因此,抽象方法实际上相当于定义了“规范”。

例如,Person类定义了抽象方法Hello(),那么,在实现子类Student的时候,就必须覆写Hello()方法:

abstract class Person {
    public abstract void Hello();
}

class Student extends Person {
    @Override
    public void Hello()
    {
    	
    }
}

面向抽象编程

当我们定义了抽象类Person,以及具体的Student、Teacher子类的时候,我们可以通过抽象类Person类型去引用具体的子类的实例:

Person s = new Student();
Person t = new Teacher();

这种引用抽象类的好处在于,我们对其进行方法调用,并不关心Person类型变量的具体子类型:

// 不关心Person变量的具体子类型:
s.run();
t.run();

同样的代码,如果引用的是一个新的子类,我们仍然不关心具体类型:

// 同样不关心新的子类是如何实现run()方法的:
Person e = new Employee();
e.run();

这种尽量引用高层类型,避免引用实际子类型的方式,称之为面向抽象编程。

面向抽象编程的本质就是:

  • 上层代码只定义规范(例如:abstract class Person);

  • 不需要子类就可以实现业务逻辑(正常编译);

  • 具体的业务逻辑由不同的子类实现,调用者并不关心。

  • 通过abstract定义的方法是抽象方法,它只有定义,没有实现。抽象方法定义了子类必须实现的接口规范;

  • 定义了抽象方法的class必须被定义为抽象类,从抽象类继承的子类必须实现抽象方法;

  • 如果不实现抽象方法,则该子类仍是一个抽象类;

  • 面向抽象编程使得调用者只关心抽象方法的定义,不关心子类的具体实现。

 

(8)接口 

Java的接口(interface)定义了纯抽象规范,一个类可以实现多个接口;

接口也是数据类型,适用于向上转型和向下转型;

接口的所有方法都是抽象方法,接口不能定义实例字段;

接口可以定义default方法(JDK>=1.8)。

在抽象类中,抽象方法本质上是定义接口规范:即规定高层类的接口,从而保证所有子类都有相同的接口实现,这样,多态就能发挥出威力。

如果一个抽象类没有字段,所有方法全部都是抽象方法:

abstract class Person {
    public abstract void run();
    public abstract void jump();
    public abstract void sing();
}

就可以把该抽象类改写为接口:interface。

在Java中,使用interface可以声明一个接口:

interface  class Person {
   void run();
   void jump();
   void sing();
}

所谓interface,就是比抽象类还要抽象的纯抽象接口,因为它连字段都不能有。因为接口定义的所有方法默认都是public abstract的,所以这两个修饰符不需要写出来(写不写效果都一样)。

*当一个具体的class去实现一个interface时,需要使用implements关键字。

*在Java中,一个类只能继承自另一个类,不能从多个类继承。但是,一个类可以实现多个interface

接口继承

一个interface可以继承自另一个interface。interface继承自interface使用extends,它相当于扩展了接口的方法。例如:

interface Hello {
    void hello();
}

interface Person extends Hello {
    void run();
    String getName();
}

此时,Person接口继承自Hello接口,因此,Person接口现在实际上有3个抽象方法签名,其中一个来自继承的Hello接口。

default方法

在接口中,可以定义default方法。

实现类可以不必覆写default方法。default方法的目的是,当我们需要给接口新增一个方法时,会涉及到修改全部子类。如果新增的是default方法,那么子类就不必全部修改,只需要在需要覆写的地方去覆写新增方法。

default方法和抽象类的普通方法是有所不同的。因为interface没有字段,default方法无法访问字段,而抽象类的普通方法可以访问实例字段。

package test10;

public class Main
{
	public static void main(String[] args)
	{
		Person2 p2 = new Student("小明");
		
		p2.speak();
	}
}
//创建一个接口
interface Person
{
	void run();
	void jump();
	void sing();
}
//接口方法扩展
interface Person2 extends Person
{//显然,Person2 是Person接口的升级版,他比Person又新添了两个方法。
	void eat();
	void drink();
	String getName();
	default void speak()
	{
		System.out.println(getName()+"speak");
	}
}
class Student implements Person2
{
	private String name;
	public Student(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }
    @Override
	public void run() {};
	public void jump() {};
	public void sing() {};
	public void eat() {};
	public void drink() {};
}

 (9)静态字段和静态方法

  • 静态字段属于所有实例“共享”的字段,实际上是属于class的字段;

  • 调用静态方法不需要实例,无法访问this,但可以访问静态字段和其他静态方法;

  • 静态方法常用于工具类和辅助方法。

在一个class中定义的字段,我们称之为实例字段。实例字段的特点是,每个实例都有独立的字段,各个实例的同名字段互不影响。

还有一种字段,是用static修饰的字段,称为静态字段:static field。

实例字段在每个实例中都有自己的一个独立“空间”,但是静态字段只有一个共享“空间”,所有实例都会共享该字段。

 

package test10;

public class Main
{
	public static void main(String[] args)
	{
		Student stu = new Student("kk",11);
		Student2 stu2 = new Student2("cc",12);
		stu.score = 100;
		System.out.println(stu.score);//100
		System.out.println(stu2.score);//100:因为此处的static修饰的score是静态字段,在对象实例中共享
	}
}
class Person
{
	protected String name;
	protected int age;
	static int score;
}
class Student extends Person
{
	Student(String name, int age)
	{
		this.name = name;
		this.age = age;
	}
}
class Student2 extends Person
{
	Student2(String name, int age)
	{
		this.name = name;
		this.age = age;
	}
}

 

静态方法

有静态字段,就有静态方法。用static修饰的方法称为静态方法。

调用实例方法必须通过一个实例变量,而调用静态方法则不需要实例变量,通过类名就可以调用。静态方法类似其它编程语言的函数。

因为静态方法属于class而不属于实例,因此,静态方法内部,无法访问this变量,也无法访问实例字段,它只能访问静态字段。

通过实例变量也可以调用静态方法,但这只是编译器自动帮我们把实例改写成类名而已。

通常情况下,通过实例变量访问静态字段和静态方法,会得到一个编译警告。

静态方法经常用于工具类。例如:

  • Arrays.sort()

  • Math.random()

静态方法也经常用于辅助方法。注意到Java程序的入口main()也是静态方法。

 

 

package test10;

public class Main
{
	public static void main(String[] args)
	{
		Person p = new Person("KK",18);
		p.setScore(100);
		System.out.print(p);
		
	}
}
class Person
{
	private String name;
	private int age;
	private static int score;
	
	Person(String name, int age)
	{
		this.name = name;
	
		this.age = age;
	}
	
	public static void setScore(int socres)
	{
		score = socres;
	}
	
	public String toString()
	{
		return name+" "+age+" "+" "+score;
	}
}

(10)包

Java内建的package机制是为了避免class命名冲突;

JDK的核心类使用java.lang包,编译器会自动导入;

JDK的其它常用类定义在java.util.*,java.math.*,java.text.*,……;

包名推荐使用倒置的域名,例如org.apache。

(11)作用域

最佳实践

如果不确定是否需要public,就不声明为public,即尽可能少地暴露对外的字段和方法。

把方法定义为package权限有助于测试,因为测试类和被测试类只要位于同一个package,测试代码就可以访问被测试类的package权限方法。

一个.java文件只能包含一个public类,但可以包含多个非public类。如果有public类,文件名必须和public类的名字相同。

小结

Java内建的访问权限包括public、protected、private和package权限;

Java在方法内部定义的变量是局部变量,局部变量的作用域从变量声明开始,到一个块结束;

final修饰符不是访问权限,它可以修饰class、field和method;

一个.java文件只能包含一个public类,但可以包含多个非public类。

 

(12)内部类

Java的内部类可分为Inner Class、Anonymous Class和Static Nested Class三种:

  • Inner Class和Anonymous Class本质上是相同的,都必须依附于Outer Class的实例,即隐含地持有Outer.this实例,并拥有Outer Class的private访问权限;

  • Static Nested Class是独立类,但拥有Outer Class的private访问权限。

 Inner Class创建实例:

Outer.Inner inner = outer.new Inner();

 创建匿名类实例:

Runnable r = new Runnable() {
    // 实现必要的抽象方法...
};

 

(13)class与jar 

JVM通过环境变量classpath决定搜索class的路径和顺序;

不推荐设置系统环境变量classpath,始终建议通过-cp命令传入;

jar包相当于目录,可以包含很多.class文件,方便下载和使用;

MANIFEST.MF文件可以提供jar包的信息,如Main-Class,这样可以直接运行jar包。

(14)模块

 

Java 9引入的模块目的是为了管理依赖;

使用模块可以按需打包JRE;

使用模块对类的访问权限有了进一步限制。

笔记内容整理来自:

文章 - 廖雪峰的官方网站 (liaoxuefeng.com)https://www.liaoxuefeng.com/category/895882450960192 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/605847.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号