ActiveMQ 了解
Active MQ 是JMS的一个具体实现,所以首先要对JMS有所了解。
1. JMS:
定义:Java消息服务(Java Message Service),是Sun是提出来的为J2EE提供企业消息处理的一套规范,他提供了创建,发送,接受,读取消息的服务。能接受消息生产者的(Message Producer)发出的消息,并把消息转发给消息消费者(Message Consumer)。(简单的说:Jms就是一个 处理消息发送 和接受的东东!)
2. JMS消息组成
JMS中的消息传递系统中传递的东东就是消息(Message)。其结构图如下:
Message 分为三个组成部分:
第一部分:
头(header)是个标准字段集,客户机和供应商都用它来标识和路由消息。
第二部分:
属性(property)支持把可选头字段添加到消息。如果您的应用程序需要不使用标准头字段对消息编目和分类,您就可以添加一个属性到消息以实现这个编目和分类。提供 set<Type>Property(...) 和get<Type>Property(...) 方法以设置和获取各种 Java 类型的属性,包括 Object。JMS 定义了一个供应商选择提供的标准属性集。
第三部分:
消息的主体(body)包含要发送给接收应用程序的内容。每个消息接口特定于它所支持的内容类型。
JMS 为不同类型的内容提供了它们各自的消息类型,但是所有消息都派生自 Message 接口。
· StreamMessage:包含 Java 基本数值流,用标准流操作来顺序的填充和读取。
· MapMessage:包含一组名/值对;名称为 string 类型,而值为 Java 的基本类型。
· TextMessage:包含一个 String。
· ObjectMessage:包含一个 Serializable Java 对象;能使用 JDK 的集合类。
· BytesMessage:包含未解释字节流: 编码主体以匹配现存的消息格式。
· XMLMessage: 包含XML内容。扩展TextMessage,XMLMessage 类型的使用,使得消息过滤非常便利。
3. JMS的消息通信模型有两种:
JMS是用于和面向消息的中间件相互通信的应用程序接口。
(1)支持点对点(point-to-point)的域(简称PTP),
点对点消息传递域(PTP)的特点如下:
• 每个消息只能有一个消费者。
• 消息的生产者和消费者之间没有时间上的相关性。无论消费者在生产者发
送消息的时候是否处于运行状态,它都可以提取消息。
(2)支持发布/订阅(publish/subscribe)类型的域(简称PUB/SUB)),
并且提供对下列类型的支持:经认可的消息传递,事务型消息的传递,一致性消息和具有持久性的订阅者支持。JMS还提供了另一种方式来对您的应用与旧的后台系统相集成。
发布/订阅消息传递域(PUB/SUB)的特点如下:
• 每个消息可以有多个消费者。
• 生产者和消费者之间有时间上的相关性。订阅一个主题的消费者只能消费
自它订阅之后发布的消息。JMS 规范允许客户创建持久订阅,这在一定程
度上放松了时间上的相关性要求。持久订阅允许消费者消费它在未处于激
活状态时发送的消息。
注意:在点对点(PTP)消息传递域中,目的地被成为队列(queue);在发布/订阅(PUB/SUB)消息传递域中,目的地被成为主题(topic)。
queue和topic之间的详解图:
Topic |
Queue |
|
概要 |
Publish Subscribe messaging 发布订阅消息 |
Point-to-Point 点对点 |
有无状态 |
topic数据默认不落地,是无状态的。 |
Queue数据默认会在mq服务器上以文件形式保存,比如Active MQ一般保存在$AMQ_HOME/data/kr-store/data下面。也可以配置成DB存储。 |
完整性保障 |
并不保证publisher发布的每条数据,Subscriber都能接受到。 |
Queue保证每条数据都能被receiver接收。 |
消息是否会丢失 |
一般来说publisher发布消息到某一个topic时,只有正在监听该topic地址的sub能够接收到消息;如果没有sub在监听,该topic就丢失了。(必须监听才能接收到消息) |
Sender发送消息到目标Queue,receiver可以异步接收这个Queue上的消息。Queue上的消息如果暂时没有receiver来取,也不会丢失。 |
消息发布接收策略 |
一对多的消息发布接收策略,监听同一个topic地址的多个sub都能收到publisher发送的消息。Sub接收完通知mq服务器 |
一对一的消息发布接收策略,一个sender发送的消息,只能有一个receiver接收。receiver接收完后,通知mq服务器已接收,mq服务器对queue里的消息采取删除或其他操作。 |
4.消息发出去后的确认模式
应用程序创建的会话有一般有5 种确认模式(非事务)。
五种确认模式说明:
· AUTO_ACKNOWLEDGE:自动确认模式。一旦接收方应用程序的方法调用从处理消息处返回,会话对象就会确认消息的接收。
· CLIENT_ACKNOWLEDGE:客户端确认模式。会话对象依赖于应用程序对被接收的消息调用一个acknowledge()方法。一旦这个方法被调用,会话会确认最后一次确认之后所有接收到的消息。这种模式允许应用程序以一个调用来接收,处理并确认一批消息。注意:在管理控制台中,如果连接工厂的Acknowledge Policy(确认方针)属性被设置为"Previous"(提前),但是你希望为一个给定的会话确认所有接收到的消息,那么就用最后一条消息来调用acknowledge()方法。
· DUPS_OK_ACKNOWLEDGE:允许副本的确认模式。一旦接收方应用程序的方法调用从处理消息处返回,会话对象就会确认消息的接收;而且允许重复确认。在需要考虑资源使用时,这种模式非常有效。注意:如果你的应用程序无法处理重复的消息的话,你应该避免使用这种模式。如果发送消息的初始化尝试失败,那么重复的消息可以被重新发送。
· NO_ACKNOWLEDGE:不确认模式。不确认收到的消息是需要的。消息发送给一个NO_ACKNOWLEDGE 会话后,它们会被WebLogic 服务器立即删除。在这种模式下,将无法重新获得已接收的消息,而且可能导致下面的结果:1. 消息可能丢失;和(或者)另一种情况:2. 如果发送消息的初始化尝试失败,会出现重复消息被发送的情况。
· MULTICAST_NO_ACKNOWLEDGE:IP组播下的不确认模式,同样无需确认。发送给一个MULTICAST_NO_ACKNOWLEDGE会话的消息, 会共享之前所述的NO_ACKNOWLEDGE 确认模式一样的特征。这种模式支持希望通过IP 组播方式进行消息通信的应用程序,而且无需依赖会话确认提供的服务质量。注意:如果你的应用程序无法处理消息的丢失或者重复,那么你应该避免使用这种模式。如果发送消息的初始化尝试失败的话,重复的消息可能会被再次发送。
(注:在上表的5 种确认模式中,AUTO_ACKNOWLEDGE ,DUPS_OK_ACKNOWLEDGE 和
CLIENT_ACKNOWLEDGE 是JMS 规范定义的,NO_ACKNOWLEDGE 和MULTICAST_NO_ACKNOWLEDGE是WebLogic JMS 提供的。)
5. JMS 支持以下两种消息提交模式:
. PERSISTENT。指示 JMS provider 持久保存消息,以保证消息不会因为 JMS
provider 的失败而丢失。(当服务器重启之后,之前的发送PERSISTENT消息,会获取到!)
.NON_PERSISTENT。不要求 JMS provider 持久保存消息。(当服务器重启之后,之前的发送NON_PERSISTENT消息,会获取不到!)
6.一种典型的JMS 程序需要经过下列几个步骤:
· 通过 JNDI 查找 ConnectionFactory。
· 用 ConnectionFactory 创建一个 Connection。
· 用 Connection 创建一个或多个 Session。
· 用 Session 和 Destination 创建所需的 MessageProducer 和 MessageConsumer。
· 启动 Connection。 (下面是对应的截图说明)
JMS 各个步骤的详细介绍
先简单介绍JMS的服务器端
(0)Broker :可以把JMS Brokers看做服务器端,这个服务器可以独立运行,也可以随着其他容器,并且它是以内嵌的方式运行,如下配置
BrokerService broker = new BrokerService();
broker.addConnector("tcp://localhost:61616");
broker.start();
此外,也可以通过BrokerFactory来创建broker,例如:
BrokerService broker = BrokerFactory.createBroker(new URI(someU
RI));
通过ActiveMQConnectionFactory还可以隐含创建内嵌的broker(后面的实例代码会用到)
下面正正式介绍每一个步骤
(1)连接工厂 (ConnectionFactory)
连接工厂是客户用来创建连接的对象.
例如: ActiveMQ 提供的ActiveMQConnectionFactory。注意(要初始化 JMS,则需要使用连接工厂。客户端通过创建 ConnectionFactory建立到 ActveMQ的连接,一个连接工厂封装了一组连接配置参数,这组参数在配置ActiveMQ时已经定义,例如brokerURL参数,此参数传入的是ActiveMQ服务地址和端口,支持openwire协议的默认连接为 tcp://localhost:61616,支持 stomp协议的默认连接为tcp://localhost:61613。
ActiveMQConnectionFactory构造方法:
ActiveMQConnectionFactory();
ActiveMQConnectionFactory(String brokerURL);
ActiveMQConnectionFactory(String userName, String password, String b rokerURL) ; ActiveMQConnectionFactory(String userName, String password, URI brok erURL) ; ActiveMQConnectionFactory(URI brokerURL); 其中 brokerURL为ActiveMQ服务地址和端口。
例如:
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://192. 168.0.135:61616");
或者
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
connectionFactory. setBrokerURL("tcp://192.168.0.135:61616"); )
(2)连接 (Connection)
Connection 封装了客户与 JMS 提供者之间的一个虚拟的连接。
注意
(当一个Connection被创建时,它的传输默认是关闭的,必须使用start方法开启。一个Connection可以建立一个或多个的Session。当一个程序执行完成后,必须关闭之前创建的Connection,否则 ActiveMQ不能释放资源,关闭一个Connection同样也关闭了 Session,MessageProducer和MessageConsumer。)
(3)会话(Seesion)
Session 是生产和消费消息的一个单线程上下文。
会话用于创建消息生产者(producer)、消息消费者(consumer)和消息(message)等。会话提供了一个事务性的上下文,在这个上下文中,一组发送和接收被组合到了一个原子操作中。
(4)目的地
目的地是客户用来指定它生产的消息的目标和它消费的消息的来源的对象。
JMS1.0.2 规范中定义了两种消息传递域:点对点(PTP)消息传递域和发布/订阅
消息传递域。(关于PTP和PUB/SUB 在“JMS的消息通信模型有两种“ 有详细介绍!)
消息生产者(producer):用于把消息发送到一个目的地。
消息消费者(consumer):它用于接收发送到目的地的消息。
消息的消费可以采用以下两种方法之一:
• 同步消费。通过调用消费者的 receive 方法从目的地中显式提取消息。
receive 方法可以一直阻塞到消息到达。
• 异步消费。客户可以为消费者注册一个消息监听器(Listener),以定义在消息到达时所采取的动作。
7.现在进行简单的实例的配置和测试
Active MQ配置和启动:
比如我下的是最新的包apache-activemq-5.4.2-bin.zip
1、安装文件:apache-activemq-5.4.2-bin.zip
2、安装过程:解压缩到apache-activemq-5.4.2-bin.zip到一个目录,比如C:/Program Files/apache-activemq-5.4.2
3、ActiveMQ配置文件在C:/Program Files/apache-activemq-5.4.2/conf/activemq.xml,采用默认的配置就可以运行了,多台做集群时可以修改该文件。
4、启动ActiveMQ:运行C:/Program Files/apache-activemq-5.4.2/bin/activemq.bat
5、测试
ActiveMQ默认使用的TCP连接端口是61616, 通过查看该端口的信息可以测试ActiveMQ是否进入DOS:netstat -an|find "61616"
TCP 0.0.0.0:61616 0.0.0.0:0 LISTENING
6、监控
ActiveMQ5.0版本默认启动时,启动了内置的jetty服务器,提供一个demo应用和用于监控ActiveMQ的admin应用。
demo:http://127.0.0.1:8161/demo/
下面是一个简单的例子(一个简单的实例)
我先建一个简单的Java Project 项目(项目名称activemq),
并且将我自己的安装目录C:/Program Files/apache-activemq-5.4.2下的activemq-all-5.4.2.jar的包加入到Reference Libraries中,这个包包含了所有jms接口api的实现。
消息的生产者ProducerTool类
import javax.jms.Connection;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
public class ProducerTool {
private String user = ActiveMQConnection.DEFAULT_USER; //默认 用户
private String password = ActiveMQConnection.DEFAULT_PASSWORD; //默认 密码
private String url = ActiveMQConnection.DEFAULT_BROKER_URL; //默认的是localhost:8080
private String subject = "TOOL.DEFAULT"; //消息目的地名称
private Destination destination = null; //在点对点(PTP)消息传递域中,目的地被成为队列(queue)
private Topic topic=null;//在发布/订阅(PUB/SUB)消息传递域中,目的地被成为主题(topic)。
private Connection connection = null; //初始化 一个JMS客户端到JMS Provider的连接
private Session session = null; //初始化 一个发送消息的进程
private MessageProducer producer = null; //初始化 消息生产者 (它是由session 创建的)
// 初始化
private void initialize() throws JMSException, Exception {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
user, password, url);
connection = connectionFactory.createConnection();
//false 参数表示 为非事务型消息,后面的参数表示消息的确认类型(见4.消息发出去后的确认模式)
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//PTP消息方式 目的地被成为队列(queue)
destination = session.createQueue(subject);
producer = session.createProducer(destination);
//在发布/订阅(PUB/SUB)消息,目的地被成为主题(topic)。
/*topic=session.createTopic(subject);
producer=session.createProducer(topic);
*/
//消息模型为非持久型
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
}
// 发送消息
public void produceMessage(String message) throws JMSException, Exception {
initialize();
TextMessage msg = session.createTextMessage(message);
connection.start();
System.out.println("Producer:->Sending message: " + message);
producer.send(msg);
System.out.println("Producer:->Message sent complete!");
}
// 关闭连接
public void close() throws JMSException {
System.out.println("Producer:->Closing connection");
if (producer != null)
producer.close();
if (session != null)
session.close();
if (connection != null)
connection.close();
}
}
消息的消费者ConsumerTool类
import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.MessageListener;
import javax.jms.Message;
import javax.jms.TextMessage;
import javax.jms.Topic;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
public class ConsumerTool implements MessageListener {
private String user = ActiveMQConnection.DEFAULT_USER; //默认 用户
private String password = ActiveMQConnection.DEFAULT_PASSWORD; //默认 密码
private String url = ActiveMQConnection.DEFAULT_BROKER_URL; //默认的是localhost:8080
private String subject = "TOOL.DEFAULT"; //消息目的地名称
private Destination destination = null; //在点对点(PTP)消息传递域中,目的地被成为队列(queue)
private Topic topic=null;//在发布/订阅(PUB/SUB)消息传递域中,目的地被成为主题(topic)。
private Connection connection = null; //初始化 一个JMS客户端到JMS Provider的连接
private Session session = null; //初始化 一个接受消息的进程
private MessageConsumer consumer = null; //初始化 消息消费者
// 初始化
private void initialize() throws JMSException, Exception {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
user, password, url);
connection = connectionFactory.createConnection();
//false 参数表示 为非事务型消息,后面的参数表示消息的确认类型(见4.消息发出去后的确认模式)
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//PTP消息方式 目的地被成为队列(queue)
destination = session.createQueue(subject);
consumer = session.createConsumer(destination);
//在发布/订阅(PUB/SUB)消息,目的地被成为主题(topic)。
/*topic=session.createTopic(subject);
consumer = session.createConsumer(topic);
*/
}
// 消费消息
public void consumeMessage() throws JMSException, Exception {
initialize();
connection.start();
System.out.println("Consumer:->Begin listening...");
//接受消息方式1:消息的异步接收 监听 实现MessageListener接口,每当消息到达时,ActiveMQ会调用MessageListener中的onMessage函数。
consumer.setMessageListener(this);
//接受消息方式2:消息的同步 主动接受消息 用recieve方式
/* TextMessage textMessage = (TextMessage)consumer.receive();
if(null!=textMessage)
System.out.println(textMessage.getText());
else
System.out.println("无"); */
}
// 关闭连接
public void close() throws JMSException {
System.out.println("Consumer:->Closing connection");
if (consumer != null)
consumer.close();
if (session != null)
session.close();
if (connection != null)
connection.close();
}
// 消息处理函数
public void onMessage(Message message) {
try {
if (message instanceof TextMessage) {
TextMessage txtMsg = (TextMessage) message;
String msg = txtMsg.getText();
System.out.println("Consumer:->Received: " + msg);
} else {
System.out.println("Consumer:->Received: " + message);
}
} catch (JMSException e) {
e.printStackTrace();
}
}
}
test测试类
import javax.jms.JMSException;
public class test {
/**
* @param args
*/
public static void main(String[] args) throws JMSException, Exception {
ConsumerTool consumer = new ConsumerTool();
ProducerTool producer = new ProducerTool();
//接受消息方式一:通过监听Listener 开始监听
consumer.consumeMessage();
// 延时500毫秒之后发送消息
Thread.sleep(500);
producer.produceMessage("Hello, world!");
//接受消息方式2:主动方式, 必须在producer产生消息后,去获取,否则获取不到
//consumer.consumeMessage();
producer.close();
// 延时500毫秒之后停止接受消息
Thread.sleep(500);
consumer.close();
}
}
/*消息的同步与异步接收
消息的同步接收是指客户端主动去接收消息,客户端可以采用MessageConsumer的receive方法去接收下一个消息。
消息的异步接收是指当消息到达时,ActiveMQ主动通知客户端。客户端可以通过注册一个实现MessageListener接口的对象到MessageConsumer。
MessageListener只有一个必须实现的方法onMessage,它只接收一个参数,即Message。
在为每个发送到Destination的消息实现onMessage时,将调用该方法。
Message receive() Message receive(long timeout) Message receiveNoWait()
其中timeout为等待时间,单位为毫秒。*/
先启动ActiveMQ;
运行test类的运行结果
在学习ActiveMQ的过程中要注意的地方:
1.注意activeMQ 内含了jefft 服务器,所以要在运行activeMQ实例之前要,启动activemq.bat.
2.主要引用的的包activemq-all-5.4.2.jar 就可以了,他包jms所需要的接口。
3.对于消息topic模式,接受消息的那一方,要使用监听方式,获取消息,否则获取不到。
未解决:
tomcat 与activemq整合失败,正在尝试.
(activemq官方也有个HelloWorld例子,http://activemq.apache.org/hello-world.html )
ActiveMQ 了解的更多相关文章
- Java消息队列--ActiveMq 实战
1.下载安装ActiveMQ ActiveMQ官网下载地址:http://activemq.apache.org/download.html ActiveMQ 提供了Windows 和Linux.Un ...
- 消息队列性能对比——ActiveMQ、RabbitMQ与ZeroMQ(译文)
Dissecting Message Queues 概述: 我花了一些时间解剖各种库执行分布式消息.在这个分析中,我看了几个不同的方面,包括API特性,易于部署和维护,以及性能质量..消息队列已经被分 ...
- (jms)ActiveMQ 安装配置.
前言 ActiveMQ他是Apache出品的一个JMS提供者,管理会话和队列,运行在JVM下,支持多种语言,如JAVA,C++,C#,应用协议: OpenWire,Stomp REST,WS Noti ...
- node(ActiveMq)
简单尝试了node下的ActiveMQ 1.下载apache-activemq-5.9.0,执行bat文件: 2.登录http://localhost:8161/admin可查看其管理后台: 3.安装 ...
- ActiveMQ的集群方案对比及部署
转载:http://blog.csdn.net/lifetragedy/article/details/51869032 ActiveMQ的集群 内嵌代理所引发的问题: 消息过载 管理混乱 如何解决这 ...
- JMS学习之路(一):整合activeMQ到SpringMVC
JMS的全称是Java Message Service,即Java消息服务.它主要用于在生产者和消费者之间进行消息传递,生产者负责产生消息,而消费者负责接收消息.把它应用到实际的业务需求中的话我们可以 ...
- ActiveMQ消息队列的使用及应用
这里就不说怎么安装了,直接解压出来就行了. 谢绝转载,作者保留所有权力 目录: 一:JMQ的两种消息模式 1.1:点对点的消息模式 1.2:订阅模式 二:点对点的实现代码 2.1:点对点的发送端 2 ...
- 从零开始学 Java - Spring 集成 ActiveMQ 配置(一)
你家小区下面有没有快递柜 近两年来,我们收取快递的方式好像变了,变得我们其实并不需要见到快递小哥也能拿到自己的快递了.对,我说的就是类似快递柜.菜鸟驿站这类的代收点的出现,把我们原来快递小哥必须拿着快 ...
- Spring下ActiveMQ实战
MessageQueue是分布式的系统里经常要用到的组件,一般来说,当需要把消息跨网段.跨集群的分发出去,就可以用这个.一些典型的示例就是: 1.集群A中的消息需要发送给多个机器共享: 2.集群A中消 ...
- ActiveMQ(li)
一.ActiveMQ 首先,ActiveMQ不是一个框架,它不是struct,webx,netty这种框架,它更像是tomcat服务器,因为你使用它之前必须启动它,activeMQ和JMS的关系有点类 ...
随机推荐
- 成功者的特点 VS 失败者的特点
- C端端口扫描工具,发现www服务
作者d_m 简述 起初是90sec一个帖子中的代码(见参考[1]),看了之后感觉很不错.当然工作内容nmap也可以完成.不过在实际比较后决定还是用python脚本完成. 优化 所以也算是很久以前的一个 ...
- sql 从另外一张表查询数据存入本表. (有关联的)
UPDATE friends INNER JOIN users ON friends.friendid=users.userid SET friends.friendname=users.userna ...
- Codeforces Round #243 (Div. 2)——Sereja and Table
看这个问题之前,能够先看看这个论文<一类算法复合的方法>,说白了就是分类讨论,可是这个思想非常重要 题目链接 题意: 首先给出联通块的定义:对于相邻(上下和左右)的同样的数字视为一个联通块 ...
- 【Excle数据透视】如何用含有单元格的数据来创建数据透视
取消合并单元格,填充相同内容项,然后创建数据透视表. 如下图:需要使用数据创建数据透视表 步骤一 开始→格式刷,然后对单元格区域G2:G15使用格式刷功能,保留合并单元格格式 步骤二 选中A2:A18 ...
- 【Python】向函数传递列表
向函数传递列表 在实际使用中你会发现,向函数传递列表是比较实用的,这种列表可能包含名字.数字.可能更复杂的对象(字典) 假设向一个函数传递一堆水果,我们说出我们喜欢所有的水果 def Obj(frui ...
- uva 1493 - Draw a Mess(并查集)
题目链接:uva 1493 - Draw a Mess 题目大意:给定一个矩形范围,有四种上色方式,后面上色回将前面的颜色覆盖,最后问9种颜色各占多少的区域. 解题思路:用并查集维护每一个位置相应下一 ...
- IOS中公布应用程序,进度条一直不走怎么处理
在IOS中公布应用程序非常是喜闻乐见. 近期1周.我更新了6次版本号.可是时不时的会卡住,进度条不走. 最后总结了几个原因. 1.在公布前你要确认自己的证书是否配置正确 2.DNS域名server有没 ...
- Java系统中如何拆分同步和异步
很多开发人员说,将应用程序切换到异步处理很复杂.因为他们有一个天然需要同步通信的Web应用程序.在这篇文章中,我想介绍一种方法来达到异步通信的目的:使用一些众所周知的库和工具来设计他们的系统. 下面的 ...
- java 方法重写原则
方法重写应遵循“三同一小一大”原则: “三同”:即方法名相同,形参列表相同,返回值类型相同: “一小”:子类方法声明抛出的异常比父类方法声明抛出的异常更小或者相等: “一大”:子类方法的访问修饰符应比 ...