1.堆内存与栈内存
Java把内存分成两种,一种叫做栈内存,一种叫做堆内存。
在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配。当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量分配的内存空间,该内存空间可以立刻被另作他用。
堆内存用于存放由new创建的对象和数组。
在Java代码,常常会使用到这样的类的声明实例化:
Person per = new Person();
这其实是包含了两个步骤,声明和实例化:
Person per = null; //声明一个名为Person类的对象per
per = new Person(); // 实例化这个per对象
声明指的是创建类的对象的过程;
实例化指的是用关键词new来开辟内存空间。
它们在内存中的划分是这样的:
2.字符串比较
String类提供了equals( )方法,比较存储在两个字符串对象的内容是否一致。
public class Login {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String uname,pwd;
System.out.print("请输入用户名: ");
uname=input.next();
System.out.print("请输入密码: ");
pwd=input.next();
if( uname.equals("TOM") && pwd.equals("1234567") ){
System.out.print("登录成功! ");
}else{
System.out.print("用户名或密码不匹配,登录失败!");
}
}
}
equals()方法比较原理:一对一比较
equals():检查组成字符串内容的字符是否完全一致
==:判断两个字符串在内存中的地址,即判断是否是同一个字符串对象。
例如以下代码:
public class StringDemo {
public static void main(String args[]) {
String str1 = "Hello";
String str2 = new String("Hello");
String str3 = str2; // 引用传递
System.out.println(str1 == str2); // false
System.out.println(str1 == str3); // false
System.out.println(str2 == str3); // true
System.out.println(str1.equals(str2)); // true
System.out.println(str1.equals(str3)); // true
System.out.println(str2.equals(str3)); // true
}
}
上方第4行代码中,new了一个对象,用“”比较str1和str2,返回的结果却是false;而用用“equals”比较str1和str2,返回的结果是true。
为了分析上面的代码,必须首先分析堆内存空间和栈内存空间,这一点非常重要:
“”和equals()的区别?
==:比较的是两个字符串内存地址(堆内存)是否相等;
equals():比较的是两个字符串的内容是否相等。
3.常用方法
- 1.字符串连接
使用“+”连接字符串:
int sqlScore = 80; //sql成绩
int javaScore = 90; //java成绩
double htmlScore = 86.7; //html成绩
String scoreSheet = "SQL:" + sqlScore + " Java:" + javaScore + " HTML:" + htmlScore;
使用String类的concat()方法,语法:A.concat(B),B字符串将被连接到A字符串后面。
String s = new String("你好,");
String name = new String("张三!");
String sentence = s.concat(name);
System.out.println(sentence);
上述代码的输出结果为:你好,张三!
- 2.字符串查找
返回出现第一个匹配的位置,如果没有找到字符或字符串,则返回-1:
截取字符串:
substring(int beginindex, int endindex):截取beginindex和endindex之间的字符串部分,包括位置beginindex但不包括位置endindex。
示例:检查文件和邮箱格式:
import java.util.*;
public class Verify{
public static void main(String[] args) {
// 声明变量
boolean fileCorrect = false; //标识文件名是否正确
boolean emailCorrect = false; //标识E-mail是否正确
System.out.print("---欢迎进入作业提交系统---");
Scanner input = new Scanner(System.in);
System.out.println("请输入Java文件名: ");
String fileName = input.next();
System.out.print("请输入你的邮箱:");
String email = input.next();
//检查Java文件名
int index = fileName.lastIndexOf("."); //"."的位置
if(index!=-1 && index!=0 &&
fileName.substring(index+1,
fileName.length()).equals("java")){
fileCorrect = true; //标识文件名正确
}else{
System.out.println("文件名无效。");
}
//检查你的邮箱格式
if(email.indexOf('@')!=-1 &&
email.indexOf('.')>email.indexOf('@')){
emailCorrect = true; //标识E-mail正确
}else{
System.out.println("E-mail无效。");
}
//输出检测结果
if(fileCorrect && emailCorrect){
System.out.println("作业提交成功!");
}else{
System.out.println("作业提交失败!");
}
}
}
- 3.字符串分割
String类提供了split()方法,将一个字符串分割为子字符串,结果作为字符串数组返回。
例如,将一行歌词按空格进行拆分成多行输出:
public class Lyric {
/**
* 拆分歌词
*/
public static void main(String[] args) {
String words="长亭外 古道边 芳草碧连天 晚风扶 柳笛声残 夕阳山外山";
String[] printword=new String[100];//接收数组
System.out.println("***原歌词格式***\n"+words);
System.out.println("\n***拆分后歌词格式***");
printword=words.split(" ");//按照空格进行拆分
for(int i=0;i<printword.length;i++){
System.out.println(printword[i]);//打印输出
}
}
}
- 4.StringBuffer类
对字符串频繁修改(如字符串连接)时,使用StringBuffer类可以大大提高程序执行效率。
StringBuffer的声明:
//创建空StringBuffer对象:
StringBuffer sb = new StringBuffer();
//创建一个变量存储字符串aaa:
StringBuffer sb = new StringBuffer("aaa");
StringBuffer的使用:
sb.append("**"); //追加字符串
sb.insert (1, "**"); //插入字符串
sb.toString(); //转化为String类型
示例如下:
public class sbAppend {
public static void main(String[ ] args) {
StringBuffer sb = new StringBuffer("青春无悔");
int num=110;
StringBuffer sb1 = sb.append("我心永恒");
System.out.println(sb1);
StringBuffer sb2 = sb1.append('啊');
System.out.println(sb2);
StringBuffer sb3 = sb2.append(num);
System.out.println(sb3);
}
}
- 5.其它方法
charAt() : 返回字符串中某个特定位置的字符
indexOf() :返回字符串中某个特定字符或子字符串首次出现的索引
toUpperCase() : 将字符串内的所有字符从小写改为大写
toLowerCase() : 将字符串内的所有字符从大写改为小写
6. 编码实操
将一个数字字符串转换成逗号分隔的数字串,即从右边开始每三个数字用逗号分隔。
例如,输入12345678,输出12,345,678。
代码如下:
import java.util.*;
public class TestInsert {
/**
* 每隔三位插入逗号
*
*/
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
//接收数字串,存放于StringBuffer类型的对象中
System.out.print("请输入一串数字: ");
String nums = input.next();
StringBuffer str=new StringBuffer(nums);
//从后往前每隔三位添加逗号
for(int i=str.length()-3;i>0;i=i-3){
str.insert(i,',');
}
System.out.print(str);
}
}
7.抽象类
在面向对象的概念中,所有的对象都是通过类来表述,但并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一类具体的对象,这样的类就是抽象类。
抽象类往往用来表征对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象。
例子:
问你个问题,你知道什么是“东西”吗?什么是“物体”吗?
“麻烦你,小王。帮我把那个东西拿过来好吗”
在生活中,你肯定用过这个词--东西。
小王:“你要让我帮你拿那个水杯吗?”
你要的是水杯类的对象。而东西是水杯的父类。通常东西类没有实例对象,但我们有时需要东西的引用指向它的子类实例。
以下代码实例化没有意义:
Pet pet = new Pet ("贝贝",20,40);
pet.print();
可以使用抽象类限制实例化:
public abstract class Pet {
}
- 抽象类的使用
抽象类需要注意以下几点:
abstract放在class前,指明该类是抽象类
abstract放在方法声明中,则该方法是抽象方法,抽象方法没有方法体
一个抽象类可以含有多个抽象方法,也可以含有已实现的方法
抽象方法必须在子类中被实现,除非子类是抽象类
抽象类示例:
/**形状抽象类**/
public abstract class Shape {
double dim;
public Shape(double dim) {
this.dim = dim; } // 抽象方法,获得面积
public abstract double callArea(); // 抽象方法,获得周长
public abstract double callPerimeter();
}
定义Shape类的一个子类Circle:
/**圆形类**/
public class Circle extends Shape {
public Circle(double dim) {
super(dim);
} // 实现抽象方法callArea()
public double callArea() { // 返回圆的面积
return 3.14 * dim * dim; } // 实现抽象方法callPerimeter()
public double callPerimeter() { // 返回圆的周长 return 2 * 3.14 * dim; }
public static void main(String[] args) { // 声明一个Shape对象,指向实现它的子类对象
Shape shape = new Circle(10); // 调用callArea()求圆的面积,并输出 System.out.println("圆的面积是:" + shape.callArea() ); // 调用callPerimeter()求圆的周长,并输出
System.out.println("圆的周长是:" + shape.callPerimeter() );
}
}
抽象类虽然具备类的形式,但由于其“抽象”性,不能定义抽象类的实例,即不能为抽象类分配具体空间,例如以下代码是错误的:
Shape circle= new Shape(3); //抽象类不能实例化
可以通过如下方式来实例一个抽象类:
Shape someShape;
//引用Circle类的实例对象
someShape = new Circle(5);
someShape.callArea();
注意:
抽象类不能实例化,但可以指向一个实现它的子类对象;
abstract不能与final同时修饰一个类;
abstract不能和static、private、final或native并列
- final用法
final关键字表示的不可变的。一个类不希望被其它类继承,可以使用final,Java中的String类就是一个final类。
public final class Penguin extends Pet {
//…
}
方法不希望被重写,可以使用final,final修饰的方法表示此方法已经是“最后的、最终的”含义,即父类的final方法是不能被子类所覆盖的,也就是说子类是不能够存在和父类一模一样的方法的。
public final void print () {
//…
}
属性不希望被修改,可以使用final:
public class Penguin {
final String home ="南极";// 居住地
public void setHome(String name){
this.home=home; //错误,不可再赋值
}
}
8.多态
多态是指,同一个事件发生在不同的对象上会产生不同的结果。
比如,同样是打印操作,不同打印机打印的照片效果不一样:
再比如,宠物饿了,需要主人给宠物喂食,不同宠物吃的东西不一样:
狗狗吃狗粮;
企鹅吃鱼。
- 多态的实现
多态的实现步骤:
(1) 编写父类
(2) 编写子类,子类重写父类方法
(3) 运行时,使用父类的类型、子类的对象
Pet pet = new Dog();
实现多态的两种形式:
使用父类作为方法形参实现多态
使用父类作为方法返回值实现多态
- 1.使用父类作为方法形参实现多态
以下实现一个主人喂食宠物例子:
(1) 新建宠物类Pet,是一个抽象类
/**
* 宠物类,狗狗和企鹅的父类
*/
public abstract class Pet {
/**
* 抽象方法eat(),负责宠物吃饭功能。
*/
public abstract void eat();
}
(2) 新建狗狗类,继承Pet,并实现吃食方法eat()
/**
* 狗狗类,宠物的子类
*/
public class Dog extends Pet {
/**
* 实现吃食方法。
*/
public void eat() {
System.out.println("狗狗吃饭中!");
}
}
(3) 新建企鹅类,继承Pet,并实现吃食方法eat()
/**
* 企鹅类,宠物的子类
*/
public class Penguin extends Pet {
/**
* 实现吃食方法。
*/
public void eat() {
System.out.println("企鹅吃饭中!");
}
}
(4) 新建主人类,负责给宠物喂食,将抽象类Pet作为方法形参
/**
* 主人类
*/
public class Master {
/**
* 主人给宠物喂食
*/
public void feed(Pet pet) {
pet.eat();
}
}
(5) 新建测试类Test,执行主人的喂食方法
/**
* 测试类,给宠物喂食
*/
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
Penguin pgn = new Penguin();
Master master=new Master();
master.feed(dog);//主人给狗狗喂食
master.feed(pgn);//主人给企鹅喂食
}
}
运行测试类Test,输出结果为:
狗狗吃饭中!
企鹅吃饭中!
- 2.使用父类作为方法返回值实现多态
以下继续实现一个主人领养宠物例子:
(1) 在主人类中添加领养宠物的方法
/**
* 主人领养宠物
* @param typeId 宠物编号
* @return
*/
public Pet getPet(int typeId){
Pet pet=null;
if(typeId==1){
pet= new Dog();
}else if(typeId==2){
pet = new Penguin();
}
return pet;
}
(2) 新建测试类Test2,根据控制台选择,领养宠物
import java.util.Scanner;
public class Test2 {
public static void main(String[] args) {
System.out.println("欢迎您来到宠物店!");
System.out.print("请选择要领养的宠物类型:(1、狗狗 2、企鹅)");
Scanner input = new Scanner(System.in);
//获取选择的宠物类型
int typeId=input.nextInt();
//领养宠物
Master master=new Master();
Pet pet=master.getPet(typeId);
if(pet!=null){
System.out.println("领养成功!");
//开始喂食
master.feed(pet);
}else{
System.out.println("对不起,没有此类型的宠物,领养失败");
}
}
}
运行测试类Test2: