好的,我们来逐一详细解答这些问题。
1. 什么样的方法应该用static修饰?不用static修饰的方法往往具有什么特性?
应该使用 static
修饰的方法:
- 工具方法(Utility Methods): 执行一个与任何特定对象实例无关的通用任务。例如:
Math.sqrt(double a)
- 计算平方根,不需要一个“Math对象”的状态。Collections.sort(List list)
- 对列表排序,操作对象是传入的参数,而非Collections
类实例本身的状态。
- 工厂方法(Factory Methods): 用于创建并返回类的实例。例如:
Calendar.getInstance()
- 根据默认时区创建一个日历对象。LocalDate.now()
- 获取当前日期的实例。
- 访问静态变量的方法: 如果方法的作用仅仅是获取或设置静态变量的值(尤其是需要一些控制逻辑时),通常会设为静态方法。例如:
getInstance()
方法在单例模式中,用于返回唯一的静态实例。
- 作为程序入口点:
main
方法必须是静态的,因为它在任何对象被创建之前就被JVM调用。
不用 static
修饰的方法(实例方法)的特性:
- 依赖于对象的状态: 它们必须在一个类的具体实例(对象) 上被调用,并且可以访问和修改该实例的实例变量(非静态属性)。
- 代表对象的行为: 这些方法定义了对象能做什么。例如,一个
Dog
对象的bark()
方法,一个Student
对象的getName()
方法。 - 使用
this
关键字: 在实例方法内部,隐含了一个指向当前调用对象的引用this
。
2. Student的getName应该用static修饰吗?
不应该。
getName()
方法的作用是获取某个特定学生对象的名字。每个 Student
对象都有自己的 name
属性(例如,张三、李四)。这个方法的行为依赖于调用它的那个具体对象的状态(即它的 name
变量)。
如果将其设为 static
,那就意味着这个方法不属于任何一个学生对象,而是属于 Student
这个类本身。调用它需要 Student.getName()
,但这无法知道是获取哪个学生的名字,逻辑上是错误的。
正确写法:
public class Student {private String name; // 实例变量,每个学生都不同// 实例方法public String getName() {return this.name; // “this”指向调用该方法的那个学生对象}
}// 使用
Student stu = new Student();
String name = stu.getName(); // 获取stu这个具体对象的名字
3. 购物车案例中,使用了什么方法将问题描述中的类、方法、属性找出来?方法与属性到底属于哪个类,要怎么判定呢?
方法:名词动词法(或领域建模/类图分析法)
-
找类(Class): 从问题描述中找出名词或名词性短语。这些通常是候选的类。
- 案例描述(简化): “用户(User)可以向购物车(ShoppingCart)中添加商品(Product/Item),可以查看购物车中的商品列表,可以修改商品数量(quantity),可以结算生成订单(Order)。”
- 找出的名词:
User
,ShoppingCart
,Product
/Item
,Order
。这些就是核心的类。
-
找属性(Attribute): 分析每个类“有什么”特征或数据。这些通常是名词。
Product
类:可能有id
(商品ID)、name
(名称)、price
(价格)、description
(描述)等。ShoppingCart
类:可能有一个items
的列表(List<CartItem>
),用来表示购物车中的多项商品。CartItem
本身可能也是一个类,包含product
和quantity
属性。User
类:可能有userId
,username
,password
等。
-
找方法(Method): 分析每个类“能做什么”操作。这些通常是动词。
ShoppingCart
类:addItem(Product p, int quantity)
(添加商品)、removeItem(Product p)
(移除商品)、updateQuantity(Product p, int quantity)
(更新数量)、getTotalPrice()
(计算总价)、checkout()
(结算)。Product
类:通常只有getter/setter方法,如getPrice()
。
判定方法与属性属于哪个类:
遵循 “职责驱动” 和 “信息专家” 原则:
- 属性属于谁? 信息(数据)存储在哪个对象中最合理、最直接?
price
显然是Product
的属性,而不是ShoppingCart
的。 - 方法属于谁? 一个操作所主要使用和修改的数据(属性)在哪个类中,这个方法就应该放在哪个类。
- 例如,
getTotalPrice()
(计算总价)需要遍历购物车中所有商品(items
) 并计算每个商品的价格 * 数量
。items
是ShoppingCart
的属性,所以这个方法理所应当属于ShoppingCart
类。 addItem(...)
方法明显是为了修改ShoppingCart
的items
列表,所以它属于ShoppingCart
。
- 例如,
4. 如何避免类名冲突与管理代码?
1. 避免类名冲突:使用包(Package)
Java中使用包(Package) 来提供唯一的命名空间(Namespace)。这类似于操作系统的文件夹系统。
- 方法: 使用公司域名的倒写作为包名的前缀,这是全球通用的约定。
- 举例: 如果你的公司域名是
www.myawesomecompany.com
,那么你的项目包名应该是com.myawesomecompany.[项目名].[模块名]
。- 你写了一个
User
类,它的全限定名(Fully Qualified Name)就是com.myawesomecompany.project.model.User
。 - 即使别人也写了一个
User
类,只要他的包名是com.othercompany.xxx.User
,这两个类就是完全不同的,不会冲突。
- 你写了一个
2. 管理代码:使用模块化和分层架构
将成千上万的类分门别类地放在不同的包(目录)中,实现模块化和分层。
-
常见的包分层结构(以MVC为例):
com.xxx.[projectname].controller
- 放置控制器类,处理Web请求。com.xxx.[projectname].service
- 放置服务接口和实现类,处理业务逻辑。com.xxx.[projectname].dao
或repository
- 放置数据访问接口和实现类,负责与数据库交互。com.xxx.[projectname].model
或entity
或domain
- 放置数据模型类(如User, Product)。com.xxx.[projectname].util
- 放置工具类。com.xxx.[projectname].config
- 放置配置类。
-
举例:
- 一个电商项目可能有:
UserController
,ProductService
,OrderDao
,ShoppingCart
等类。 - 它们会被分别放入:
com.myawesomecompany.eshop.controller.UserController
com.myawesomecompany.eshop.service.ProductService
com.myawesomecompany.eshop.dao.OrderDao
com.myawesomecompany.eshop.model.ShoppingCart
- 这样,代码结构清晰,易于查找和维护。
- 一个电商项目可能有:
5. 《阿里巴巴Java开发手册》编程规范(至少7条)
-
【OOP规约】 关于
equals
方法:Object
的equals
方法容易抛空指针异常,应使用常量或确定有值的对象来调用equals
。- 正例:
"test".equals(object);
- 反例:
object.equals("test");
- 正例:
-
【OOP规约】 所有POJO类(简单Java对象)的属性必须使用包装数据类型(如
Integer
,Long
),不要使用基本数据类型(如int
,long
)。原因是数据库查询结果可能为null
,基本数据类型无法表示null
,会自动赋默认值(如0),会导致问题。 -
【变量命名】 代码中的命名均不能以下划线或美元符号开始或结束。
- 反例:
_name
/__name
/$name
/name_
/name$
- 反例:
-
【类命名、方法命名】 类名使用
UpperCamelCase
(大驼峰)风格,方法名、参数名、成员变量、局部变量都使用lowerCamelCase
(小驼峰)风格。- 正例:
UserService
(类),getUserInfo
(方法)
- 正例:
-
【常量命名】 常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。
- 正例:
MAX_STOCK_COUNT
- 反例:
MAX_COUNT
- 正例:
-
【包命名】 包名统一使用小写,且只有一个英语名词,点分隔符之间不能有数字。
- 正例:
com.alibaba.open.util
- 正例:
-
【代码格式】 如果是大括号内为空,则简洁地写成
{}
即可,不需要换行;如果是非空代码块,则:- 左大括号
{
前不换行,后换行。 - 右大括号
}
前换行,后还有else
等代码则不换行;表示终止的右大括号后必须换行。
- 左大括号
-
(附加一条)【OOP规约】 不能使用过时的类或方法。接口过时必须加
@Deprecated
注解,并清晰地说明采用的新接口或者新服务是什么。